2019年5月10日金曜日

[Perl] 04. 正規表現

今回はPerlの正規表現についてのメモ。
正規表現とは、文字列の集まりを別の文字列を使って表現する方法のこと。ワイルドカード (*, ?) の上位互換という認識。パターンマッチ構文(=~)で正規表現を使う場合を例として、メモしておく。

パターンマッチは、文字列が正規表現で書かれたパターンを含む場合に真とする。
if (<文字列> =~ <正規表現>) {
  処理
}



基本
正規表現は通常 / で括って表す。
以下の例では $word に C という文字列が含まれているかを判定している。
$word = 'ABCDEF';
if ($word =~ /C/) {
    print "match!\n";
}

区切りの変更
先頭に m を付加すると / 以外でも括ることができる。文字列として / を使用したいときに見やすくなる。下記に挙げた記号以外も使えるが、実際に使うのはこれくらい。

m/パターン/
m!パターン!
m?パターン?
m{パターン}

メタ文字
正規表現にはメタ文字と言われる予約語がある。このメタ文字を使用することで様々な表現をすることが可能になっている。

. ^ $ * + ? { } [ ] ( ) | \

メタ文字を文字列として扱いたい場合は、直前に \ を挿入することで可能となる。
以下の例では $word に . が含まれるかを判定している。. はメタ文字なので直前に \ を挿入して文字列化している。
$word = 'ABC.DEF';
if ($word =~ /\./) {
    print "match!\n";
}

一文字を表す .
. は1文字を表す。文字の種類は意識しない。
以下の例では $word が A と F の間に4文字含むかどうかを判定している。
$word = 'ABCDEF';
if ($word =~ /A....F/) {
    print "match!\n";
}

以下の例では $word が3文字の文字列を含むかどうかを判定している。$word が2文字以下の場合はアンマッチとなる。
$word = 'ABCDEF';
if ($word =~ /.../) {
    print "match!\n";
}

先頭/末尾 ^, $
^, $ を使って先頭や末尾の文字列判定を行うことができる。
$word = 'ABCDEF';

# 文字列の先頭がAB
if ($word =~ /^AB/) {
    print "match!\n";
}

# 文字列の末尾がEF
if ($word =~ /EF$/) {
    print "match!\n";
}

量指定子 +, *, ?, {}
+, *, ? を使って文字列が0回以上または1回以上連続することを表現できる。
$word = 'ABBCCC';

# + : 直前の1文字が1回以上連続
if ($word =~ /AB+CCC/) {
    print "match!\n";
}

# * : 直前の1文字が0回以上連続
if ($word =~ /AB*CCC/) {
    print "match!\n";
}

# ? : 直前の1文字が0個または1個 (今回'B'は2回連続しているのでアンマッチ)
if ($word =~ /AB?CCC/) {
    print "match!\n";
}

{} を使って文字列の連続回数を指定することができる。
$word = 'ABBCCC';

# {n} : 直前の1文字がn回連続
if ($word =~ /AB{2}CCC/) {
    print "match!\n";
}

# {n,} : 直前の1文字がn回以上連続
if ($word =~ /AB{2,}CCC/) {
    print "match!\n";
}

# {n,m} : 直前の1文字がn回以上m回以下連続
if ($word =~ /AB{1,3}CCC/) {
    print "match!\n";
}

文字クラス []
[] を使って文字列の範囲を指定することができる。
A または B であることを表したい場合は [AB] となる。A~Z のように範囲で指定したい場合は [A-Z] となる。上記の量指定子 ( + や {} ) と一緒に使われることが多い。
$word = 'ABC';

# B または D
if ($word =~ /A[BD]C/) {
    print "match!\n";
}

# A~Z 内の一文字
if ($word =~ /A[A-Z]C/) {
    print "match!\n";
}

# A~Z または a~z または 0~9 内の一文字
if ($word =~ /A[A-Za-z0-9]C/) {
    print "match!X\n";
}

[] 内に ^ を用いることで特定の文字以外であることを表すことができる。上記で挙げた文字列の先頭という意味ではないので注意。
$word = 'ABC';

# X 以外の一文字
if ($word =~ /A[^X]C/) {
    print "match!\n";
}

グループ化 ()
() 使って文字列のグループ化を行うことができる。
$word = 'ABCDEF';

# 3文字の文字列 + 1文字の文字列 を含む場合にグループ化
if ($word =~ /(...)(.)/) {
    print "$1\n";
    print "$2\n";
}

$word = 'ABC123';

# 1文字以上の文字列 + 1文字以上の数字 を含む場合にグループ化
if ($word =~ /([A-Z]+)([0-9]+)/) {
    print "$1\n";
    print "$2\n";
}
実行結果
ABC
D
ABC
123

複数表現 |
| を使って複数の文字列判定を行うことができる。
$word = 'ABCDEF';

# BCD または XYZ を含む
if ($word =~ /BCD|XYZ/) {
    print "match!\n";
}

修飾子
正規表現には修飾子(オプション)がいくつかある。修飾子は正規表現の直後に付加する。複数指定も可能。

s 単一行として扱う (\n も . でマッチする)
m 複数行として扱う (^, $ は各行の先頭と末尾とマッチする)
i 大文字/小文字を区別しない
x 空白や改行を許可できるように拡張する
g 繰り返しマッチする

g は置換処理を行う場合によく使う。上記以外にも p, c, o 等があるが、使わないので省略。特に o の使用は非推奨。
$word = "ABC\n123";

# m : 文字列中の \n を行末とみなす (Cを末尾と認識できる)
if ($word =~ /C$/m) {
    print "match!\n";
}

$word = 'ABC123';

# i : 大文字/小文字を区別しない
if ($word =~ /abc/i) {
    print "match!\n";
}

# x : 空白や改行を許可できるように拡張する
if ($word =~ /
              ^ABC    # 先頭は ABC
              [0-9]+  # 数字が1つ以上
             /x) {
    print "match!\n";
}

エスケープシーケンス
エスケープシーケンスは \ + 文字 の組み合わせで特別な意味を持たせたもの。print 文でよく使う \n (改行) もこれにあたる。ファイル処理を行うときに必要そうなものだけをピックアップ。

\w 英数字と_ ([0-9a-zA-Z_] と同じ)
\W 英数字と_以外
\d 数字 ([0-9] と同じ)
\D 数字以外
\s 空白
\S 空白以外
\n 改行 (LF)
\r 復帰 (CR)
\t タブ文字
\l 次の文字を小文字に変換
\u 次の文字を大文字に変換
\L \E までを小文字に変換
\U \E までを小文字に変換
\Q \E までのメタ文字をエスケープ
\E \Q, \L, \U の処理を終了する

正規表現を変数に保存
qr 演算子を使うことで、正規表現(修飾子を含む)を変数に保存できるようになる。メリットとしては qr 演算子を実行する際にパターンがコンパイルされるので、繰り返し処理などで速度向上が見込める。
$word = "ABCDEF";
$regexp = qr/abc/i;  # abc (大文字/小文字は区別しない)

if ($word =~ $regexp) {
    print "match!\n";
}
実行結果
match!


0 件のコメント:

コメントを投稿