2642


Perlや.NETの正規表現には`{n}?`という最小量指定子が存在しますが、これはどのような場面で利用されるのでしょうか。


CC BY-SA 3.0
2014-12-26T13:19:32.333
2641
2014-12-26T20:36:50.007
2014-12-26T13:56:54.560
3639
3639
question
.net perl 正規表現
最小量指定子 {n}? の使いどころ


{n} と {n}? は同じで、他の量指定子と対称にするために存在していると思います。


CC BY-SA 3.0
2014-12-26T13:39:40.490
2642
2014-12-26T15:21:44.847
2014-12-26T15:21:44.847
4236
4236
2641
answer


単なる冗長な表記であって `{n}` と等価です。

Perl のマニュアル (perlre) より:

> `{n}?` Match exactly n times, not greedily ( **redundant** )


CC BY-SA 3.0
2014-12-26T14:23:10.127
2643
2014-12-26T14:23:10.127
76
2641
answer


`{n}`と`{n}?`が同じ動作をするというのは他の方達が説明している通りで、 .NETでは以下のページに量指定子の説明があります。

<http://msdn.microsoft.com/ja-jp/library/az24scfc(v=vs.110).aspx#quantifiers>

どのような場面で使用するのかということですが、 これは上のページにもあるとおり、直前の要素とちょうど `n` 回一致するものを取得したい場合に使用します。

たとえばC#であれば、以下のようなコードが書けると思います(実用的かどうかは別の話ですが)。

[code]

    var matches = Regex.Matches("123123123", "(123){2}");
    Debug.Assert(matches.Count == 1);
    Debug.Assert(matches[0].Index == 0);
    Debug.Assert(matches[0].Length == 6);
    
    var line = "      半角スペースでインデントされた行"; // 半角スペース6文字
    var match = Regex.Match(line, " {4}");
    Debug.Assert(match.Success);
    
    var newline = line.Substring(match.Index + match.Length);
    Debug.Assert(newline == "  半角スペースでインデントされた行"); // 半角スペース2文字
    
[/code]


CC BY-SA 3.0
2014-12-26T14:46:58.597
2645
2014-12-26T14:46:58.597
295
2641
answer


正規表現の質問ですね。これは、 Ruby などにも当てはまります。

`{n}` は、 `+`, `?`, `*`
などと同じように繰り返しや文字出現の表現(quantifier)であり、波括弧の中の数値で繰り返し回数の範囲を指定します。 `{最小回数, 最大回数}`
で表現するところを、`{n, n}` とし、最大最小が同じことを `{n}` と表現しています。

`?`
はマッチにあたっての最小量指定（言い方カッコイイ！）です。複数の一致が見られたときに最大一致のものを選択せずに、最小一致のものを選択せよという指定になります。

ここまでは理解されていると思います。

さて、実際は `?` はマッチングモードの切り替え指定です。 無指定または環境によっては `+`
だと、欲張りマッチ（Greedy）でありこれがデフォルトのマッチングモードです。 `?`
の指定だと、マッチングモードは、怠け者(Lazy)もしくはやる気なし(Reluctant)モードになります。やる気のないモードだと、繰り返しマッチ条件を１度満たした段階でそれ以上の走査を諦めます。

ですので、`{n}` の欲張りマッチだと、「指定数一致した！」と報告し、`{n}?`
の怠け者マッチだと「もういいやここであきらめる」と報告する、という内部的にですが、動きに違いがでます。ただし、二つのモードの報告する内容はいつも同じになりますので、`{n}?`
と指定するようなときには `?` の有無による違いはないことになります。

というわけで私の回答は、「気分的なもので、差がない」としたいですが、どうでしょうか。

