NAME

perlfaq6 - 正規表現 ($Revision: 10126 $)

DESCRIPTION

この章は驚くほど小さくなっています。 なぜならFAQの残りの部分は 正規表現を伴った回答と一緒にあちこちに散在しているからです。 たとえばURLをデコードするとかあるものが数値かどうか確認することは 正規表現を使って処理されますが、この回答はこの資料のあらゆる所で 見つけることができます(正確には perlfaq9: "How do I decode or create those %-encodings on the web" と perlfaq4: "How do I determine whether a scalar is a number/whole/integer/float")。

判読し難い、保守できないようなものにすることなく正規表現を使うには?

正規表現を保守可能なものにし、理解できるようにするための 三つの技法があります。

正規表現の外側にコメントを付ける

通常のPerlのコメントを使って、 あなたが何を、どのようにしているかを説明します。

        # turn the line into the first word, a colon, and the
        # number of characters on the rest of the line
        s/^(\w+)(.*)/ lc($1) . ":" . length($2) /meg;
正規表現の内側にコメントを付ける

<CODE>/x</CODE> 修飾子は、正規表現中にある空白を無視するようにし、 (キャラクタクラスの中にあるものを除く)、通常のコメントが使えるように します。 あなたの想像できるように、空白とコメントは非常に助けに なります。

