- 締切済み
正規表現の達人にお聞きしたい。
正規表現で、文字数は4文字ですが、そのうち1つでもaがあれば マッチするということで、1~3まではマッチし、 4はa無しなのでマッチせず、5は4文字でないのでマッチしない ような正規表現は書くことは可能でしょうか? 1) aaaa 2) ajjj 3) kaoa 4) jjjj 5) axxxx
- みんなの回答 (5)
- 専門家の回答
みんなの回答
- ma5aru
- ベストアンサー率0% (0/0)
以下スクリプトで、各案のパフォーマンスを計測してみました。 -- #!/usr/bin/perl use Time::HiRes qw(gettimeofday); my @testData = qw(aaaa ajjj kaoa jjjj axxxx); # テストデータ # aを含む4文字にマッチする正規表現 全案 my %matchesIt4CharIncluding_a = ( # Tacosanさん 全ケースベタ書き案 'No.1' => qr/^(?:a...|.a..|..a.|...a)$/, # kirikirkazさん 明示的バックトラック案 'No.2' => qr/^(?=.*a).{4}$/, # poppo64さん 明示的バックトラック逆案 'No.3' => qr/^(?=.{4}$).*a/, # kinoko2009さん 評価コード埋込案 'No.4' => qr/(?(?{length!=4||index($_,"a")<0})(?!))/, # (参考) パターン展開コード埋込案 'No.5' => qr/^[b-z]{0,3}+a(??{'[a-z]{'.(4-length($&)).'}+'})$/, ); my $times = 1000_000; # パフォーマンス計測用 繰り返し回数 foreach my $No (sort(keys(%matchesIt4CharIncluding_a))) { my($start, $n) = (get_utime(), $times); printf("%s MATCH's", $No); printf(" : ( %s )", join(' ', grep(/$matchesIt4CharIncluding_a{$No}/, @testData))); grep(/$matchesIt4CharIncluding_a{$No}/, @testData) while ($n--); printf(" x %d time=%.6fs\n", $times, get_utime() - $start); } sub get_utime { return sprintf('%d.%06d', gettimeofday()); } __END__ 以下、実行結果。 [WindowsVista on Core2DuoT9400@2.53GHz] C:\Users\user\Desktop>matchesIt4CharIncluding_a.pl No.1 MATCH's : ( aaaa ajjj kaoa ) x 1000000 time=8.511500s No.2 MATCH's : ( aaaa ajjj kaoa ) x 1000000 time=8.518548s No.3 MATCH's : ( aaaa ajjj kaoa ) x 1000000 time=8.526566s No.4 MATCH's : ( aaaa ajjj kaoa ) x 1000000 time=12.486100s No.5 MATCH's : ( aaaa ajjj kaoa ) x 1000000 time=51.380000s [Linux on VMwarePlayer for WindowsVista] user@ubuntu:~$ ./matchesIt4CharIncluding_a.pl No.1 MATCH's : ( aaaa ajjj kaoa ) x 1000000 time=4.751165s No.2 MATCH's : ( aaaa ajjj kaoa ) x 1000000 time=5.005868s No.3 MATCH's : ( aaaa ajjj kaoa ) x 1000000 time=5.051104s No.4 MATCH's : ( aaaa ajjj kaoa ) x 1000000 time=8.477054s No.5 MATCH's : ( aaaa ajjj kaoa ) x 1000000 time=36.206683s 見た目にもシンプルで分かりやすい正規表現が高速、という結果となりました。 これは、多少バックトラックが発生したとしても、ユーザーで回避策コードを 埋め込むよりも、Perlの正規表現エンジンの最適化は優れているので、巧妙・ 技巧的なコードに拘る必要はない、ということなのかもしれません。
- kinoko2009
- ベストアンサー率100% (1/1)
/(?(?{length!=4||index($_,"a")<0})(?!))/
- poppo64
- ベストアンサー率57% (11/19)
No.2さんのをひっくり返して、次のようにもも書けますね。 /^(?=.{4}$).*a/ ^(?=.{4}$) の部分で文字数が4文字であることを確認し、 .*a で a が含まれていることを確認しています。 # 確認コード foreach my $str ( qw/ aaaa ajjj kaoa jjjj axxxx / ){ $str =~ /^(?=.{0,4}$).{0,3}a/ ? print "$str: MATCH\n" : print "$str: NO MATCH\n"; }; # 出力 # aaaa: MATCH # ajjj: MATCH # kaoa: MATCH # jjjj: NO MATCH # axxxx: NO MATCH
- kirikirkaz
- ベストアンサー率60% (21/35)
こんなのどうでしょう。 /^(?=.*a).{4}$/ (?=.*a) でaが含むかを確認し、また最初の位置まで戻って .{4} で4文字かどうかを確認しています。 ---- テストコード ---- $\ = "\n"; for $p (qw(aaaa ajjj kaoa jjjj)) { print; print $p; print "match?:".($p =~ /^(?=.*a).{4}$/); } ---- テストコード ---- ---- 出力 ---- aaaa match?:1 ajjj match?:1 kaoa match?:1 jjjj match?: axxxx match?: ---- 出力 ----
- Tacosan
- ベストアンサー率23% (3656/15482)
ベタに書くなら ^(?:a...|.a..|..a.|...a)$