正規表現とは、文字列の集合を表現する方法である。
正規表現はawk
プログラミングにおいて非常に基本的な
部分であるので、その書式と使い方は章を分けて説明するに値する。
スラッシュ(`/')に囲まれた正規表現は、その正規表現が示す集合に
属するテキストが含まれる全ての入力レコードにマッチするawk
の
パターンである。
もっとも単純な正規表現は、文字や数字、もしくはその両方の並びである。
このような正規表現はそのような並びを含む任意の文字列にマッチする。
したがって、`foo'という正規表現は`foo'を含む任意の文字列
にマッチすることになる。
それにより、/foo/
というパターンはレコードのどこであっても
`foo'という三文字を含む入力レコードにマッチすることになる
のである。他の種類の正規表現は、あなたがより複雑な文字列の集合を指定でき
るようにするものである。
Initially, the examples will be simple. As we explain more about how regular expressions work, we will present more complicated examples.
正規表現はスラッシュで囲まれたパターンとして使うことができる。そのような正 規表現は各レコードのテキスト全体に対してのテストが行われる。(通常は、成功 かどうかを決めるにはテキストの一部分だけ見れば良い)以下に挙げる例は`foo' を含むレコードの第二フィールドを出力する。
$ awk '/foo/ { print $2 }' BBS-list -| 555-1234 -| 555-6699 -| 555-6480 -| 555-2127
正規表現はまた、マッチング式として使うことができる。これらの式は
マッチングを試みる文字列を特定することを許し、カレント入力レコード
全体とマッチングすることをしないで済むようにできる。
`~'と `!~'という二つの演算子は正規表現の比較をおこなう。
これらの演算子を使った式はパターンとしてや使ったり、if
、
while
、for
、do
文中で使うこととができる。
exp ~ /regexp/
$ awk '$1 ~ /J/' inventory-shipped -| Jan 13 25 15 115 -| Jun 31 42 75 492 -| Jul 24 34 67 436 -| Jan 21 36 64 620こう書いても良い。
awk '{ if ($1 ~ /J/) print }' inventory-shipped
exp !~ /regexp/
$ awk '$1 !~ /J/' inventory-shipped -| Feb 15 32 24 226 -| Mar 15 24 34 228 -| Apr 31 52 63 420 -| May 16 34 29 208 ...
正規表現が/foo/
のようにスラッシュで囲まれて記述されたとき、
これを正規表現定数と呼ぶ。これは5.27
のような数値定数や
"foo"
のような文字列定数のようなものである。
一部のキャラクタは("foo"
)のような文字列定数やregexp constants (
code{/foo/})のような正規表現定数にそのままの形で含める事ができない。このよ
うなキャラクタは、キャラクタそのものではなくバックスラッシュ(`\'で始
まるエスケープシーケンス)を代わりに使うことで文字列などに含めること
ができる。
エスケープシーケンスの一つの用途は文字列定数に二重引用符を含めるためである。 生の二重引用符は文字列の終端になってしまうため、それが二重引用符のキャラク タそのものであることを明示するために`\"'を使わなければならない。例を 挙げよう。
$ awk 'BEGIN { print "He said \"hi!\" to her." }' -| He said "hi!" to her.
バックスラッシュキャラクタそれ自身は、そのまま含むことのできないキャラク
タである。文字列や正規表現中にバックスラッシュを一つ含めるには、`\\'
と記述する。したがって、`"' と `\'という二つのキャラクタからな
る文字列は"\"\\"
と記述しなければならない。
もう一つのバックスラッシュの用途は、タブとか改行のような非印字キャラクタ (unprintable characters)を表すためのものである。非印字キャラクタをそのま ま文字列定数や正規表現定数に含めてしまうと多分見づらくなってしまう。
以下にawk
で使われる全てのエスケープシーケンスを挙げる。
特に断りがない限り、これらのエスケープシーケンス全ては文字列定数と
正規表現定数の両方に適用される。
\\
\a
\b
\f
\n
\r
\t
\v
\nnn
\xhh...
awk
では許され
ていない)。
\/
awk
に残りの正規表現を処理し続けるのを指示するのにエスケープしてスラ
ッシュをパターンの一部にする必要がある。
\"
awk
に処理し続けるのを指示するのにエスケープしてスラッシュを文字
列の一部にする必要がある。
gawk
では、正規表現中で特別な意味を持つバックスラッシュで始まる二文字の
エスケープシーケンスが幾つか追加されている。
セクション gawk
でのみ使える正規表現演算子を参照.
文字列定数中で、上の一覧にないキャラクタの前にバックスラッシュを 置いた場合にはなにがおこるのだろうか? POSIXでは故意にそれが定義されていない。ここでは二つの選択肢がある。
awk
とgawk
の
両方が行っている。たとえば、"a\qc"
は "aqc"
と等しい。
awk
処理系では
これを行っている。このような処理系では、"a\qc"
は
"a\\qc"
としたときと同じである。
正規表現中に上の一覧にないキャラクタや、
セクション gawk
でのみ使える正規表現演算子を参照
にリストアップされていないキャラクタの前にバックスラッシュがある場合、
それが通常は正規表現演算子として扱われるものであったとしても、
そのキャラクタをその文字そのものとみなす。
一例を挙げると、/a\+b/
は`a+b'という三文字にマッチする。
移植性を完全なものとするには、先に挙げたキャラクタ以外のキャラクタの 前にバックスラッシュを置かないようにすること。
もう一つ、興味深い疑問がある。八進や十六進のエスケープを使用して
正規表現のメタキャラクタを表すシーケンスを記述したとき、
(セクション 正規表現演算子を参照).
awk
はそのようなキャラクタを文字として見るのだろうか?
それとも正規表現の演算子と見るのだろうか?
このようなキャラクタは伝統的にその文字そのものとして扱われる(d.c.)。
しかし、POSIX標準ではこれはメタキャラクタであるかのように扱うよう指示さ
れており、gawk
の振る舞いもそうなっている。ただし互換モード
(セクション コマンドラインオプションを参照)では、gawk
は正規表現定数中で八進
や十六進のエスケープシーケンスによって表現されたキャラクタは文字そのもの
として扱われる。したがって、/a\52b/
は/a\*b/
と等価である。
まとめ
awk
がプログラムを
読み込んだときに行われる。
gawk
は正規表現定数、動的正規表現
(セクション 動的正規表現を使うを参照)の両方で、
セクション gawk
でのみ使える正規表現演算子を参照.
で述べられているような特殊な演算子を処理する。
以下に挙げるような正規表現演算子とかメタキャラクタと呼ばれる 正規表現の記述力を増加させるようなキャラクタを使って、 正規表現式を組み合わせることができる。
先にセクション エスケープシーケンスを参照で述べたエスケープシーケンスは 正規表現の中でも使うことができて、それらは先頭に`\'が置かれている。 正規表現の処理に先立って、 このようなエスケープシーケンスは解析されて、そのシーケンスの表す キャラクタに置きかえられる。
以下はメタキャラクタの一覧である。 エスケープシーケンスではなくこの一覧にないキャラクタはすべて、 それ自身を表すキャラクタである。
\
\$は`$'というキャラクタにマッチする。 @cindex regexp, anchors
^
^@chapterは文字列の先頭にある`@chapter'にマッチする。これは Texinfoファイルで章の先頭を見つけるのに使用できる。 `^'は文字列の先頭部分にのみマッチさせるのに使う 目印であるので、アンカー(anchor)として知られる。 `^'は 文字列中にある行(line embedded in a string)の 先頭にはマッチしないことに注意。次の例の条件式は真にはならない。
if ("line1\nLINE 2" ~ /^L/) ...
$
p$これは`p'で終わるレコードにマッチする。`$'もアンカーであり、 文字列中の行末にはマッチしない。例えば、
if ("line1\nLINE 2" ~ /1$/) ...この条件は真にはならない。
.
.Pこれは`P'が後に続く任意の一文字にマッチする。連接を使うことによっ て、`U.A'のような`U'で始まり`A'で終わる任意の三文字の並 びにマッチするような正規表現を作ることができる。 厳密なPOSIXモード(セクション コマンドラインオプションを参照)では、 `.'はすべてのビットが0であるキャラクタ、NULにはマッチしない。 しかしながら、NULは単にもう一つのキャラクタである。 他のバージョンの
awk
では、NULキャラクタには
マッチさせることができない。
[...]
[MVX]これは文字列中にある`M', `V', `X'のいずれかの キャラクタとマッチする。 キャラクタの範囲を、範囲の始点と終点の間にハイフンを置き、 全体をブラケットに囲まれた中に置くことで 指示することができる。
[0-9]これは任意の数字にマッチする。 "すべてのアルファベットと数字"を表す常識的な考えである
[A-Za-z0-9]
のように複数の範囲を使うこともできる。
`\', `]', `-', `^' といったキャラクタを
キャラクタリストの中に含めるには、そのキャラクタの前に`\'を
つける。例えば、
[d\]]これは`d'か`]'にマッチする。 このキャラクタリスト中の`\'の扱いは、他の
awk
処理系と共通であ
り、これはPOSIXの定めるところでもある。awk
における正規表現はPOSIX
で規定されている拡張正規表現(Extended Regular Expressions, EREs)のスーパ
ーセットである。POSIXの拡張正規表現は伝統的なegrep
ユーティリティ
が受け付ける正規表現に基づいている。
キャラクタクラスはPOSIX標準により導入された新しい機能である。
キャラクタクラスは(特定の属性を持つ)
キャラクタのリストを表す特殊な記述であるが、表現されるキャラクタ
自身が国ごと、あるいはキャラクタセットごとに異なるものとなる可能性がある。
例を挙げると、USAでいうところのアルファベットとフランスのアルファベット
では異なるものとなるということである。
キャラクタクラスは、キャラクタリストを囲むブラケットの内側
でのみ有効である。キャラクタクラスは`[:'、クラスを区別するキーワード、
`:]'から構成される。以下はPOSIX標準で定義されているキャラクタクラス
の一覧である。
[:alnum:]
[:alpha:]
[:blank:]
[:cntrl:]
[:digit:]
[:graph:]
[:lower:]
[:print:]
[:punct:]
[:space:]
[:upper:]
[:xdigit:]
/[A-Za-z0-9]/
と書かなければならなかった。もし、使用する環境のキャ
ラクタセット が、元のものとは違うものであった場合、これはマッチしなくなっ
てしまうだろう。POSIXのキャラクタセットを使うことで/[[:alnum:]]/
の
ように記述 ができ、かつこれはすべてのキャラクタセットにおいて、アル
ファベッ トか数字にマッチするのである。
キャラクタリスト中に更に二つの特殊なシーケンスを置くことができる。
これらは、一文字以上の長さを持つ一つのシンボルを持つことのできるような
非ASCIIキャラクタセットに適用して、通常のキャラクタと同じように
照合やソートなどを行うようにするためのものである(例えばフランス語
では"e"はアクサングラーブの付いた"`e" と等価である)。
[[.ch.]]
はこの照合要素にマッチする正規表現であるが、
[ch]
は`c'か`h'にマッチする正規表現である。
[[=e=]]
は
`e' か `'e' か ``e'にマッチする正規表現である。
gawk
が使っている正規表現マッチングライブラリ
の関数は、POSIXキャラクタクラスのみを認識し、照合シンボルや等価クラス
を認識しない。
[^ ...]
[^0-9]これは数字でない任意のキャラクタにマッチする。
|
^P|[0-9]この例は`^P'か`[0-9]'で始まる文字列にマッチする。 これは`P'か数値で始まる文字列にマッチするということである。 選択は演算子の左右のそれぞれで、可能な限り大きな正規表現に適用される。 言い換えるなら、`|'は正規表現演算子の中で最低の優先順位である ということである。
(...)
*
ph*ここで`*'はその前にある`h'に適用され、一文字の`p'に続いて任意 の数の`h'がある文字列にマッチする。これは`p'だけで`h'が一個も ないようなパターンにもマッチする。 `*'のくり返しは、可能なかぎり最も小さな式が採用される。 (もしより大きな 式を繰り返したいのならば括弧を使えばよい) そしてその式は可能な限り大きなくり 返しを見つけだす。例えば、
awk '/\(c[ad][ad]*r x\)/ { print }' sample`sample'中のレコードのうち、`(car x)', `(cdr x)', `(cadr x)'のような文字列を含むものを全て出力する。カッコの前にバッ クスラッシュをつけてエスケープしていることに注意。
+
wh+yこの例でいうと、`wh*y'ではすべてマッチしていた`why' や `whhy'、`wy'のうち、前者二者にはマッチするが、最後のものにはマ ッチしないということである。次の例は、`*'で最後に挙げた例を書き直し たものである。
awk '/\(c[ad]+r x\)/ { print }' sample
?
fe?dこれは`fed'や`fd'にマッチするが、それ以外にはマッチしない。
{n}
{n,}
{n,m}
wh{3}y
wh{3,5}y
wh{2,}y
awk
では使えなかった。
POSIX標準の一部として、
awk
とegrep
とほかのユーティリティとの
一貫性のため、これが追加された。
しかしながら、古いプログラムでは`{'や`}'を正規表現定数の中で
使っているかもしれないので、gawk
はデフォルトでは正規表現中の
interval expressionsはマッチしない。`--posix'オプションか
`--re-interval'オプション(セクション コマンドラインオプションを参照)
が指定されると、interval expressionsを正規表現として使うことができるよう
になる。
正規表現では、`*', `+', `?'といった演算子は`{' や `}'と同じ優先順位を持ち、その上に連接があり、下には`|'がある。 算術式と同じように、カッコはそれで括った演算子の優先順位を変更することが できる。
gawk
が互換モード(セクション コマンドラインオプションを参照)で
動作している場合、キャラクタクラスとinterval expressionは
正規表現中で使うことができない。
The next
次の
section
セクションでは
GNU特有の正規表現演算子について述べる。
さらに、gawk
が正規表現キャラクタを解釈する
方法に影響するコマンドラインオプションについて
詳しく説明する。
gawk
でのみ使える正規表現演算子
GNUのソフトウェアは、幾つかの正規表現演算子を追加した正規表現を提供して
いる。このセクションで説明されるこれらの演算子はgawk
特有の
ものであり、他のawk
の処理系では使用できないものである。
追加されている演算子のほとんどは、単語のマッチングに関係したものである。 ここで、単語とは一文字以上の、文字、数字、アンダースコア(`_') の並びである。
\w
[[:alnum:]_]
の簡潔な表現とみなして良い。
\W
[^[:alnum:]_]
の簡潔な表現とみなして良い。
\<
/\<away/
は`away'にマッチするが、
`stowaway'にはマッチしない。
\>
/stow\>/
は`stow'にマッチするが、
`stowaway'にはマッチしない。
\y
\B
/\Brat\B/
は`crate'にマッチする。しかし、`dirty rat'
にはマッチしない。`B'は簡単にいうと`\y'の反対語である。
さらに二つのバッファ上で動作する演算子がある。Emacsでは、バッファ
はEmacsのバッファのことである。他のプログラムでは、gawk
が使用し
ている正規表現ライブラリは文字列全体をバッファにあたるものとしている。
awk
においては、`^'や`$'は常に文字列の先頭や末尾に働く
ので、これらの演算子は新しい機能をもたらすものではない。これらは他の
GNUソフトウェアとの互換性のために提供されている。
\`
\'
他のGNUソフトウェアでは、語の区切り演算子は`\b'である。
しかしながら、これはawk
言語では
バックスペースがすでに`\b'と定義されているので衝突してしまう。
そこで、gawk
では違う演算子を使っているのである。
ある別のやり方でGNU特有の演算子では二つのバックスラッシュを 要求するというものがあったが、これは混乱を招くと考えられたので 現在の方法の、`\y'をGNUの`\b'として扱うほうが ちょっとばかしましである。
gawk
が正規表現中のキャラクタをどのように解釈するかを
制御するためのコマンドラインオプション(セクション コマンドラインオプションを参照)
が幾つかある。
gawk
は
above.
前述した
POSIXの正規表現と、GNUの正規表現演算子のすべての機能を
提供する。ただし、interval expressionはサポートしない。
--posix
--traditional
awk
の正規表現がマッチする。GNUの演算子は特別でなく、
interval expressionとPOSIXのキャラクタクラス([[:alnum:]]
など)も使
用不可である。八進や十六進のエスケープシーケンスで表わされたキャラクタは、
それが正規表現のメタキャラクタであってもキャラクタそのものとして扱われる。
--re-interval
大小文字は通常正規表現の中では通常のキャラクタマッチのとき(メタキャラクタを除 く)でも、キャラクタセットの内側のときでも区別される。したがって、正規表現中 の`w'は小文字の`w'とだけマッチして大文字の`W'とはマッチしない。
大小文字を無視してマッチを行う最も単純な方法はキャラクタセットを使って `[Ww]'の様にすることである。しかし、これは正規表現を読みにくいものとし てしまう。他に取るべき手段としては二つある。
プログラム中の任意の点で大小文字に関係なくマッチングを行うための手段の一つは、
データをtolower
か toupper
という組み込みの文字列処理関数(まだ
これらの関数の説明はされていない)を使用して、大文字、小文字どちらかに揃えて
しまうというものである。(tolower
及びtoupper
の詳しい説明は、
セクション Built-in Functions for String Manipulationを参照).
たとえば
tolower($1) ~ /foo/ { ... }
この例ではマッチングを行なう前に、最初のフィールドを小文字に変換している。
もう一つの、gawk
に特有な方法は、変数
IGNORECASE
(セクション 組み込み変数を参照)
に非0の値をセットすることであり、これによってすべての
正規表現演算、文字列演算で大小文字が無視される。
IGNROECSE
は他の大部分の変数と同様に
ゼロで初期化されているので、デフォルトでは大小文字は
区別される。
x = "aB" if (x ~ /ab/) ... # このテストは失敗する IGNORECASE = 1 if (x ~ /ab/) ... # 今度は成功する
一般的にはIGNORECASE
を特定のルールをcase-insensitive(大小文字を区
別しない)にしたり、case-sensitive(大小文字を区別する)ようにするために使
うことはできない。それは、特定のルールのパターンのためにIGNORECASE
をセットする方法がないからである。
これを行うには、キャラクタクラスを使うか、tolower
を使うか
しなければならない。しかし、IGNORECASE
を
すべてのルールに対して、動的にオンにしたりオフにしたりすることは
できる。
IGNORECASE
はコマンドラインででも、BEGIN
ルール中で
でもセットすることができる
(セクション Other Command Line Argumentsを参照と
セクション Startup and Cleanup Actionsを参照)。
コマンドラインでIGNORECASE
の設定をすることによって、
プログラムを編集することなしに
大小文字を区別しないものにすることができる。
バージョン3.0以前のgawk
では、IGNORECASE
の
値は正規表現演算にのみ影響していて、`=='や`!='などを
使った文字列比較などには影響しなかった。
バージョン3.0からは、IGNORECASE
の値は
正規表現演算と文字列比較の両方に影響するようになった。
gawk
のバージョン3.0から、大小文字の間の等価性(equivalence)は
ISO-8859-1(ISO Latin-1)キャラクタセットに基づくものとなった。
このキャラクタセットは伝統的な128 ASCIIキャラクタセットのスーパーセット
であり、ヨーロッパ諸国の言語で使用するのに適当なキャラクをいくつか
提供するものである。
IGNORECASE
の値はgawk
が互換モード
(セクション コマンドラインオプションを参照)のときには何の効果も持たない。
互換モードでは大小文字の区別は常に行われる。
echo aaaabcd | awk '{ sub(/a+/, "<A>"); print }'
これはsub
関数(まだこれは説明していない。
セクション Built-in Functions for String Manipulationを参照)
を使って入力レコードを変更する例である。ここで、/a+/
という
正規表現は"一つ以上の`a'"を表しており、それにマッチする
テキストを`<A>'に置き換えるというものである。
入力には四つの`a'がある。出力はどうなるだろうか?言い換えれば、"一
つ以上"とは幾つなのか、awk
は`a'二つにマッチさせるのか、それ
とも三つなのか、あるいは四つ全てにマッチさせるのかということである。
答えを言うと、awk
(とPOSIX)の正規表現は、常にマッチするもののうち
最も左にあり、最も長いキャラクタの並びにマッチする
(最左最長一致)。したがって、
この例では四つの連続した`a'が(一つの)`<A>'に置き換えられる。
$ echo aaaabcd | awk '{ sub(/a+/, "<A>"); print }' -| <A>bcd
単純にマッチする/しないをテストするときには今述べた事は
さして重要ではない。しかし、正規表現に基づいたフィールドやレコードの
分割、そしてmatch
, sub
, gsub
,gensub
といった関数を使用してテキストのマッチングや置き換えをする場合には
非常に重要である。
この原則を理解する事は正規表現に基づいたレコード/フィールドの
分割のためにも重要である(セクション 入力をレコードへと分割をするやりかたを参照,
と セクション フィールドの分割方法の指定を参照)。
`~'演算子や`!~'演算子の右辺に置くものは正規表現定数(スラッシュの間に置 かれた文字列)である必要はなく、任意の式を置くことができる。そのような式 は評価され、必要があれば文字列に変換される。評価され変換が行なわれた 結果は正規表現として扱われる。 このように演算が行われる正規表現は動的正規表現(dynamic regular expression)と呼ばれる。例を挙げよう。
BEGIN { identifier_regexp = "[A-Za-z_][A-Za-z_0-9]+" } $0 ~ identifier_regexp { print }
これはidentifier_regexp
にawk
での変数名を表す正規表現をセ
ットし、入力レコード中にこの正規表現にマッチするものがあるかをテストす
るものである。
警告:`~'演算子や`!~'演算子を使った場合、スラッシュで
囲まれた正規表現定数と二重引用符で囲まれた文字列定数とでは違いがある。文
字列定数を使おうとする場合、文字列が二度走査されることを理解する
必要がある。一度目はawk
がプログラムを読み込んだとき、二度目は演算
子の左側に置かれた文字列と右側のパターンとのマッチングを行うときである。
これは式として評価される(先の例で言う identifier_regexp
のように)
任意の文字列において真であり、文字列定数そのものにはあてはまらない。
文字列が二度走査されることによる違いとはなんだろうか? その答えは、エスケープシーケンス、特にバックスラシュの振る舞いにある。 正規表現を通して文字列中にあるバックスラッシュを得るには、 二つのバックスラッシュを置かなければならない。
例えば、/\*/
は*
という文字にマッチする正規表現定数であり、
これはバックスラッシュ一つを必要とする。同じものを文字列を使って行う
には、"\\*"
としなければならないだろう。最初のバックスラッシュは
二個めのバックスラッシュをエスケープするものであり、これによってこの文
字列は`\'と`*'という二つのキャラクタからなる文字列となる。
正規表現を記述するのに正規表現定数と文字列定数の両方が使えるとき、どち らを使ったほうが良いのだろうか? その答えは"正規表現定数"である。以下 にその理由を挙げる。
awk
はあなたが
記述した正規表現に注意することができ、そしてそれをより効果的にパターンマ
ッチングするための内部的な表現で格納する事ができるようになる。文字列定数
を使った場合、awk
はまず初めに文字列を内部表現に変換しなければなら
ず、その後でパターンマッチングを行う。