Perl: 2012年8月アーカイブ

あるディレクトリに、

・頭4桁が月日(色々な月日のファイルが混在)
・その後に1文字のアルファベット(まあ、他の文字でもいいんだけど)
・その更に後ろに2桁の数字
・最後に、JPEG の拡張子(.jpg)

という命名条件のファイルがあった時、そのファイル一覧の中から、2月9日(0209)の日付のもので、

(1)アルファベットの後ろの 2桁の数字が 01 のものをまず抽出したい。
(2)その後、2桁の数字が 01以外のファイルを抽出したい。

という時、(1) のファイル名に当てる正規表現は簡単だよな。

例えばファイル名の一覧が、

0209C01.jpg
0209C02.jpg
0209Q38.jpg
0209C10.jpg
0209A28.jpg
0210C28.jpg
0209Y28.jpg
0212C01.jpg

だったら、

if ($d =~ /^0209.{1}01\.jpg$/) {...}

で 0209C01.jpg だけがマッチする。(.{1}のところは、[A-Z]{1}じゃないかと言われそうだけど、ここには必ずアルファベットが入っているので .{1}で十分なんだ。{1}はいらないけど、わかりやすくするためにね)

じゃあ、(2) の抽出はその否定でいいのか?って言うと、それは駄目だよね。

if ($d !~ /^0209.{1}01\.jpg$/) {...}

では、

0209C02.jpg
0209Q38.jpg
0209C10.jpg
0209A28.jpg
0210C28.jpg
0209Y28.jpg
0212C01.jpg

が全部マッチしてしまう。実際にマッチさせたいのは先頭が 0209 のものだけなのに。

まあ、

if ($d !~ /^0209.{1}01\.jpg$/ && $d =~ /^0209/) {...}

とすればいいんだけど、何か 2つの条件を && でつなげるのも格好悪いなあ、何となく。

こういう時に、日頃はあんまり使わない「否定先読み」を使うと正規表現一発で済むので格好良い。

if ($d =~ /^0209.{1}((?!01).){2}\.jpg$/) {...}

これなら、

・2月9日(頭 4桁が 0209)のものだけで
・アルファベットの後ろの 2桁が 01 以外のもの

にマッチするので、上記の例なら、

0209C02.jpg
0209Q38.jpg
0209C10.jpg
0209A28.jpg
0209Y28.jpg

がマッチして幸せなのである。
Perl v5.8.5 の正規表現なんじゃけど、例えば、

<div class="hoge1">
<div class="hoge2">
ほげほげ
</div>
<div class="hoge3"></div>
</div>わたしは恥知らずな夜のホームラン王よ!<div class="date">[ 8/6 12:48 ]</div>

みたいなテキスト(HTML ソース)があったとして、

/\/div>(.*?)<div class=\"date\">\[(.*?)\]/gs

みたいなマッチング条件だと、

$1に

<div class="hoge3"></div>
</div>わたしは恥知らずな夜のホームラン王よ!

がセットされてしまう。最短マッチの(.*?)っていう条件にしてるのに、最長マッチしたものがセットされているってわけだ。

で、

/.+\/div>(.*?)<div class=\"date\">\[(.*?)\]/gs

みたいに、頭に .+ という「任意の数文字」という条件を足すと、ちゃんと

わたしは恥知らずな夜のホームラン王よ!

だけを取ってくる。

何、これ?

正規表現の神よ、なんで .+ を付けるだけで結果が変わってくるのん?.*?って最短マッチしてるんだから、その前に任意の文字があろうがなかろうが関係ないやん。
なんでぇ?教えてぇ?

このアーカイブについて

このページには、2012年8月以降に書かれたブログ記事のうちPerlカテゴリに属しているものが含まれています。

前のアーカイブはPerl: 2012年5月です。

次のアーカイブはPerl: 2012年9月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。


月別 アーカイブ

電気ウナギ的○○ mobile ver.

携帯版「電気ウナギ的○○」はこちら