「。*?」を使用する方法と理由


9

SuperUserこの質問に回答しました。これは、出力のgrep中に使用される正規表現の種類に関連したものでした。

私が出した答えはこれでした:

 tail -f log | grep "some_string.*some_string"

そして、私の答えへの3つのコメントで@ボブはこれを書いた:

.*貪欲であり、あなたが望むより多くをキャプチャするかもしれません。.*?通常はより良いです。

そしてこれ、

これ?はの修飾子で*あり、貪欲なデフォルトの代わりに遅延させます。PCREを想定しています。

私はグーグルで検索しましPCREたが、私の回答でこれの意味を理解できませんでしたか?

そして最後にこれ、

また、これは正規表現(デフォルトではPOSIX正規表現を実行するgrep)であり、シェルグロブではないことも指摘しておく必要があります。

正規表現とは何か、そしてgrepコマンドでの非常に基本的な使い方だけを知っています。だから、私はそれらの3つのコメントのどれも得ることができず、私はこれらの質問を念頭に置いています:

  • 用法の違いは何ですか.*?対は.*
  • どちらが良いですか、どのような状況下ですか?例を挙げてください。

また、コメントを理解しておくと役に立ちます。


更新:質問への回答として、正規表現はシェルグロブとどのように異なりますか? @Kusalanandaがコメントでこのリンクを提供しました。

注:必要に応じて、コンテキストを参照するために回答する前に、この質問に対する私の回答を読んでください。


これは2つの非常に異なる質問です。最初の質問はunix.stackexchange.com/questions/57957/…によって回答されますが、2番目の質問はパターンの適用に依存します(すべての状況で「より良い」とは言えません)。
クサラナンダ

この質問を編集して、.*vs .*?。の問題のみについて質問することができます。「正規表現とシェルグロブの違い」の質問は、このサイトですでに対処されています。
クサラナンダ

回答:


7

アショクはすでに指摘違いを.*し、.*?私はいくつかの追加情報を提供しますので、。

grep (GNUバージョンを想定)は、文字列を照合する4つの方法をサポートしています。

  • 固定ストリング
  • 基本正規表現(BRE)
  • 拡張正規表現(ERE)
  • Perl互換の正規表現(PCRE)

grep デフォルトではBREを使用します。

BREとEREはPOSIXの正規表現の章に記載されており、PCREはその公式Webサイトに記載されています。機能と構文は実装によって異なる場合があることに注意してください。

BREもEREも遅延をサポートしていないことは言うまでもありません。

複数の隣接する重複記号(「+」​​、「*」、「?」、および間隔)の動作により、未定義の結果が生成されます。

したがって、その機能を使用する場合は、代わりにPCREを使用する必要があります。

# BRE greedy
$ grep -o 'c.*s' <<< 'can cats eat plants?'
can cats eat plants

# BRE lazy
$ grep -o 'c.*\?s' <<< 'can cats eat plants?'
can cats eat plants

# ERE greedy
$ grep -E -o 'c.*s' <<< 'can cats eat plants?'
can cats eat plants

# ERE lazy
$ grep -E -o 'c.*?s' <<< 'can cats eat plants?'
can cats eat plants

# PCRE greedy
$ grep -P -o 'c.*s' <<< 'can cats eat plants?'
can cats eat plants

# PCRE lazy
$ grep -P -o 'c.*?s' <<< 'can cats eat plants?'
can cats

編集1

.*vs について少し説明してもらえます.*?か?

  • .*「最長」の1パターンに一致させるために使用されます。

  • .*?「最短」の1パターンに一致させるために使用されます。

私の経験では、最も望まれる行動は通常2番目の行動です。

たとえば、次の文字列があり、HTMLタグ2のみを一致させ、それらの間のコンテンツは一致させたくないとします。

<title>My webpage title</title>

.*対比較します.*?

# Greedy
$ grep -P -o '<.*>' <<< '<title>My webpage title</title>'
<title>My webpage title</title>

# Lazy
$ grep -P -o '<.*?>' <<< '<title>My webpage title</title>'
<title>
</title>

1. Kusalanandaが指摘したように、正規表現のコンテキストでの「最長」と「最短」の意味は少し注意が必要です。詳細については、公式ドキュメントを参照してください。
2. 正規表現でhtmlを解析することはお勧めしません。これは単なる教育目的の例であり、本番環境では使用しないでください。