/xによって

        s{<(?:[^>'"]*|".*?"|'.*?')+>}{}gs;

この正規表現を以下の様に記述できます:

        s{ <                    # opening angle bracket
                (?:                 # Non-backreffing grouping paren
                        [^>'"] *        # 0 or more things that are neither > nor ' nor "
                                |           #    or else
                        ".*?"           # a section between double quotes (stingy match)
                                |           #    or else
                        '.*?'           # a section between single quotes (stingy match)
                ) +                 #   all occurring one or more times
                >                   # closing angle bracket
        }{}gsx;                 # replace with nothing, i.e. delete

これでもまだ散文(prose)程には明確にはなっていませんが、 パターンの各部分の意味を説明するには非常に便利なものです。

異なった区切り

私たちは通常、/で区切られたものをパターンであると考えていますが、 パターンはほとんどすべてのキャラクタを使って区切ることが可能です。 perlreはこれを説明しています。 たとえば、先に挙げたs/// では、区切りとしてカーリーブレースを使っています。 スラッシュ以外の区切りを選択することによって、パターンの中に存在する 区切り記号と同じものをクォートする手間を省くことができます。

        s/\/usr\/local/\/usr\/share/g;  # bad delimiter choice
        s#/usr/local#/usr/share#g;              # better

二行以上に対するマッチングでトラブルがありました。何が悪いのでしょう?

マッチングの対象となっている文字列が実際には二行以上になっていないか、 パターンで正しい修飾子 (modifier)を使っていないかのいずれかでしょう (多分)。

複数行のデータを一つの文字列にする方法はたくさんあります。 これを、入力を読み込んでいる間自動で行なわせたいというのであれば、 一度に二行以上読ませるために $/を(パラグラフ単位で読み込みたいなら '' を、 ファイル全体を読み込みたいなら undef を)設定したくなるでしょう。

あなたが使いたいのは /s/m のいずれなのか(あるいはこれら 両方なのか)を決めるのを助けるために、perlre を読んでください: /s はドットが改行を含むようにしますし、/m はキャレットとドル記号が 文字列の両端だけでなく改行の前後でマッチするようにします。 そして、複数行に渡る文字列を取得するようにさせる必要があります。

たとえば、以下に挙げるプログラムは重複した単語を、たとえそれが行を またがっていても(ただしパラグラフはまたがっていない)探し出すものです。 この例では、/sの必要はありません。 なぜなら、この行をまたがらせたい正規表現でドットを使っていないからです。 /mを使う必要もありません。 それは、キャレットやドル記号をレコードの中にある改行の前後で マッチさせることは望んでいないからです。 しかし、$/ をデフォルト以外のものに設定することは避けられませんし、 そうしなければ複数行レコードを読み込むことはできないのです。

        $/ = '';                # read in more whole paragraph, not just one line
        while ( <> ) {
                while ( /\b([\w'-]+)(\s+\1)+\b/gi ) {   # word starts alpha
                        print "Duplicate $1 at paragraph $.\n";
                }
        }

以下の例は、“From ”で始まるセンテンス(多くのメイラーによって 変形されるであろうもの)を検索するものです。

        $/ = '';                # read in more whole paragraph, not just one line
        while ( <> ) {
                while ( /^From /gm ) { # /m makes ^ match next to \n
                print "leading from in paragraph $.\n";
                }
        }

次の例は、パラグラフ中の START と END に挟まれた部分を検索するものです:

        undef $/;               # read in whole file, not just one line or paragraph
        while ( <> ) {
                while ( /START(.*?)END/sgm ) { # /s makes . cross line boundaries
                    print "$1\n";
                }
        }

異なる行にある二つのパターンに挟まれている行を取り出すのはどうやればできますか?

Perlの .. 演算子を使えます(perlop に説明があります)。

        perl -ne 'print if /START/ .. /END/' file1 file2 ...

行ではなく、テキストが必要なら次のようにします

        perl -0777 -ne 'print "$1\n" while /START(.*?)END/gs' file1 file2 ...

しかし、STARTEND が現れるのを入れ子にさせたいというのであれば、 このセクションにある質問で説明されている問題に直面することになります。

..を使った別の例です:

        while (<>) {
                $in_header =   1  .. /^$/;
                $in_body   = /^$/ .. eof;
        # now choose between them
        } continue {
                $. = 0 if eof;  # fix $.
        }

$/ に正規表現を入れたけど動きません。何が悪いの?

$/ は文字列でなければなりません。 もし本当にそうする必要があるなら、以下の例が使えます。

もし File::Stream があれば、簡単です。

        use File::Stream;

        my $stream = File::Stream->new(
                $filehandle,
                separator => qr/\s*,\s*/,
                );

        print "$_\n" while <$stream>;

File::Stream がなければ、もう少し作業が必要です。

継続的にバッファに追加するために、sysread の 4 引数形式が使えます。 バッファに追加した後、(正規表現を使って)行全体が揃っているかを チェックします。

        local $_ = "";
        while( sysread FH, $_, 8192, length ) {
                while( s/^((?s).*?)your_pattern/ ) {
                        my $record = $1;
                        # do stuff here.
                }
        }

もしファイル全体を最後までメモリに入れることを気にしないのなら、 foreach および、c フラグと \G アンカーを使ったマッチングを使って 同じことができます。

        local $_ = "";
        while( sysread FH, $_, 8192, length ) {
                foreach my $record ( m/\G((?s).*?)your_pattern/gc ) {
                        # do stuff here.
                }
        substr( $_, 0, pos ) = "" if pos;
        }

演算子の左辺では大小文字を無視して、演算子の右辺では元の大小文字を保存しておくような置換をするには?

以下に Larry Rosler によるとっても Perl ぽい解法があります。 これは ASCII 文字列に対するビット xor の動作を悪用します。

        $_= "this is a TEsT case";

        $old = 'test';
        $new = 'success';

        s{(\Q$old\E)}
        { uc $new | (uc $1 ^ $1) .
                (uc(substr $1, -1) ^ substr $1, -1) x
                (length($new) - length $1)
        }egi;

        print;

以下はサブルーチンにしたものです。 上記と同じ手法です:

        sub preserve_case($$) {
                my ($old, $new) = @_;
                my $mask = uc $old ^ $old;

                uc $new | $mask .
                        substr($mask, -1) x (length($new) - length($old))
    }

        $a = "this is a TEsT case";
        $a =~ s/(test)/preserve_case($1, "success")/egi;
        print "$a\n";

これは以下のように表示します:

        this is a SUcCESS case

代替案として、もし元のものの方が長い場合には置き換え単語の大文字小文字を 維持する場合は、Jeff Pinyan による以下のコードが使えます:

        sub preserve_case {
                my ($from, $to) = @_;
                my ($lf, $lt) = map length, @_;

                if ($lt < $lf) { $from = substr $from, 0, $lt }
                else { $from .= substr $to, $lf }

                return uc $to | ($from ^ uc $from);
                }

これは文章を "this is a SUcCess case." に変更します。

もしもっと C っぽい解決法が好みなら、 以下に挙げるスクリプトは、大小文字の違いを保ったまま、 文字毎に置換を行ないます。 (そしてこれは Perl っぽい解法より 240% 遅いです。) 置換対象の文字列よりも多くの文字が置換後の文字列にあるのであれば、 最後の文字の大小文字の種別が置換後の文字列の残りの部分の 文字に対して使われます。

        # Original by Nathan Torkington, massaged by Jeffrey Friedl
        #
        sub preserve_case($$)
        {
                my ($old, $new) = @_;
                my ($state) = 0; # 0 = no change; 1 = lc; 2 = uc
                my ($i, $oldlen, $newlen, $c) = (0, length($old), length($new));
                my ($len) = $oldlen < $newlen ? $oldlen : $newlen;

                for ($i = 0; $i < $len; $i++) {
                        if ($c = substr($old, $i, 1), $c =~ /[\W\d_]/) {
                                $state = 0;
                        } elsif (lc $c eq $c) {
                                substr($new, $i, 1) = lc(substr($new, $i, 1));
                                $state = 1;
                        } else {
                                substr($new, $i, 1) = uc(substr($new, $i, 1));
                                $state = 2;
                        }
                }
                # finish up with any remaining new (for when new is longer than old)
                if ($newlen > $oldlen) {
                        if ($state == 1) {
                                substr($new, $oldlen) = lc(substr($new, $oldlen));
                        } elsif ($state == 2) {
                                substr($new, $oldlen) = uc(substr($new, $oldlen));
                        }
                }
                return $new;
        }

\w がローカルな文字セットにマッチするようにするには?

use locale; をスクリプトに書いてください。 \w 文字クラスが現在のロケールから取られるようになります。

詳しくは perllocale を参照してください。

/[a-zA-Z]/の locale-smart なバージョンでマッチさせるには?

perlre で記述されている、POSIX 文字クラス文法 /[[:alpha:]]/ が 使えます。

あなたがどこにいるかに関わらず、英文字は \w から数字と下線を除いたものです。 正規表現としては、これは /[^\W\d_]/ のようになります。 その逆である非英字は、\W に含まれる全ての文字に数字と下線を加えたもの、 つまり /[\W\d_]/ です。

正規表現の中で使う変数をクォートするには?

Perlの構文解析器(parser)は、区切りがシングルクォーテーションでない限り、 正規表現の中にある $variable や @variable といったものを展開します。 s/// による置換の右側にあるものはダブルクォーテーションで 括られた文字列とみなされるということを忘れないでください。 また、すべての正規表現演算子はその前に \Q を置いておかないと、 正規表現演算子として振る舞うということも忘れないでください。 以下に例を挙げます。

        $string = "Placido P. Octopus";
        $regex  = "P.";

        $string =~ s/$regex/Polyp/;
        # $string is now "Polypacido P. Octopus"

. 正規表現では特別で、任意の 1 文字にマッチングするので、 ここでの P. は元の文字列の <Pl> にマッチングします。

. の特殊な意味をエスケープするには、\Q を使います:

        $string = "Placido P. Octopus";
        $regex  = "P.";

        $string =~ s/\Q$regex/Polyp/;
        # $string is now "Placido Polyp Octopus"

\Q を使うことによって、正規表現中の . は通常の文字として扱われるので、 P.P の後にピリオドがあるものにマッチングします。

/o は実際なんのためのものなのですか?

(brian d foy によって寄贈されました)

正規表現の /o オプション (perlopperlreref で文書化されています) は、正規表現を一度だけコンパイルするように Perl に伝えます。 これはパターンに変数が含まれている場合にのみ有用です。 Perls 5.6 以降では、パターンが変わらない場合はこれを自動的に扱います。

マッチング演算子 m//, 置換演算子 s///, 正規表現クォート演算子 qr// はダブルクォート風構造なので、パターン中で変数を展開できます。 詳細については "How can I quote a variable to use in a regex?" の答えを 参照してください。

この例は正規表現を引数リストから取って、それにマッチングする入力行を 表示します:

        my $pattern = shift @ARGV;
        
        while( <> ) {
                print if m/$pattern/;
                }

バージョン 5.6 より前の Perl では、$pattern に変更がなくても反復毎に 正規表現が再コンパイルされます。 /o をつけると、パターンを初回にコンパイルし、引き続く反復では 再利用するように Perl に伝えることで、再コンパイルを防ぎます:

        my $pattern = shift @ARGV;
        
        while( <> ) {
                print if m/$pattern/o; # useful for Perl < 5.6
                }

バージョン 5.6 以降では、変数が変更されていない場合は Perl は正規表現を 再コンパイルしませんので、おそらく /o は不要です。 害はもたらしませんが、助けにもなりません。 どのバージョンでも、たとえ変数の値が変わっても正規表現を一度だけ コンパイルするようにしたい場合は、未だに /o が必要です。

Perl が正規表現を再コンパイルしたときに自分自身で検証するために、動作中の Perl の正規表現エンジンを見守ることもできます。 use re 'debug' プラグマ (Perl 5.005 以降で実装されています) は詳細を 表示します。 5.6 より前の Perl では、各反復で正規表現がコンパイルされる毎に re の 報告を見ることになります。 Perl 5.6 以降では、最初の反復でのみ re の報告を見ることになります。

        use re 'debug';
        
        $regex = 'Perl';
        foreach ( qw(Perl Java Ruby Python) ) {
                print STDERR "-" x 73, "\n";
                print STDERR "Trying $_...\n";
                print STDERR "\t$_ is good!\n" if m/$regex/;
                }

ファイルから、C 形式のコメントを剥ぎ取る(strip)するにはどのように正規表現を使えば良いのでしょうか?

実際これは可能なのですが、あなたが考えているよりも非常に難しいものです。 たとえば次の一行野郎 (one-liner) はほとんどの場合にうまく行きますが、 すべての場合ではありません。

        perl -0777 -pe 's{/\*.*?\*/}{}gs' foo.c

そう、これは C のプログラムを簡単に考えすぎているのです。 特に、クォートされた文字列にコメントが出現するということを考慮していません。 このため、Jeffrey Friedl が作成し、後に Fred Curtis によって修正された 次の例のようなことが必要になります。

        $/ = undef;
        $_ = <>;
        s#/\*[^*]*\*+([^/*][^*]*\*+)*/|("(\\.|[^"\\])*"|'(\\.|[^'\\])*'|.[^/"'\\]*)#defined $2 ? $2 : ""#gse;
        print;

もちろんこれは、/x 修飾子を使って空白やコメントを付加することで、 より読みやすくすることが可能です。 以下は Fred Curtis の提供による拡張版です。

    s{
       /\*         ##  Start of /* ... */ comment
       [^*]*\*+    ##  Non-* followed by 1-or-more *'s
       (
         [^/*][^*]*\*+
       )*          ##  0-or-more things which don't start with /
                   ##    but do end with '*'
       /           ##  End of /* ... */ comment

     |         ##     OR  various things which aren't comments:

       (
         "           ##  Start of " ... " string
         (
           \\.           ##  Escaped char
         |               ##    OR
           [^"\\]        ##  Non "\
         )*
         "           ##  End of " ... " string

       |         ##     OR

         '           ##  Start of ' ... ' string
         (
           \\.           ##  Escaped char
         |               ##    OR
           [^'\\]        ##  Non '\
         )*
         '           ##  End of ' ... ' string

       |         ##     OR

         .           ##  Anything other char
         [^/"'\\]*   ##  Chars which doesn't start a comment, string or escape
       )
     }{defined $2 ? $2 : ""}gxse;

(継続文字を使って複数行にわたっていない限りは)少しの変更で C++ コメントも 除去します:

        s#/\*[^*]*\*+([^/*][^*]*\*+)*/|//[^\n]*|("(\\.|[^"\\])*"|'(\\.|[^'\\])*'|.[^/"'\\]*)#defined $2 ? $2 : ""#gse;

Perl の正規表現をバランスされたテキストのマッチングに使えますか?

歴史的に、Perl の正規表現はバランスされたテキストをマッチングする 能力はありませんでした。 5.6.1 以降の最近の perl では、これを行えるようにする実験的機能が 追加されています。 バランスの取られたかっこにマッチングする例を見るには、最近の perlre マニュアルページの (??{ }) 構造のドキュメントを見てください。 この機能を使う前に、マニュアルにある警告について特に注意するように してください。

CPAN には、コンテキストに依存したテキストにマッチングするために有用な 多くのモジュールがあります。 Damian Conway は Regexp::Common でいくつかの有用なパターンを提供しています。 Text::Balanced モジュールはこの問題に対する一般的な解決法を提供します。

バランスされたテキストのマッチングのよくある活用法のひとつは XML と HTML に 対する作業です。 これらのニーズに対応する多くのモジュールが利用可能です。 2 つの例は HTML::Parser と XML::Parser です。 その他にもたくさんあります。

ネストする可能性のある `', {}, () のような 単一キャラクタのバランスを検査するための精巧なサブルーチンが、 http://www.cpan.org/authors/id/TOMC/scripts/pull_quotes.gz に あります(7-bit ASCII 専用)。

CPAN にある C::Scan モジュールもこのようなサブルーチンを内部的に 使っているのですが、ドキュメントには載っていません。

正規表現が貪欲(greedy)であるとはどういうことですか?

ほとんどの人が、貪欲正規表現(greedy regexps)は可能な限りマッチすると 考えています。 技術的には、量指定子(?, *, +, {}) はパターン全体よりも貪欲です。 Perl は、全体的な貪欲性よりも局所貪欲性と即時の満足性を好みます。 同じ量指定子の non-greedy バージョンを得るには、 ??, *?, +?, {}?を使います。

例:

        $s1 = $s2 = "I am very very cold";
        $s1 =~ s/ve.*y //;      # I am cold
        $s2 =~ s/ve.*?y //;     # I am very cold

二番目の置換が、“y ”を見つけてすぐにマッチングを中断していることに 注目してください。 量指定子 *? は正規表現エンジンに対して、あなたが熱いジャガイモを 扱っているときのように、可能な限り早くマッチするものを見つけて制御を次の行に 渡すように効果的に指示します。

各行の単語単位に処理をするにはどうすれば良いですか?

split 関数を使います:

        while (<>) {
                foreach $word ( split ) {
                        # do something with $word here
                }
        }

これは実際には英語でいうところの語ではないことに注意してください。 これは、単なる連続した空白でない文字の塊です。

アルファベットもしくは数字の並びのみを対象とするには以下のようにして できます。

        while (<>) {
                foreach $word (m/(\w+)/g) {
                        # do something with $word here
                }
        }

語の出現頻度や行の出現頻度のまとめを出力するには?

これを行うためには、入力ストリームにある単語のそれぞれについて解析する 必要があります。 私たちはここで、一つ前の質問と同様に、非空白文字の塊を語と するのではなく、英字、ハイフン、アポストロフィ、の塊を語とします:

        while (<>) {
                while ( /(\b[^\W_\d][\w'-]+\b)/g ) {   # misses "`sheep'"
                        $seen{$1}++;
                }
        }

        while ( ($word, $count) = each %seen ) {
                print "$count $word\n";
                }

同じことを行に対して行いたいのであれば、正規表現は必要ないでしょう。

        while (<>) {
                $seen{$_}++;
                }

        while ( ($line, $count) = each %seen ) {
                print "$count $line";
        }

ソートされた順序で出力したいのなら、perlfaq4: "How do I sort a hash (optionally by value instead of key)?" を参照してください。

曖昧なマッチング (approximate matching) はどうやればできますか?

CPAN で入手できる String::Approx モジュールを参照してください。

たくさんの正規表現を一度に効率良くマッチングするには?

(brian d foy によって寄贈されました)

マッチングを行う毎に Perl が正規表現をコンパイルすることを避けてください。 この例の場合、$pattern が何者なのかを知る方法がないので、perl は foreach() ループの反復毎に正規表現を再コンパイルしなければなりません。

        @patterns = qw( foo bar baz );

        LINE: while( <DATA> )
                {
                foreach $pattern ( @patterns )
                        {
                        if( /\b$pattern\b/i )
                                {
                                print;
                                next LINE;
                                }
                        }
                }

qr// 演算子は perl 5.005 で現れました。 これは正規表現をコンパイルしますが、適用はしません。 プリコンパイルされた正規表現を使うと、perl の作業は減ります。 この例では、各パターンをプリコンパイルされた形式に変換するための map() を 挿入しています。 スクリプトの残りの部分は同じですが、より速いです。

        @patterns = map { qr/\b$_\b/i } qw( foo bar baz );

        LINE: while( <> )
                {
                foreach $pattern ( @patterns )
                        {
                        print if /$pattern/i;
                        next LINE;
                        }
                }

いくつかの場合では、複数のパターンを一つの正規表現にできるかもしれません。 しかし、バックトラッキングが必要になる状況に注意してください。

        $regex = join '|', qw( foo bar baz );

        LINE: while( <> )
                {
                print if /\b(?:$regex)\b/i;
                }

正規表現の効率に関するさらなる詳細については、Jeffrey Freidl による Mastering Regular Expressions を参照してください。 彼は、どのように正規表現エンジンが動作するかと、なぜある種のパターンが 驚くほど非効率かについて説明しています。 perl がどのように正規表現を適用するかを理解すれば、これらを個々の状況に 調整できます。

なぜ \b を使った単語境界の検索がうまく行かないのでしょうか?

(brian d foy によって寄贈されました)

実際に \b が何を行うかをしっかり理解してください: これは単語文字 (\w) と、単語文字でない何かとの境界です。 単語文字でないというのは \W かもしれませんが、文字列の最初と最後にも あります。

これは空白と非空白との境界ではなく(違うんです!)、私たちが文を作るのに 使う単語の間のものではありません。

正規表現的に言うと、単語境界は (\b) は「ゼロ幅アサーション」です; これは文字列中の文字ではなく、ある位置での条件を表現しています。

正規表現 /\bPerl\b/ では、"P" の前と "l" の後に単語境界がなければなりません。 "P" の前と "l" の後に単語文字以外の何かがある限り、このパターンは マッチングします。 以下の文字列は /\bPerl\b/ にマッチングします。

        "Perl"    # no word char before P or after l
        "Perl "   # same as previous (space is not a word char)
        "'Perl'"  # the ' char is not a word char
        "Perl's"  # no word char before P, non-word char after "l"

これらの文字列は /\bPerl\b/ にマッチングしません。

        "Perl_"   # _ is a word char!
        "Perler"  # no word char before P, but one after l

しかし、単語にマッチングさせるために \b を使う必要はありません。 単語文字に囲まれた非単語文字を探すのに使えます。 これらの文字列は、パターン /\b'\b/ にマッチングします。

        "don't"   # the ' char is surrounded by "n" and "t"
        "qep'a'"  # the ' char is surrounded by "p" and "a"

これらの文字列は /\b'\b/ にマッチングしません。

        "foo'"    # there is no word char after non-word '

単語境界ではないということを指定するために、\b の逆である \B も使えます。

パターン /\Bam\B/ では、"a" の前と "m" の後ろに単語文字が必要です。 これらはパターン /\Bam\B/ にマッチングします:

        "llama"   # "am" surrounded by word chars
        "Samuel"  # same

これらの文字列は /\Bam\B/ にマッチングしません。

        "Sam"      # no word boundary before "a", but one after "m"
        "I am Sam" # "am" surrounded by non-word chars

なぜ $&, $`, $' を使うとプログラムが遅くなるの?

(brian d foy によって寄贈されました)

プログラムのどこかでそういった変数が使われているのを見つけてしまうと、 Perl はすべてのパターンマッチングに対してそれに対処することを やらなければなりません。 これは、全てのパターンマッチングに置いて、一部は $` に、一部は $& に、 一部は $' に、という形で文字列全体がコピーされるということを意味します。 従って、ペナルティは長い文字列とパターンを何度もマッチングさせるときに 最も厳しいものとなります。 可能であれば $& や $'、$` を使わないようにすべきなのですが、 それができないのであれば、一度これらの変数を使ってしまったら好きなように 使いましょう。 なぜなら、罰金はすでに払ってしまったのですから。 アルゴリズムの中にはこういった変数を使うことが適切であるものが あるということに注意してください。 リリース 5.005 からは、$& はもはや“高価な”ものではありません。

Perl 5.6.1 から、特殊変数 @- と @+ で $`, $&, $' の機能を 置き換えられるようになりました。 これらの配列はそれぞれのマッチングの開始位置と終了位置へのポインタを 含みます(詳しい話については perlvar を参照してください)ので、 本質的にはこれらから同じ情報が得られますが、 過度の文字列コピーのリスクはありません。

正規表現の中で \G を使うと何が良いのですか?

次のマッチングを同じ文字列のうち、前回のマッチングを中止したところから 始めるために \G アンカーを使います。 正規表現エンジンはこのアンカーがあるときには次のマッチングを探すために 何の文字も読み飛ばさないので、\G は文字列先頭アンカーである ^ と 似ています。 \G アンカーは典型的には g フラグと共に使います。 これは次のマッチングの開始位置として pos() の値を使います。 マッチング演算子がマッチングに成功すると、pos() の値を最後のマッチングの 次の文字(または次のマッチングの最初の文字; これはあなたがこれをどのように 見るかの問題です)の位置に更新します。 各文字列はそれぞれ独自の pos() の値を持ちます。

"1122a44" のような文字列で、連続した数字のペアの全てにマッチングし、数字で ないものが現れたら停止したいとします。 1122 にはマッチングしたいですが、2244 の間に a が 現れると、a で停止したいとします。 数字のペアのマッチングは単に a を読み飛ばし、引き続き 44 に マッチングします。

        $_ = "1122a44";
        my @pairs = m/(\d\d)/g;   # qw( 11 22 44 )

\G アンカーを使うと、22 の後のマッチングを a から始めることを 強制します。 ここでは数字が見付からないのでマッチングできず、従って次のマッチングは 失敗してマッチング演算子は既に見付かったペアを返します。

        $_ = "1122a44";
        my @pairs = m/\G(\d\d)/g; # qw( 11 22 )

スカラコンテキストにも \G アンカーが使えます。 g フラグはまだ必要です。

        $_ = "1122a44";
        while( m/\G(\d\d)/g )
                {
                print "Found $1\n";
                }

文字 a のマッチングに失敗した後、perl は pos() をリセットし、 同じ文字列に対する次のマッチングは先頭から行います。

        $_ = "1122a44";
        while( m/\G(\d\d)/g )
                {
                print "Found $1\n";
                }

        print "Found $1 after while" if m/(\d\d)/g; # finds "11"

perlopperlreref に書いているように、c フラグをつけることで pos() のリセットを抑制できます。 引き続くマッチングは、その間に同じ文字列でのマッチングが失敗していても、 直前の成功したマッチングが終了した場所 (pos() の値) から開始されます。 この場合、while() ループの後のマッチングは (最後のマッチングが停止した) a から開始され、何のアンカーも使っていないので、44 を見つけるために a をスキップできます。

        $_ = "1122a44";
        while( m/\G(\d\d)/gc )
                {
                print "Found $1\n";
                }

        print "Found $1 after while" if m/(\d\d)/g; # finds "44"

典型的には、トークナイザのようなものの中で、一つのマッチングに失敗した ときに他のマッチングを試したいときに、\G アンカーに c フラグを つけて使います。 Jeffrey Friedl が、5.004 以降で動作する、これに関する例を提供してくれました。

        while (<>) {
                chomp;
                PARSER: {
                        m/ \G( \d+\b    )/gcx   && do { print "number: $1\n";  redo; };
                        m/ \G( \w+      )/gcx   && do { print "word:   $1\n";  redo; };
                        m/ \G( \s+      )/gcx   && do { print "space:  $1\n";  redo; };
                        m/ \G( [^\w\d]+ )/gcx   && do { print "other:  $1\n";  redo; };
                }
        }

それぞれの行において、PARSER ループはまず数字の列に引き続く単語境界に マッチングしようと試みます。 このマッチングは直前のマッチングが行われた場所(最初のマッチングの場合は 文字列の先頭)から始められなければなりません。 m/ \G( \d+\b )/gcxc フラグを使っているので、もし文字列がこの 正規表現にマッチングしなければ、perl は pos() をリセットせず、次の マッチングは、他のパターンを試す場合でも同じ位置から開始します。

Perl の正規表現ルーチンは DFA ですか NFA ですか? また、それは POSIX に従ってますか?

Perl の正規表現は egrep(1) の DFA (deterministic finite automata, 決定性有限オートマトン)と似たものではあるのですが、 実際のところはバックトラックや後方参照 (backreferencing)のために NFA として実装されています。 そして、Perl の正規表現は POSIX 形式のものでもありません。 なぜなら、それはすべてのケースにおいて最悪の振る舞いを行うからです (一部の人は、それが遅さをもたらすにもかからわず、一貫性をもたらすという点を 好んでいるようです)。 上記のことなどに関しての詳細は Jeffrery Friedl による O'Reilly から 出版されている "Mastering Regular Expressions" という本を参照してください。

無効コンテキストで grep を使うことのどこが間違っているのでしょうか?

問題は、grep はそのコンテキストには関係なくリストを返すことです。 これはつまり、Perl にあなたが無視してしまうための戻り値のリストを 作らせるということです。 もしリストが大きいなら、時間とメモリの両方を無駄にします。 あなたの目的がリスト全体に対して反復することなら、for ループを 使ってください。

5.8.1 より前の perl では、map も同じ問題の影響を受けていました。 しかし 5.8.1 から、これは修正され、map はコンテキストを 認識するようになりました - 無効コンテキストではリストは作られません。

マルチバイト文字を含む文字列をマッチングさせるには?

Perl 5.6 から、Perl にはあるレベルのマルチバイト文字サポートがあります。 Perl 5.8 以降を推奨します。 対応するマルチバイト文字のレパートリーには、Unicode と、Encode モジュール 経由のレガシーエンコーディングを含みます。 perluniintro, perlunicode, Encode を参照してください。

もしもっと古い Perl に捕まっているなら、Unicode::String モジュールで Unicode ができ、Unicode::Map8Unicode::Map のモジュールを使って 文字変換ができます。 日本語エンコーディングを使っているなら、jperl 5.005_03 を使ってみたいかも しれません。

最後に、The Perl Journal の第五号でこの問題についてより詳しい記事を書いた Jeffery Friedl により提案された手法の幾つかを挙げます。

さて、ここで ASCII の大文字二文字で火星語の符号化をしていると仮定しましょう (たとえば、"CV", "SG", "VS", "XX" などといった二バイトの並びが 火星語の一文字を表わすということです)。

ですから、火星語の符号化をしている 12 バイトの "I am CVSGXX!" 文字列は、 'I', ' ', 'a', 'm', ' ', 'CV', 'SG', 'XX', '!' という九文字で構成されます。

ここで、/GX/ という一文字検索をしたいと考えてみましょう。 Perl は火星語については何も知りませんから、"I am CVSGXX!" という文字列にある "GX" 二バイトを見つけ出してしまうでしょうが、これは文字として そこにあるものではありません。 つまり、"SG" に続けて "XX" があるのでそう見えるだけであって、 本当に "GX" があるわけではないのです。 これは大きな問題です。

この問題に対処する方法が、どれもうんざりするようなものですが、幾つかあります:

        # Make sure adjacent "martian" bytes are no longer adjacent.
        $martian =~ s/([A-Z][A-Z])/ $1 /g;

        print "found GX!\n" if $martian =~ /GX/;

あるいは:

        @chars = $martian =~ m/([A-Z][A-Z]|[^A-Z])/g;
        # above is conceptually similar to:     @chars = $text =~ m/(.)/g;
        #
        foreach $char (@chars) {
        print "found GX!\n", last if $char eq 'GX';
        }

あるいは:

        while ($martian =~ m/\G([A-Z][A-Z]|.)/gs) {  # \G probably unneeded
                print "found GX!\n", last if $1 eq 'GX';
                }

以下は Benjamin Goldberg による、同じことをもう少しましに行うもので、 ゼロ幅否定前方参照アサーションを使っています。

        print "found GX!\n" if  $martian =~ m/
                (?<![A-Z])
                (?:[A-Z][A-Z])*?
                GX
                /x;

これはもし "martian" 文字 GX が文字列にあれば成功し、そうでなければ 失敗します。 ゼロ幅否定前方参照アサーション (?<!) を使いたくないなら、(?<![A-Z]) を (?:^|[^A-Z]) で置き換えられます。

これには $-[0] と $+[0] に間違ったものが入るという欠点がありますが、普通 これは回避できます。

変数に入っている正規表現でマッチングを行うには?

(brian d foy によって寄贈されました)

マッチング演算子(あるいはその他の正規表現として働くもの)にパターンを ハードコーディングする必要はありません。 後で使うためにパターンを変数に入れることができます。

マッチング演算子はダブルクォートコンテキストなので、ダブルクォート文字列と 同様に変数展開できます。 個の場合、正規表現をユーザー入力として読み込んで、$regex に保管します。 パターンが $regex に入れば、この変数をマッチング演算子の中で使えます。

        chomp( my $regex = <STDIN> );

        if( $string =~ m/$regex/ ) { ... }

$regex 内の全ての正規表現特殊文字は特殊なままで、パターンが 有効でなければ Perl はエラーを出します。 例えば、このパターンには組になっていないかっこがあります。

        my $regex = "Unmatched ( paren";

        "Two parens to bind them all" =~ m/$regex/;

Perl が正規表現をコンパイルする時に、かっこをマッチング記憶の開始として 扱います。 閉じかっこが見つからないと、エラーが出ます:

        Unmatched ( in regex; marked by <-- HERE in m/Unmatched ( <-- HERE  paren/ at script line 3.

状況に依存して、いくつかの方法でこれを回避できます。 まず、文字列中のどの文字も特別ではないなら、文字列を使う前に quotemeta でエスケープできます。

        chomp( my $regex = <STDIN> );
        $regex = quotemeta( $regex );

        if( $string =~ m/$regex/ ) { ... }

\Q\E のシーケンスを使うことで、これをマッチング演算子の中で直接 行うこともできます。 \Q は Perl に特殊文字のエスケープを開始する位置を示し、\E はそれを 終了する位置を示します (さらなる詳細については perlop を 参照してください)。

        chomp( my $regex = <STDIN> );

        if( $string =~ m/\Q$regex\E/ ) { ... }

あるいは、正規表現クォート演算子である qr// が使えます(さらなる 詳細については perlop を参照してください)。 これはパターンをクォートし、おそらくはコンパイルされます; また、パターンに正規表現フラグを適用できます。

        chomp( my $input = <STDIN> );

        my $regex = qr/$input/is;

        $string =~ m/$regex/  # same as m/$input/is;

全体を eval ブロックで包むことで全てのエラーをトラップしたいかも しれません。

        chomp( my $input = <STDIN> );

        eval {
                if( $string =~ m/\Q$input\E/ ) { ... }
                };
        warn $@ if $@;

または...

        my $regex = eval { qr/$input/is };
        if( defined $regex ) {
                $string =~ m/$regex/;
                }
        else {
                warn $@;
                }

REVISION

Revision: $Revision: 10126 $

Date: $Date: 2007-10-27 21:29:20 +0200 (Sat, 27 Oct 2007) $

See perlfaq for source control details and availability.

AUTHOR AND COPYRIGHT

Copyright (c) 1997-2007 Tom Christiansen, Nathan Torkington, and other authors as noted. All rights reserved.

This documentation is free; you can redistribute it and/or modify it under the same terms as Perl itself.

Irrespective of its distribution, all code examples in this file are hereby placed into the public domain. You are permitted and encouraged to use this code in your own programs for fun or for profit as you see fit. A simple comment in the code giving credit would be courteous but is not required.