POSTD PRODUCED BY NIJIBOX

POSTD PRODUCED BY NIJIBOX

ニジボックスが運営する
エンジニアに向けた
キュレーションメディア

POSTD PRODUCED BY NIJIBOX

POSTD PRODUCED BY NIJIBOX

ニジボックスが運営する
エンジニアに向けた
キュレーションメディア

FeedlyRSSTwitterFacebook
Liz Bennett

本記事は、原著者の許諾のもとに翻訳・掲載しております。

わずかな文字がいかにしてパフォーマンスに大きな違いを生めるかというお話


使使 12

2稿使

稿使 http://www.rexegg.com/ 


2
.* (.*)\[(.*)\]:.*

[12]\d{3}-[01]\d-[0-3]\d (.*)\[(.*)\]:.*

これらの表現は、以下のフォーマットのテキストにマッチするように記述されています。

2014-08-26 app[web.1]: 50.0.134.125 - - [26/Aug/2014 00:27:41] "GET / HTTP/1.1" 200 14 0.0005

 app  web.1 2


 


21使






50.0.134.125 - - [26/Aug/2014 00:27:41] \"GET / HTTP/1.1\" 200 14 0.0005

悪い正規表現はマッチしない入力データに対してどのように実行されるか

悪い正規表現は以下のような感じです。メインのセクションには色付けしています。

.* (.*)\ [ (.*) \]:.*

1. 正規表現は .* で始まりますが、 * が貪欲なため、最初にできるだけ多くのテキストを取得するべく全てのイベントをスキャンします。 ^(1)

50.0.134.125 - - [26/Aug/2014 00:27:41] \"GET / HTTP/1.1\" 200 14 0.0005
                                                                       

2. 最後まで行くと、最初のスペースに到達するまでバックトラックします。求めているのは左角括弧の前にある(長さ0のこともありますが可能な限り長い)文字列(.*)[です。

50.0.134.125 - - [26/Aug/2014 00:27:41] \"GET / HTTP/1.1\" 200 14 0.0005
                                                                 

3. 0.0005をスキャン後、左角括弧に遭遇しない限り、バックトラックを続けて前のスペースを探します。

50.0.134.125 - - [26/Aug/2014 00:27:41] \"GET / HTTP/1.1\" 200 14 0.0005
                                                                       

50.0.134.125 - - [26/Aug/2014 00:27:41] \"GET / HTTP/1.1\" 200 14 0.0005
                                                              

4. このスキャニングと再スキャニングは、イベント内の3番目のスペースに到達するまで各スペースで続きます。

50.0.134.125 - - [26/Aug/2014 00:27:41] \"GET / HTTP/1.1\" 200 14 0.0005
                                                                       

50.0.134.125 - - [26/Aug/2014 00:27:41] \"GET / HTTP/1.1\" 200 14 0.0005
                                                          

50.0.134.125 - - [26/Aug/2014 00:27:41] \"GET / HTTP/1.1\" 200 14 0.0005
                                                                       

50.0.134.125 - - [26/Aug/2014 00:27:41] \"GET / HTTP/1.1\" 200 14 0.0005
                                               

50.0.134.125 - - [26/Aug/2014 00:27:41] \"GET / HTTP/1.1\" 200 14 0.0005
                                                                       

50.0.134.125 - - [26/Aug/2014 00:27:41] \"GET / HTTP/1.1\" 200 14 0.0005
                                             

50.0.134.125 - - [26/Aug/2014 00:27:41] \"GET / HTTP/1.1\" 200 14 0.0005
                                                                       

50.0.134.125 - - [26/Aug/2014 00:27:41] \"GET / HTTP/1.1\" 200 14 0.0005
                                       

50.0.134.125 - - [26/Aug/2014 00:27:41] \"GET / HTTP/1.1\" 200 14 0.0005
                                                                       

50.0.134.125 - - [26/Aug/2014 00:27:41] \"GET / HTTP/1.1\" 200 14 0.0005
                             

50.0.134.125 - - [26/Aug/2014 00:27:41] \"GET / HTTP/1.1\" 200 14 0.0005
                                                                       

50.0.134.125 - - [26/Aug/2014 00:27:41] \"GET / HTTP/1.1\" 200 14 0.0005
                

5. 左角括弧の前の長さ0の文字列を見つけると(この場合、最初のキャプチャグループは空です)、先に進んで \[.*\]: を照合します。ここに別の .* があるため、再びイベント全体を消費しますが、今回は右角括弧を探します。

50.0.134.125 - - [26/Aug/2014 00:27:41] \"GET / HTTP/1.1\" 200 14 0.0005
                                                                       

50.0.134.125 - - [26/Aug/2014 00:27:41] \"GET / HTTP/1.1\" 200 14 0.0005
                

6. 
50.0.134.125 - - [26/Aug/2014 00:27:41] \"GET / HTTP/1.1\" 200 14 0.0005
              

7. 2番目のスペースまで行き、少しバックトラックをして、やっと先頭に戻ります。

50.0.134.125 - - [26/Aug/2014 00:27:41] \"GET / HTTP/1.1\" 200 14 0.0005
                                                                       

50.0.134.125 - - [26/Aug/2014 00:27:41] \"GET / HTTP/1.1\" 200 14 0.0005
            

50.0.134.125 - - [26/Aug/2014 00:27:41] \"GET / HTTP/1.1\" 200 14 0.0005
                                                                       

50.0.134.125 - - [26/Aug/2014 00:27:41] \"GET / HTTP/1.1\" 200 14 0.0005

8. 



2125


21 ^(2) 100 ^(3)  100110024042


稿100178004700


  ^(4) 
[12]\d{3}-[01]\d-[0-3]\d ([^ \[]*?)\[([^\]]*?)\]:.*

最良の正規表現といい正規表現を分けるものは何か


322


(一)  ?

(二)使    


 . 33?

. [^ \]] 2