.*vs について少し説明してもらえます.*?か?
C0deDaedalus

@ C0deDaedalus更新されました。
nxnev

9

私が次のような文字列を取るとします。

can cats eat plants?

greedy c.*sを使用すると、文字列全体がで始まり、cで終わるため、文字列全体が一致します。s貪欲な演算子であるため、sが最後に出現するまで一致し続けます。

一方、レイジーc.*?sを使用すると、最初に出現するs文字列が見つかるまでしか一致しませんcan cats

上記の例から、それを収集できる可能性があります。

"Greedy"は、可能な限り長い文字列に一致することを意味します。「レイジー」とは、可能な限り短い文字列に一致することを意味します。追加?などの数量詞に*+?、または{n,m}になり、それは怠惰。


1
「可能な限り最短」はcatsなので、その意味で厳密に「最短」を強制するのではありません。
クサラナンダ

2
@Kusalananda true、厳密にはそういう意味ではありませんが、ここで「可能な限り最短」とは、cとsの両方が最初に現れる間を意味します。
アショク

1

文字列は(単純なものからより複雑なものまで)いくつかの方法で照合できます。

  1. 静的文字列として(var = 'Hello World!'と想定):

    [ "$var" = "Hello World!" ] && echo yes
    echo "$var" | grep -F "Hello"
    grep -F "Hello" <<<"$var"

  2. グロブとして:

    echo ./* #pwd内のすべてのファイルを一覧表示ます。
    case $var in (*Worl*) echo yes;; (*) echo no;; esac
    [[ "$var" == *"Worl"* ]] && echo yes

    基本的なグロブと拡張グロブがあります。このcase例では、基本的なグロブを使用しています。bashの[[例では拡張グロブを使用しています。最初のファイルの一致は、基本的なものかextglob、bashでの設定のようなシェルで拡張されたものです。この場合、どちらも同じです。Grepはグロブを使用できませんでした。

    アスタリスクグロブのアスタリスクとは異なる手段で何か正規表現

    * matches any number (including none) of任意の文字を要素の前の
    * matches any number (including none) of the

  3. 基本的な正規表現(BRE)として:

    echo "$var" | sed 's/W.*d//' #print:Hello!
    grep -o 'W.*d' <<<"$var" #print World!

    (基本)シェルまたはawkにはBREはありません。

  4. 拡張正規表現(ERE):

    [[ "$var" =~ (H.*l) ]] #一致:Hello Worl
    echo "$var" | sed -E 's/(d|o)//g' #印刷:Hell Wrl!
    awk '/W.*d/{print $1}' <<<"$var" #印刷:こんにちは
    grep -oE 'H.*l' <<<"$var" #印刷:こんにちはWorl

  5. Perl互換の正規表現:

    grep -oP 'H.*?l #print:Hel

PCREでのみ、aに*?は特定の構文上の意味があります。
アスタリスクが怠惰になります(貪欲ではありません):貪欲の代わりに怠惰

$ grep -oP 'e.*l' <<<"$var"
ello Worl

$ grep -oP 'e.*?l' <<<"$var"
el

これは氷山の一角にすぎず、貪欲、怠惰従順、または物欲があります。先読みと後読みもありますが、アスタリスクには適用されません*

貪欲でない正規表現と同じ効果を得るための代替手段があります:

$ grep -o 'e[^o]*o' <<<"$var"
ello

アイデアは非常に単純です。ドットを使用しないで.、次に一致する文字を否定し[^o]ます。Webタグを使用:

$ grep -o '<[^>]*>' <<<'<script type="text/javascript">document.write(5 + 6);</script>'
<script type="text/javascript">
</script>

上記により、@ Bob 3のコメントがすべて明確になります。言い換え:

  • 。*はグロブではなく一般的な正規表現です。
  • PCREと互換性があるのは正規表現だけです。
  • PCREの場合:?*数量詞を変更します。.*貪欲で.*?はありません。

ご質問

  • の使用法の違いは何ですか。?対。

    • A .*?はPCRE構文でのみ有効です。
    • A .*はよりポータブルです。
    • 貪欲でない一致と同じ効果は、ドットを否定された文字範囲で置き換えることによって行うことができます: [^a]*
  • どちらが良いですか、どのような状況下ですか?例を挙げてください。
    いい?目標次第です。良いことはありません。それぞれが異なる目的に役立ちます。上記の例をいくつか示しました。もっと必要ですか?

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.