web-dev-qa-db-ja.com

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

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

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

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

@article{gjn,

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

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

私はこの結果を得たいです:

 Author =   {Grzegorz J. Nalepa},



エコー "記事{gjn、Author = {Grzegorz J. Nalepa}、 "| awk '{sub(/[^、] *、/、 "");印刷} '
↑↑^^^^^

@文字からカンマ(,)入力文字列と正規表現の両方の最初の位置にある文字(および変更された.*から[^,]*)。より短い文字列を選択する正規表現を書くことは可能ですか?

, Author =   {Grzegorz J. Nalepa},

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

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

私はこの結果を得たいです:

,article{gjn
14
nowy1

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

つまり、@の後に任意の数(*)の非コンマ([^,])が続き、その後にコンマ(,)が続きます。

このアプローチは@.*?,と同等に機能しますが、@.*?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")

これは、固定stringsでは機能しますが、@.*?foo.barと同等の任意の正規表現では機能しません。

20

awkが貪欲でない一致を行うことができないための回避策を提供するいくつかの良い答えがすでにあるので、 Perl互換の正規表現 を使用してそれを行う別の方法に関する情報を提供します=(PCRE)。最も単純な「一致して印刷」するawkスクリプトは、Perl-nコマンドラインオプションを使用して簡単に再実装でき、さらに複雑なスクリプトは a2p AwkからPerlへのトランスレータ。

Perl には、Perleスクリプトや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
6
cas

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

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
}
3
Jim Mellander

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

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の回答に基づいてこれを使用しています。 smatchmatchのように動作し、次を返します:

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

2
ericbn

Awkには貪欲でないマッチングを行う方法はありません。ただし、目的の出力を取得できる場合があります。 schの提案はその行で機能します。コンマに頼ることはできないが、「Author」が常に必要なものの始まりである場合は、次のようにすることができます。

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

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

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

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

1
user17591

私はこれが古い記事であることを知っています。しかし、これは要求に応じて単にawkをOPとして使用するものです:
A = @ article {gjn2010jucs、Author = {Grzegorz J. Nalepa}、
echo $ A | awk 'sub(/ @ [^、] * /、 "")'

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

0
VINAY NAIR

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

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
kerolasa