800470017000 20


使使

 稿1

Tweet This Post Button


  1. 全ての正規表現のエンジンがここで説明したとおりに動くわけではありません。この投稿のステップスルーはJavaやRuby、PerlやPython、そしてPHPが使う(最適化されていない)アルゴリズムを示したもので、再帰的なバックトラックアルゴリズムです。AwkやgrepはThompson NFAアルゴリズムを使っています。実際のところ、それはどのような点においてもかなり高速ですが、サポートしている機能は制限されます。この2つのアルゴリズムについて、 https://swtch.com/~rsc/regexp/regexp1.html のサイトに詳細な説明があるので、ご興味のある方はご覧ください。

  2. それぞれの正規表現は事前にコンパイルされています。正規表現を繰り返し使う場合、使用前にコンパイルすることでもパフォーマンスを大幅に向上させることが可能です。

  3. パフォーマンステストは、正式な基準値取得を目指したものではなく、ちょっとした実証に過ぎません。テスト環境は2014年のMacbook、OpenJDK 1.7上で行いました。テスト用のコードについては https://github.com/zzbennett/RegexPerfTest/blob/master/Main.java をご覧ください。


  4. 稿 http://www.rexegg.com/regex-quantifiers.html#explicit_greed  
監修者
監修者_古川陽介
古川陽介
株式会社リクルート プロダクト統括本部 プロダクト開発統括室 グループマネジャー 株式会社ニジボックス デベロップメント室 室長 Node.js 日本ユーザーグループ代表
複合機メーカー、ゲーム会社を経て、2016年に株式会社リクルートテクノロジーズ(現リクルート)入社。 現在はAPソリューショングループのマネジャーとしてアプリ基盤の改善や運用、各種開発支援ツールの開発、またテックリードとしてエンジニアチームの支援や育成までを担う。 2019年より株式会社ニジボックスを兼務し、室長としてエンジニア育成基盤の設計、技術指南も遂行。 Node.js 日本ユーザーグループの代表を務め、Node学園祭などを主宰。