Perlのエラーメッセージのコーナー。
Perlのエラーメッセージが表示される最小のプログラムを書いて、どういうときにどう怒られるかを研究する。
今回のお題は
Perlのエラーメッセージが表示される最小のプログラムを書いて、どういうときにどう怒られるかを研究する。
今回のお題は
?+* follows nothing in regexpである。
これはSVOの第3文型の英文である。
S(主語)?+*
V(動詞)follows
O(目的語)nothing
M(修飾語)in regexp
訳すと「正規表現で、?、+、*が、何の後ろにも来ていない」というエラーである。
regexpとはregular expressionの略で、正規表現と訳される。
これ、一般化式とかそういう訳し方の方がいいんじゃないだろうか。
regular expressionの略としては、regexp(レグエクスプ)という言い方と、regex(レグエックス)という言い方がある。
正規表現だけでまるまる一冊書かれた本である『詳説 正規表現』いわゆるフクロウ本では「regexが一般的であるが、regexpという変な人もたまにいる」と言い切っていたような気がする。
でも、フクロウ本はかなりPerl推しの本だが、Perlのエラーメッセージにはregexpと出てくるのだ。
ぼくは前いた会社の先輩にEmacsを無理やり仕込まれた。
Emacsは最初のうちめちゃくちゃ難しくて、泣きながら覚えたが、Emacsの関数もregexpと言うのが出てくる。
だからregexpがそこまで特殊な言い方ではないだろう。
正規表現とは何かというと、可変文字列を検索するときに使うものである。
むやみに嫌っている人がいて、前の会社にも、ずいぶんコンピューター好きを自認している人が「自分は正規表現だけは苦手だ」と言い張っていた。
ちょっとだけでも覚えると超便利なので、今日このブログを読んでる人は、この機会にちょっとだけでも覚えて下さい。
パソコンには検索置換が付きものである。
大島優子
前田敦子
篠田麻里子
板野友美
渡辺麻友
高橋みなみ
小嶋陽菜
浅田真央
益子直美
本田美奈子.
という10人の人間の中で、漢字の「田」を使っている人を探したい。
この場合は単純に「田」という固定の漢字を検索すれば良い。
//がマッチ演算子で、正規表現が文字に一致(match)すると真を、しないと偽を返す。
「田」が、この場合パターンという。
パターンを書くための文法(規則)が正規表現である。
このへん用語が錯綜していて面倒だ。
上のプログラムでは、正規表現を使って「田」というパターンを作り、そのパターンをマッチ演算子に入れて/田/という式を書いた。
この式が真を返せば行を印字する。
では実行してみよう。
このとき、固定の文字列ではなく、可変の文字列も探せると便利だ。
名前に子がつく名前の人を探したい。
単純に「子」という文字を使うと、「益子直美」も引っ掛かってしまう。
(わざとらしい例だなー)
この場合正規表現を使って「子$」という正規表現を書く。
これは「子$」(子の後ろにドル記号)という変わった名前を抜き出したいわけではなく、ここで$は行の末尾を示す。
このように特殊な意味を持つ字をメタ文字と言う。
「本田美奈子.」は「子が付く名前」なのだろうか。
これは難しい問題なので、あとで検討する。
さて、.(ドット)という文字も正規表現ではメタ文字であって、任意の一文字を示す。
いま、田という字と子という字の間に2文字入っている人を探す。
「田..子」でいいような気がする。
このように.は任意の1文字というメタ文字である。
今、本田美奈子.さんだけを抜き出そうと思って、ドット1文字のパターンを書いたとする。
どんなに変わった名前が流行っても、さすがに「」ちゃん(読み方はキョムちゃん?ヌルちゃん?からもじちゃん?)という子はいないだろう。
たいていのシステムが対応していないと思われる。
さて、本当にドットがつく名前を検索するにはどうすればいいだろう。
この場合.の前に\を付ける。
この\は半角のバックスラッシュ(逆斜線。reverse solidas)という字であるが、文字コード系やフォントによっては半角の円記号¥にも見えるというやっかいな字だ。
文字コード的には0x5Cである。
さっきのプログラムをこのように書き換える。
/./が/\./になっただけ。
では実行。
パターンの中で.の前に\を書くことによって、.のメタ性が打ち消され、そのまま.を検索するパターンになったのである。
これを「ドットをバックスラッシュでエスケープする」と言う。
さて、$は行末という位置を指定する正規表現であった。
ドルマークそのものを検索するときはやはり\でエスケープして\$と書く。
.は任意の1文字を示す正規表現であった。
他にパターンを繰り返す正規表現というのがある。
例えば5文字の名前を抜きたい。
.を5回繰り返せばいい。
このようなときは「.....」と書いてもいいが、「.{5}」と書いてもいい。
ここで{5}は.を5回繰り返すという意味だ。
これは、6文字の名前には5文字の文字列が入っているから当然の結果である。
もし5文字に限る、とするなら、先頭を示す位置指定のメタ文字^と、末尾を示す$の間にパターンを挟んで
さて、さきほどの「田と子の間に2文字入る」を改造して、「田と子の間に1文字以上入る」としよう。
こうなる。
ただしこれだと「子」というただ1文字の名前を持つ「前田子」とか「大島子」という名前に対応できない。
そう考えれば「田.*子」と書く。
ここで*はゼロ回以上繰り返すという意味になる。
.*は任意の文字をゼロ回以上繰り返すので、どんな文字列にも(空文字列にも)マッチするパターンになる。
*、+、{n}、?のように、パターンを繰り返す回数を指定する正規表現を量指定子と言う。
りょうしていしと読む。
なんでコンピューター関係ってこういう難しい言葉を覚えさせられるんだろうね。
英語だとクォンティファイヤーだが、英語は基本固い表現が多いのでそれほど気にならない。
りょうしていしと書いて変換すると漁師停止となりがちなので注意が必要だ。
紹介しなかったが、?はゼロ回または1回のことだ。
田と子の間に文字が1個または2個であれば「田..?子」と書ける。これで「田.子」または「田..子」ということになる。
「山田さくら子」のような人はこれでは引っ掛からない。
さて、量指定子はすべてメタ文字である。
いま、前田敦子、大島優子、篠田麻里子というAKB卒業メンバー3人が改名して、それぞれ前田敦子+、大島優子+、篠田麻里子+となったとする。
「まえだあつこプラス」などと読む。
この3人を検索するために「+」というパターンを書いた。
このブログ記事の最初に引用したメッセージは、『プログラミングPerl(Programming Perl)第3版』(いわゆるラクダ本)から引用したのだが、そのときは「?+* follows nothing in regexp」だった。
まず、「?*+」という即物的な表現だったのが「Quantifire(クォンティファイヤー)」という一般的な名詞になっている。
それはいいんだけど、正規表現の略称がregexpではなくてregexになっている!
最初の方で「フクロウ本にはregexって書いてあるけど俺はregexpと言う、PerlとEmacsがそうだから」などと言っていたのが台無しである。
台無しなりに、もうだいぶ時間を掛けてここまで書いてしまったからあえて直さない。
まあここ数年の間にそういう用語の変遷があったということだろうか。
ということで、このメッセージの意味は、.や+や?は量指定のメタ文字であるから、たとえば+のついた人名を探す、という正規表現が書きたければ、
ここまで書いて思ったが(このブログ良くこのフレーズ出てくるな!)、{n}も量指定子だがこれをエスケープしないで使ったらどうなるだろう。
結果的にはまったく同じで、
これを入れないとまずいから、やはり「?+* follows...」というメッセージは間違っていたのだ。
S(主語)?+*
V(動詞)follows
O(目的語)nothing
M(修飾語)in regexp
訳すと「正規表現で、?、+、*が、何の後ろにも来ていない」というエラーである。
regexpとはregular expressionの略で、正規表現と訳される。
これ、一般化式とかそういう訳し方の方がいいんじゃないだろうか。
regular expressionの略としては、regexp(レグエクスプ)という言い方と、regex(レグエックス)という言い方がある。
正規表現だけでまるまる一冊書かれた本である『詳説 正規表現』いわゆるフクロウ本では「regexが一般的であるが、regexpという変な人もたまにいる」と言い切っていたような気がする。
でも、フクロウ本はかなりPerl推しの本だが、Perlのエラーメッセージにはregexpと出てくるのだ。
ぼくは前いた会社の先輩にEmacsを無理やり仕込まれた。
Emacsは最初のうちめちゃくちゃ難しくて、泣きながら覚えたが、Emacsの関数もregexpと言うのが出てくる。
だからregexpがそこまで特殊な言い方ではないだろう。
正規表現とは何かというと、可変文字列を検索するときに使うものである。
むやみに嫌っている人がいて、前の会社にも、ずいぶんコンピューター好きを自認している人が「自分は正規表現だけは苦手だ」と言い張っていた。
ちょっとだけでも覚えると超便利なので、今日このブログを読んでる人は、この機会にちょっとだけでも覚えて下さい。
パソコンには検索置換が付きものである。
大島優子
前田敦子
篠田麻里子
板野友美
渡辺麻友
高橋みなみ
小嶋陽菜
浅田真央
益子直美
本田美奈子.
という10人の人間の中で、漢字の「田」を使っている人を探したい。
この場合は単純に「田」という固定の漢字を検索すれば良い。
#! /usr/local/bin/perlここで/田/というところが正規表現を使っている。
# regTest.pl -- 正規表現のテスト
use utf8;
binmode STDOUT, ":encoding(utf8)";
while(<DATA>) {
print if /田/;
}
__DATA__
大島優子
前田敦子
篠田麻里子
板野友美
渡辺麻友
高橋みなみ
小嶋陽菜
浅田真央
益子直美
本田美奈子.
//がマッチ演算子で、正規表現が文字に一致(match)すると真を、しないと偽を返す。
「田」が、この場合パターンという。
パターンを書くための文法(規則)が正規表現である。
このへん用語が錯綜していて面倒だ。
上のプログラムでは、正規表現を使って「田」というパターンを作り、そのパターンをマッチ演算子に入れて/田/という式を書いた。
この式が真を返せば行を印字する。
では実行してみよう。
$ regTest.pl普通に「田」を含む名前が検索できた。
前田敦子
篠田麻里子
浅田真央
本田美奈子.
このとき、固定の文字列ではなく、可変の文字列も探せると便利だ。
名前に子がつく名前の人を探したい。
単純に「子」という文字を使うと、「益子直美」も引っ掛かってしまう。
(わざとらしい例だなー)
この場合正規表現を使って「子$」という正規表現を書く。
これは「子$」(子の後ろにドル記号)という変わった名前を抜き出したいわけではなく、ここで$は行の末尾を示す。
このように特殊な意味を持つ字をメタ文字と言う。
#! /usr/local/bin/perl実行。
# regTest.pl -- 正規表現のテスト
use utf8;
binmode STDOUT, ":encoding(utf8)";
while(<DATA>) {
print if /子$/;
}
__DATA__
大島優子
前田敦子
篠田麻里子
板野友美
渡辺麻友
高橋みなみ
小嶋陽菜
浅田真央
益子直美
本田美奈子.
$ regTest.pl「本田美奈子.」(ほんだみなこ・ドット)さんも弾かれてしまったが、これは予定の行動だ。
大島優子
前田敦子
篠田麻里子
「本田美奈子.」は「子が付く名前」なのだろうか。
これは難しい問題なので、あとで検討する。
さて、.(ドット)という文字も正規表現ではメタ文字であって、任意の一文字を示す。
いま、田という字と子という字の間に2文字入っている人を探す。
「田..子」でいいような気がする。
#! /usr/local/bin/perl実行。
# regTest.pl -- 正規表現のテスト
use utf8;
binmode STDOUT, ":encoding(utf8)";
while(<DATA>) {
print if /田..子/;
}
__DATA__
大島優子
前田敦子
篠田麻里子
板野友美
渡辺麻友
高橋みなみ
小嶋陽菜
浅田真央
益子直美
本田美奈子.
$ regTest.pl見事に「なんとか田かんとか子(かんとかの部分が2文字)」が抜けた。
篠田麻里子
本田美奈子.
このように.は任意の1文字というメタ文字である。
今、本田美奈子.さんだけを抜き出そうと思って、ドット1文字のパターンを書いたとする。
#! /usr/local/bin/perl実行すると、予定外の結果になる。
# regTest.pl -- 正規表現のテスト
use utf8;
binmode STDOUT, ":encoding(utf8)";
while(<DATA>) {
print if /./;
}
__DATA__
大島優子
前田敦子
篠田麻里子
板野友美
渡辺麻友
高橋みなみ
小嶋陽菜
浅田真央
益子直美
本田美奈子.
$ regTest.pl任意の1文字だから、1文字以上の文字列である名前にはすべてマッチしてしまうのだ。
大島優子
前田敦子
篠田麻里子
板野友美
渡辺麻友
高橋みなみ
小嶋陽菜
浅田真央
益子直美
本田美奈子.
どんなに変わった名前が流行っても、さすがに「」ちゃん(読み方はキョムちゃん?ヌルちゃん?からもじちゃん?)という子はいないだろう。
たいていのシステムが対応していないと思われる。
さて、本当にドットがつく名前を検索するにはどうすればいいだろう。
この場合.の前に\を付ける。
この\は半角のバックスラッシュ(逆斜線。reverse solidas)という字であるが、文字コード系やフォントによっては半角の円記号¥にも見えるというやっかいな字だ。
文字コード的には0x5Cである。
さっきのプログラムをこのように書き換える。
#! /usr/local/bin/perl超・微妙である。
# regTest.pl -- 正規表現のテスト
use utf8;
binmode STDOUT, ":encoding(utf8)";
while(<DATA>) {
print if /\./;
}
__DATA__
大島優子
前田敦子
篠田麻里子
板野友美
渡辺麻友
高橋みなみ
小嶋陽菜
浅田真央
益子直美
本田美奈子.
/./が/\./になっただけ。
では実行。
$ regTest.pl見事に本田美奈子だけが抜けた。
本田美奈子.
パターンの中で.の前に\を書くことによって、.のメタ性が打ち消され、そのまま.を検索するパターンになったのである。
これを「ドットをバックスラッシュでエスケープする」と言う。
さて、$は行末という位置を指定する正規表現であった。
ドルマークそのものを検索するときはやはり\でエスケープして\$と書く。
.は任意の1文字を示す正規表現であった。
他にパターンを繰り返す正規表現というのがある。
例えば5文字の名前を抜きたい。
.を5回繰り返せばいい。
このようなときは「.....」と書いてもいいが、「.{5}」と書いてもいい。
ここで{5}は.を5回繰り返すという意味だ。
#! /usr/local/bin/perl実行してみる。
# regTest.pl -- 正規表現のテスト
use utf8;
binmode STDOUT, ":encoding(utf8)";
while(<DATA>) {
print if /.{5}/;
}
__DATA__
大島優子
前田敦子
篠田麻里子
板野友美
渡辺麻友
高橋みなみ
小嶋陽菜
浅田真央
益子直美
本田美奈子.
$ regTest.pl5文字の篠田麻里子、高橋みなみの他に、6文字の本田美奈子.も引っ掛かった。
篠田麻里子
高橋みなみ
本田美奈子.
これは、6文字の名前には5文字の文字列が入っているから当然の結果である。
もし5文字に限る、とするなら、先頭を示す位置指定のメタ文字^と、末尾を示す$の間にパターンを挟んで
#! /usr/local/bin/perlとかになる。
# regTest.pl -- 正規表現のテスト
use utf8;
binmode STDOUT, ":encoding(utf8)";
while(<DATA>) {
print if /^.{5}$/;
}
__DATA__
大島優子
前田敦子
篠田麻里子
板野友美
渡辺麻友
高橋みなみ
小嶋陽菜
浅田真央
益子直美
本田美奈子.
さて、さきほどの「田と子の間に2文字入る」を改造して、「田と子の間に1文字以上入る」としよう。
こうなる。
#! /usr/local/bin/perlここで「.+」は.を1回以上繰り返すという意味になる。
# regTest.pl -- 正規表現のテスト
use utf8;
binmode STDOUT, ":encoding(utf8)";
while(<DATA>) {
print if /田.+子/;
}
__DATA__
大島優子
前田敦子
篠田麻里子
板野友美
渡辺麻友
高橋みなみ
小嶋陽菜
浅田真央
益子直美
本田美奈子.
ただしこれだと「子」というただ1文字の名前を持つ「前田子」とか「大島子」という名前に対応できない。
そう考えれば「田.*子」と書く。
ここで*はゼロ回以上繰り返すという意味になる。
.*は任意の文字をゼロ回以上繰り返すので、どんな文字列にも(空文字列にも)マッチするパターンになる。
*、+、{n}、?のように、パターンを繰り返す回数を指定する正規表現を量指定子と言う。
りょうしていしと読む。
なんでコンピューター関係ってこういう難しい言葉を覚えさせられるんだろうね。
英語だとクォンティファイヤーだが、英語は基本固い表現が多いのでそれほど気にならない。
りょうしていしと書いて変換すると漁師停止となりがちなので注意が必要だ。
紹介しなかったが、?はゼロ回または1回のことだ。
田と子の間に文字が1個または2個であれば「田..?子」と書ける。これで「田.子」または「田..子」ということになる。
「山田さくら子」のような人はこれでは引っ掛からない。
さて、量指定子はすべてメタ文字である。
いま、前田敦子、大島優子、篠田麻里子というAKB卒業メンバー3人が改名して、それぞれ前田敦子+、大島優子+、篠田麻里子+となったとする。
「まえだあつこプラス」などと読む。
この3人を検索するために「+」というパターンを書いた。
#! /usr/local/bin/perl当然ダメである。
# regTest.pl -- 正規表現のテスト
use utf8;
binmode STDOUT, ":encoding(utf8)";
while(<DATA>) {
print if /+/;
}
__DATA__
大島優子+
前田敦子+
篠田麻里子+
板野友美
渡辺麻友
高橋みなみ
小嶋陽菜
浅田真央
益子直美
本田美奈子.
$ regTest.plこのメッセージを表示するためにいままで頑張って文章を書いていたのだが、ぼくの使っているPerlのバージョン5.18.2ではメッセージが「Quantifier follows nothing in regex」だ。
Quantifier follows nothing in regex; marked by <-- HERE in m/+ <-- HERE / at ./regTest.pl line 9.
このブログ記事の最初に引用したメッセージは、『プログラミングPerl(Programming Perl)第3版』(いわゆるラクダ本)から引用したのだが、そのときは「?+* follows nothing in regexp」だった。
まず、「?*+」という即物的な表現だったのが「Quantifire(クォンティファイヤー)」という一般的な名詞になっている。
それはいいんだけど、正規表現の略称がregexpではなくてregexになっている!
最初の方で「フクロウ本にはregexって書いてあるけど俺はregexpと言う、PerlとEmacsがそうだから」などと言っていたのが台無しである。
台無しなりに、もうだいぶ時間を掛けてここまで書いてしまったからあえて直さない。
まあここ数年の間にそういう用語の変遷があったということだろうか。
ということで、このメッセージの意味は、.や+や?は量指定のメタ文字であるから、たとえば+のついた人名を探す、という正規表現が書きたければ、
print if /\+/;のように\でエスケープして書け、という意味である。
ここまで書いて思ったが(このブログ良くこのフレーズ出てくるな!)、{n}も量指定子だがこれをエスケープしないで使ったらどうなるだろう。
結果的にはまったく同じで、
[perl]$ regTest.plのようになっている。<-- HEREの前後の文字列が変化していることに注意して欲しい。
Quantifier follows nothing in regex; marked by <-- HERE in m/{ <-- HERE 2}/ at ./regTest.pl line 9.
これを入れないとまずいから、やはり「?+* follows...」というメッセージは間違っていたのだ。