[code]

    $ perl -e '$s="aaaaaabbbbbbbbccccccc"; print $s =~ /(b+)/;'
    -> bbbbbbbb
    
    $ perl -e '$s="aaaaaabbbbbbbbccccccc"; print $s =~ /(b+?)/;'
    -> b
    
    $ perl -e '$s="aaaaaabbbbbbbbccccccc"; print $s =~ /(b{4})/;'
    -> bbbb
    
    $ perl -e '$s="aaaaaabbbbbbbbccccccc"; print $s =~ /(b{4}?)/;'
    -> bbbb      # ここで ? の意味はない
    
    $ perl -e '$s="aaaaaabbbbbbbbccccccc"; print $s =~ /(b{4,8})/;'
    -> bbbbbbbb
    
    $ perl -e '$s="aaaaaabbbbbbbbccccccc"; print $s =~ /(b{4,8}?)/;'
    -> bbbb      # ここで 8 は意味がない
    
    $ perl -e '$s="abbbcabbbbcabbbbbcabbbbbbc"; print $s =~ /(a.{4,}c)/;'
    -> abbbbcabbbbbcabbbbbbc
    
    $ perl -e '$s="abbbcabbbbcabbbbbcabbbbbbc"; print $s =~ /(a.{4,}c)/;'
    -> abbbbc
    
    $ perl -e '$s="abbbcabbbbcabbbbbcabbbbbbc"; print $s =~ /(a.+c)/;'
    -> abbbcabbbbcabbbbbcabbbbbbc
    
    $ perl -e '$s="abbbcabbbbcabbbbbcabbbbbbc"; print $s =~ /(a.+?c)/;'
    -> abbbc
    
[/code]


CC BY-SA 3.0
2014-12-26T14:47:55.063
2646
2014-12-26T14:47:55.063
4978
2641
answer


Perl 5.18.2 のソースコードを眺めてみました。quantifier を処理しているのは `regcomp.c` にある `S_regpiece`
関数です。そこにこんなコメントがありました。

[code]

    - regpiece - something followed by possible [*+?]
    *
    * Note that the branching code sequences used for ? and the general cases
    * of * and + are somewhat optimized:  they use the same NOTHING node as
    * both the endmarker for their branch list and the body of the last branch.
    * It might seem that this node could be dispensed with entirely, but the
    * endmarker role is not redundant.
    
[/code]

Perl の開発者が regexp 用のスキャナを作る上では役に立っているのでしょうかね、、？

ところで、この関数を眺めてみると、Perl では、

[code]

    {n} {n}? {n}+
    
[/code]

は全て同じ正規表現なのですね(まぁ、ドキュメントに書いてありますね)。ところが、`{n}*` 形式はエラーとして処理しています。

[code]

    $ printf "I am very very cold.\n" | perl -w -pe "s/(v.*y ){2}//"
    I am cold.
    $ printf "I am very very cold.\n" | perl -w -pe "s/(v.*y ){2}?//"
    I am cold.
    $ printf "I am very very cold.\n" | perl -w -pe "s/(v.*y ){2}+//"
    I am cold.
    $ printf "I am very very cold.\n" | perl -w -pe "s/(v.*y ){2}*//"
    Nested quantifiers in regex; marked by <-- HERE in m/(v.*y ){2}* ...
    
[/code]

これは `{n,m}*` 形式でもエラーになります。

話は脱線しますが、先の関数では `{,m}` として `n` を省略すると `{0,m}`
として処理されます。これはワーニングも出ませんし、エラーにもなりません。0回のマッチングで処理されて、入力文字列はそのまま表示されます。`*`
を付けてもエラーになりません。

[code]

    $ printf "I am very very cold.\n" | perl -w -pe "s/(v.*y ){,2}//"
    I am very very cold.
    $ printf "I am very very cold.\n" | perl -w -pe "s/(v.*y ){0,2}//"
    I am very very cold.
    $ printf "I am very very cold.\n" | perl -w -pe "s/(v.*y ){,2}*//"
    I am very very cold.
    
[/code]

一方、`n > m` で指定するとエラーメッセージが表示されます。処理は行われず、入力データはそのままです。

[code]

    $ printf "I am very very cold.\n" | perl -w -pe "s/(v.*y ){2,1}//"
    Quantifier {n,m} with n > m can't match in regex; marked by <-- HERE in m/(v.*y ){2,1} ...
    I am very very cold.
    
[/code]

ところで、ブレースの中に整数値以外を入れるとどうなるのでしょうか。

[code]

    $ printf "I am very very cold.\n" | perl -w -pe "s/(v.*y ){very very cold}//"
    I am very very cold.
    
[/code]

これも何も起きません。これは `{0,0}` となってしまい、処理はスキップされて次の処理が実行されます。

[code]

    $ printf "I am very very cold.\n" | perl -w -pe "s/(v.*y ){very very cold}//;s/cold/sleepy/;"
    I am very very sleepy.
    
[/code]

`{}` や `{,}` などでも同様で、Perl で想定している形式以外は一部を除いて(n>mなど)静かに無視されてしまいます。


CC BY-SA 3.0
2014-12-26T20:19:48.223
2658
2014-12-26T20:36:50.007
2014-12-26T20:36:50.007
2641
answer