AWKで正規表現の貪欲さを減らす方法は?


14

で貪欲でないパターン(正規表現)マッチ​​ングを行いたいawk。以下に例を示します。

echo "@article{gjn, Author =   {Grzegorz J. Nalepa}, " | awk '{ sub(/@.*,/,""); print }'

短い文字列を選択する正規表現を書くことは可能ですか?

@article{gjn,

この長い文字列の代わりに?:

@article{gjn, Author =   {Grzegorz J. Nalepa},

この結果を取得したい:

 Author =   {Grzegorz J. Nalepa},



私は別の例を持っています:

echo "  article {gjn、Author = {Grzegorz J. Nalepa}、" | awk '{sub(/  [^、] *、/、 ""); 印刷} '
      ↑↑^^^^^

入力文字列と正規表現の両方の最初の位置で、@文字をコンマ(,)文字に変更.*しました(また、に変更しました[^,]*)。短い文字列を選択する正規表現を書くことは可能ですか?

, Author =   {Grzegorz J. Nalepa},

長い文字列の代わりに?:

,article{gjn, Author =   {Grzegorz J. Nalepa},

この結果を取得したい:

,article{gjn

4
正規表現が堅牢なHTML解析には不十分であるように、おそらくこの種の文脈依存の文法解析を行うことはできません。ただし、入力のセットがかなり制限され、整形式である場合、制限が何であるかを宣言している限り、正規表現を回避できる場合があります。たとえばAuthor、コンマと空白の後に、空白が続き、空白が=続き、空白が続き、{その後に任意の非}続きが続くことを探すことができますが}、これは(とりわけ)あなたが部品{}内にネストできないことを必要とします= { ... }
jw013

@ jw013、ご説明ありがとうございます。それでも、他のユーザーの提案をお待ちしています。
nowy1

回答:


18

その後@、最初までを選択する場合は、次の,ように指定する必要があります@[^,]*,

であることを@、任意の数(続く*非コンマの)( [^,])(コンマが続きます,)。

このアプローチはの同等物として機能しますが、の@.*?,ようなものには機能しません@.*?string文字を否定するのは簡単ですが、正規表現の文字列を否定することははるかに困難です。

別のアプローチは、入力を前処理してstring、それ以外の場合は入力に含まれない文字で置換または追加することです。

gsub(/string/, "\1&") # pre-process
gsub(/@[^\1]*\1string/, "")
gsub(/\1/, "") # revert the pre-processing

入力に置換文字が含まれないことを保証できない場合(\1上記)、1つのアプローチはエスケープメカニズムを使用することです。

gsub(/\1/, "\1\3") # use \1 as the escape character and escape itself as \1\3
                   # in case it's present in the input
gsub(/\2/, "\1\4") # use \2 as our maker character and escape it
                   # as \1\4 in case it's present in the input
gsub(/string/, "\2&") # mark the "string" occurrences

gsub(/@[^\2]*\2string/, "")

# then roll back the marking and escaping
gsub(/\2/, "")
gsub(/\1\4/, "\2")
gsub(/\1\3/, "\1")

これはfixedで機能しますstringが、の同等のような任意の正規表現では機能しません@.*?foo.bar


良い反応をありがとうございました。私の編集では、さらに別の例を尋ねました(私の編集を参照)。
nowy1

6

awk貪欲でないマッチを行うことができないための回避策を提供するいくつかの良い答えが既にあるので、Perl Compatible Regular Expressions(PCRE)を使用してそれを行う別の方法に関するいくつかの情報を提供しています。コマンドラインオプションを使用すると、最も単純な「マッチアンドプリント」awkスクリプトを簡単に再実装でき、より複雑なスクリプトはa2p AwkからPerlへのトランスレーターで変換できることに注意してください。perl-n

Perlには、PerlスクリプトやPCREを使用するあらゆるもので使用できる貪欲でない演算子があります。たとえば、GNU grepの-Pオプションにも実装されています。

PCREはPerlの正規表現と同一ではありませんが、非常に近いものです。非常に高速であり、拡張された正規表現に対するPerlの機能強化が非常に役立つため、多くのプログラムで正規表現ライブラリの一般的な選択肢です。

perlre(1)のmanページ:

   By default, a quantified subpattern is "greedy", that is, it will match
   as many times as possible (given a particular starting location) while
   still allowing the rest of the pattern to match.  If you want it to
   match the minimum number of times possible, follow the quantifier with
   a "?".  Note that the meanings don't change, just the "greediness":

       *?        Match 0 or more times, not greedily
       +?        Match 1 or more times, not greedily
       ??        Match 0 or 1 time, not greedily
       {n}?      Match exactly n times, not greedily (redundant)
       {n,}?     Match at least n times, not greedily
       {n,m}?    Match at least n but not more than m times, not greedily

3

これは古い投稿ですが、次の情報は他の人にとって役に立つかもしれません。

awkで貪欲でないREマッチングを実行する方法は、明らかに粗雑ですが、あります。基本的な考え方は、match(string、RE)関数を使用し、次のような(テストされていない)一致が失敗するまで文字列のサイズを徐々に小さくすることです。

if (match(string, RE)) {
    rstart = RSTART
    for (i=RLENGTH; i>=1; i--)
        if (!(match(substr(string,1,rstart+i-1), RE))) break;
    # At this point, the non-greedy match will start at rstart
    #  for a length of i+1
}

2

一般的な表現の場合、これは貪欲でない一致として使用できます。

function smatch(s, r) {
    if (match(s, r)) {
        m = RSTART
        do {
            n = RLENGTH
        } while (match(substr(s, m, n - 1), r))
        RSTART = m
        RLENGTH = n
        return RSTART
    } else return 0
}

@JimMellanderの回答に基づいてこれを使用しています。smatch次のように動作しmatchます:

s 正規表現rが発生する位置 、または発生しない場合は0。変数RSTARTRLENGTHは、一致した文字列の位置と長さに設定されます。


1

awkには欲張りでないマッチングを行う方法はありません。ただし、目的の出力を取得できる場合があります。schの提案は、その行で機能します。コンマに依存できないが、「作成者」が常にあなたが望むものの始まりである場合、これを行うことができます:

awk '{ sub(/@.*Author/,"Author"); print }'

Authorの前の文字数が常に同じである場合、これを行うことができます。

awk '{ sub(/@.{21}/,""); print }'

セット全体でデータがどのように見えるかを知る必要があります。


0

方法が常にある。指定された問題は、コンマをセパレーターとして使用することでかなり簡単に解決できます。

echo "@article{gjn2010jucs, Author =   {Grzegorz J. Nalepa}, " |
awk -F, '{sub(/^[ \t]/, "", $2); print $2}'

フィールドの数が異なる場合、通常は少し優れたものが必要です。このような場合、ストップワードを使用すると行から何でも切り取ることができるため、ストップワードを見つけることで成果が得られることがよくあります。この例のコンテキスト内では、ストップワードの意味を次に示します。

echo "@article{gjn2010jucs, Author =   {Grzegorz J. Nalepa}, " |
awk  '{sub(/.*Author/, "Author", $0); sub(/},.*/, "}", $0); print $0}'

0

これは古い投稿であることを知っています。ただし、要求されたとおりにawkをOPとして使用するだけです
。A= @ article {gjn2010jucs、Author = {Grzegorz J. Nalepa}、
echo $ A | awk 'sub(/ @ [^、] * /、 "))'

出力:
、著者= {Grzegorz J. Nalepa}、


1
その答えは、約5つの理由で間違っています。
スコット

3
何が間違っているのか理解してもらえますか?出力は要求されたものと一致しているようです。答えが正しい/正しくない理由を理解しようとしています。
VINAY NAIR
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.