先日、正規表現で初歩的なミスをした。
その備忘録。
例えばこんな文字列データがあるとする。
"-23.4428956,151.9065836 : Heron Island, Great Barrier Reef, Australia" "34.6873785, 135.5260056 : 大阪城" "27.1733248, 78.0421187 : Taj Mahal" "0.5738056,37.5750334 : Samburu, Kenya"
世界の名所の場所を示す地理のデータだ。
緯度, 経度 : 場所の説明
という形式になっている。
この各文字列を処理して、
緯度 "34.6873785" 経度 "135.5260056" 説明 "大阪城"
の部分を取り出したい。
それでこんなパターンを書いてみた。
^\s*([.+-0-9]+)\s*,\s*([.+-0-9]+)\s*:\s*(.*)$
皆さん良くご存じだと思うが、意味は以下の通り。
記号 | 説明 |
^ | 文字列の先頭から |
\s* |
0個以上(*)の空白(\s)が有って、 正確には、空白、タブ、フォーム フィードなどの任意の空白文字と一致します。[ \f\n\r\t\v] と同じ意味です。(MSDN) |
([.+-0-9]+) | [小数点、プラス、マイナス、0~9までの数字]の文字が一個以上(+)あり、それをキャプチャするためのカッコ()。これで緯度を取得する予定 |
\s* | 0個以上(*)の空白が有って、 |
, | カンマがあって |
\s* | 0個以上(*)の空白が有って、 |
([.+-0-9]+) | 同上 |
\s* | 0個以上(*)の空白が有って、 |
: | コロンが有って |
\s* | 0個以上(*)の空白が有って、 |
(.*) | 任意の文字(.)が0個以上(*)連続する |
$ | 文字列の末尾 |
ところが、全然マッチしないのだ。
結論としては、正規表現で範囲指定する時に使うマイナス記号 – の使い方をうっかり間違っていたのが原因だった。
当記事ではその辺りを備忘録として記事にした。
全然マッチしない
上記のパターンでやっても全然マッチしない。
JavaScriptでやっていたので具体的には下のコードのような記述となる。
var str = "34.6873785, 135.5260056 : 大阪城"; var rgx = new RegExp('^\\s*([.+-0-9]+)\\s*,\\s*([.+-0-9]+)\\s*:\\s*(.*)$', 'g'); var ary = rgx.exec(str);
実行してみると、全然マッチしない。
上手くマッチすると ary 変数の中にキャプチャされた部分文字列が配列として入る予定なのだが。
JavaScriptで文字列を使って正規表現を定義する場合には、例えば \s ではなくて \\s とする必要があるのも忘れていて、それが一層の混乱を引き起こした。
2時間くらい悪戦苦闘していて気が付いた。
[.+-0-9]
この部分がおかしいのだ。
角括弧(かくかっこ、ブラケット bracket)[]の中に文字を入れると以下のような意味になる。
[xyz] | 文字 ‘x’ ,’y’ ,’z’ のどれか一文字と一致。 |
[^xyz] |
除外する文字セットを指定する。 文字 ‘x’ ,’y’ ,’z’ 以外の一文字と一致。 |
[a-z] |
文字の範囲を指定。 [a-z] は英小文字 ‘a’ から ‘z’ の範囲にある任意の文字と一致。 |
[^a-z] |
否定の文字の範囲。 [^a-z] は英小文字以外の任意の文字と一致。 |
この範囲指定のマイナス記号の使い方に注意しなくてはならない。
範囲指定の例
0-9 数字の0~9
a-z 半角英小文字のa~z
A-Z 半角英大文字文字のA~Z
などは良く目にする。
全角の英数字および半角カタカナを検出するなら
([a-zA-Z0-9ヲ-゚])
こんな感じか。
あるいは、全ての漢字を取り出すという正規表現の例としてネットで良く見るのは
[一-龠々]
がある。このパターンでほぼ取り出せるようだが、完璧に取り出せる訳ではないようだ。Yahoo知恵袋に説明あり
つまりマイナス記号は、その前後の文字の文字コードを数字に置き換えて考えて、その数字範囲にある文字を全部含めるという意味になる。
そうすると、ワテが書いた
[.+-0-9]
の意味は
. 小数点のピリオド。\.と書いても良い。
+-0 +~0 までの範囲の文字となるが、これがおかしい。
0-9 0~9 までの範囲の文字。これは良い。
と分解できる。
そうすると、変な意味になるよな。
JavaScriptがこの表現をどう解釈するのかは未確認だ。兎に角、おかしなパターンだ。
ここが間違いだった。
マイナス記号の正しい使い方
マイナス記号を範囲指定の意味で使うのではなくて、マイナス記号そのものとして使うなら、エスケープすれば良い。
[.+\-0-9]
あるいは、先頭に書く。
[-+0-9.]
-を末尾に書いても良いのかな。
なお、角括弧の中にピリオド . を書いても任意の一文字とはならない。
あくまでピリオドの一文字を表す。もし気になるなら必要に応じてエスケープを入れて
[\-+0-9\.]
などとしても同じ意味になるし、分かり易い
それと否定の ^ も[]内で使う場合には、先頭に書くと否定の意味だが、2文字目以降に書くと単純にその文字 ^ 自身を表す。
数字にマッチする正規表現(不完全)
2時間の悪戦苦闘で角括弧の中のマイナス記号の勘違い問題が解決したのだが、それを使って
([-+0-9.]+)
として数字を取り出す事は出来た。
でもこの部分は改良の余地がある。
このパターンでも冒頭の例のデータでは上手く行き緯度や経度の数字が上手く取り出せる。
でもこのパターンだと数字ではない文字列
--++..00+1+..78
などにもマッチしてしまう。まあそりゃそうだな。
ちなみにこの人は
近藤マッチさん。
正規表現が得意かどうかは未確認だ。
数字にマッチする正規表現改良型
改良型を考えてみた。
([-+]?\d*.?\d+)
意味を解説すると以下の通り。
記号 | 説明 |
[-+]? |
マイナスかプラス記号のどちらか一文字。 それが0個あるいは1個ある(?)。 |
\d* | 数字が0個以上ある( [0-9]*と同じ) |
.? | ピリオド(小数点)が0個あるいは1個ある(?)。 |
\d+ | 数字が1個以上ある( [0-9]+と同じ) |
これなら、
123 +123 -123 1.23 +1.23 -1.23 0.123 -0.123 .123 +.123 -.123 .0 .000 000.000
などにマッチする。
これが正解なのかな?
なお上記の表現では、
- 数字の3桁区切りのカンマ有の場合 12,000,000
- 浮動小数点形式 1.23456789E+23
などには対応していない。
まとめ
当記事に関して、もしお気付きの点がありましたらコメントでお知らせください。
ワテの場合、正規表現は独学で覚えた。
昔、UnixやLinuxを勉強していた時に、シェルのコマンドの grap, sed, awk などを使ってファイルの処理をしたり、プログラムを書いたりした経験があるが、その時に正規表現を覚えた。
最初は grap や sed で正規表現を組み合わせてファイルやフォルダの検索などに活用した。
Windowsならワイルドカードの * を使って
*.txt
みたいな大雑把なフィルター処理しか出来ないが、sed や grap や find コマンドなどで正規表現を組み合わせると、非常に細かいフィルター検索が可能だ。
正規表現は一度覚えると、日常のちょっとした作業で時々使っていれば忘れる事は無い。
また、こんなパターンはどんな風に書けばいいのかな?などの疑問が生じたらネットの掲示板などで質問して、上級者の人に教えて貰うと良い。
世の中にはワテには思い付かないような洗練されたパターンをスラスタと書ける人がいる。
そうやって少しずつ自分で使えるパターンを増やして行くと、パソコン作業が捗る事は言うまでも無い。
入力する度に毎回ポストバックが発生するので煩わしい。
初期の作品なので改良の余地が多い。いずれ改良版を出したい。
正規表現の本を読む
こういうので繰り返し練習するのが正規表現マスターの近道かも。
ちなみに非正規表現と言う言葉は聞いた事が無い。
非正規の人達が今回の衆議院選挙で
衆議院選挙 | 2016年 |
公示日 | 6月22日(水) |
期日前投票日 | 6月23日(木)~7月9日(土) |
投票日 | 7月10日(日) |
投票する事により自らの意志を示せば、政党の勢力分布が変わるかもしれない。
まさに非正規の人々の意思表現。
さあ、どうなるかな。
コメント