C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[7]: 正規表現でフリーズ


(過去ログ 128 を表示中)

[トピック内 11 記事 (1 - 11 表示)]  << 0 >>

■76054 / inTopicNo.1)  正規表現でフリーズ
  
□投稿者/ 化學兄弟 (1回)-(2015/05/28(Thu) 12:18:07)

分類:[VB.NET/VB2005 以降] 

開発環境:VB2008

正規表現でテキストの解析を行っているのですが、
あるテキストでフリーズ状態となってしまいます。

Const TAG_PTN As String = "^<font(.|\s)+</font>$"
Return Regex.IsMatch(htmlPart, TAG_PTN, RegexOptions.IgnoreCase)

フリーズが発生する時のテキストの内容はお伝えできないのですが、
そのテキスト以外ではフリーズを確認しておりません。

また、色々と調べたところ、パターン内の"(.|\s)+"の部分が問題のようで、
上記パターンの"(.|\s)+"の部分を".+"に変え、

Const TAG_PTN As String = "^<font.+</font>$"

とするとフリーズは発生しません。
#上記のパターンは問題再現確認用のもので、実際のパターンはもっと複雑なのですが、
 やはり"(.|\s)+"の部分を".+"に変えることによりうまく動きました。

このパターンで考えられる問題は何でしょうか?
また、"(.|\s)+"のパターンは、改行を含むすべての文字にマッチさせたいために
入れたのですが、他にパターンの書き方はありますでしょうか?

あまり正規表現に慣れていないので、どうぞお知恵をお貸しください。

以上、よろしくお願いいたします。
引用返信 編集キー/
■76056 / inTopicNo.2)  Re[1]: 正規表現でフリーズ
□投稿者/ shu (748回)-(2015/05/28(Thu) 12:36:30)
No76054 (化學兄弟 さん) に返信

> Return Regex.IsMatch(htmlPart, TAG_PTN, RegexOptions.IgnoreCase)
Dim ret = Regex.IsMatch(htmlPart, TAG_PTN, RegexOptions.IgnoreCase)
Return ret

としてこの1行目でブレークポイントを貼ってステップ実行したときにReturn retに
到達しないのでしょうか?これ自体はすぐに処理が進んでいるとしたら他のところで
終了条件に達しないループになっていないでしょうか?
引用返信 編集キー/
■76058 / inTopicNo.3)  Re[2]: 正規表現でフリーズ
□投稿者/ 化學兄弟 (2回)-(2015/05/28(Thu) 13:04:43)
2015/05/28(Thu) 13:15:37 編集(投稿者)

(誤字を修正しました)
No76056 (shu さん) に返信
> ■No76054 (化學兄弟 さん) に返信
>
>>Return Regex.IsMatch(htmlPart, TAG_PTN, RegexOptions.IgnoreCase)
> Dim ret = Regex.IsMatch(htmlPart, TAG_PTN, RegexOptions.IgnoreCase)
> Return ret
>
> としてこの1行目でブレークポイントを貼ってステップ実行したときにReturn retに
> 到達しないのでしょうか?これ自体はすぐに処理が進んでいるとしたら他のところで
> 終了条件に達しないループになっていないでしょうか?

shu様、ご連絡ありがとうございます。
テスト用プロジェクトを用意し、上記コードで問題のテキストの処理を
ステップ実行で行ってみました。
すると、

Dim ret = Regex.IsMatch(htmlPart, TAG_PTN, RegexOptions.IgnoreCase)

の行までは来ます(行が黄色くなる)。
が、次のステップを進めると、Return retには到達せず、フリーズ状態となります。
この時に一時停止を行うと、上記行が緑色になります。
(以降、ステップ実行しても状況は上記と同じです。)


よく考えると、パターン(.|\s)の"\s"には、"."にも含まれる改行以外のスペース文字が
含まれるので、これが問題を生む可能性があるのでしょうか?
このため、改行も含むすべての文字にマッチさせるには、
例えば"[.\n\r]"と書くのが正解なのでしょうか?
(これだと、確かにフリーズは発生しません。)
引用返信 編集キー/
■76063 / inTopicNo.4)  Re[3]: 正規表現でフリーズ
□投稿者/ shu (749回)-(2015/05/28(Thu) 15:25:42)
No76058 (化學兄弟 さん) に返信

違うパターン文字列ですが
http://okwave.jp/qa/q1934309.html
のようにパターン文字列によりかなり時間がかかることが
あるとありました。この関係ではないでしょうか?
引用返信 編集キー/
■76065 / inTopicNo.5)  Re[1]: 正規表現でフリーズ
□投稿者/ 魔界の仮面弁士 (351回)-(2015/05/28(Thu) 15:59:04)
No76054 (化學兄弟 さん) に返信
> フリーズが発生する時のテキストの内容はお伝えできないのですが、

そのものズバリでなくても、状況を再現できるダミーデータを提供できませんか?


> あまり正規表現に慣れていないので、

私も正規表現は得意なほうでは無いので、あくまで 一般論 として書きますが、
たとえ単純な短い正規表現であっても、その内容次第では、
対象文字列が長くなるほど、加速度的に遅くなってしまうことがあります。

遅くなる要因は幾つかありますが、例を挙げるとすれば OR 分岐や繰り返し表現などです。


繰り返し処理は特に顕著です。極端な例を挙げれば、
 Const TAG_PTN As String = "(.*)*^" '探索結果は「^」と同じ
の場合、常に行頭の 0 文字にマッチすることが自明ですが、
そこに至るまでにはすべての組み合わせが試されることになるため、
文字数が長くなるにつれ、速度低下が顕著となります。

実際、相手が 30 文字程度であっても、相当に長い時間がかかっていました。

 Const TAG_PTN As String = "(.*)*^"
 For n As Integer = 0 To 40
  Dim sw As Stopwatch = Stopwatch.StartNew()
  Dim htmlPart As String = StrDup(n, "@"c)
  Dim isMatch As Boolean = Regex.IsMatch(htmlPart, TAG_PTN, RegexOptions.IgnoreCase)
  sw.Stop()
  Console.WriteLine("{0}, {1:F4}", n, sw.Elapsed.TotalMilliseconds)
 Next

当方環境での例ですが、上記のコードで
 18文字 => 約64ミリ秒
 19文字 => 約128ミリ秒
 20文字 => 約256ミリ秒
 21文字 => 約512ミリ秒
 22文字 => 約1秒
 23文字 => 約2秒
 24文字 => 約4秒
 25文字 => 約8秒
 26文字 => 約16秒
のように、文字数に応じてほぼ倍々の時間がかかっていました。
このまま続けていれば、40 文字で 4 分以上かかる見込みですね。

正規表現を "((.*)*)*^" にすれば、繰り返し回数がさらに伸びることでしょう。



そして、もう一つ例に挙げた OR 分岐 ですが…今回の場合、
> Const TAG_PTN As String = "^<font(.|\s)+</font>$"
の「(.|\s)」ですが、\s というのは空白文字で、. は任意文字ですよね。

. には \s の意味も含まれるので、OR 探索は不要に見えるのですが、
自らが改善案として提示された "(.|\s)+" → ".+" ではマズイのでしょうか?

「空白文字」に合致した場合の検索パターンが、
(.|\s) の場合、. の倍になってしまい、無駄に見えます。

それ自体は僅かな差だと思いますが、「+」による繰り返し処理の内側にあるため、
マッチングする文字数が長くなるほど、その差は累積されてしまうでしょう。


それでなくとも、OR 分岐は比較的低速となりがちです。
[abc] で済む処理を (a|b|c) と書いた場合、意味は同じでも
後者の方がパフォーマンス面では劣ります。

実際、
 Const TAG_PTN1 As String = "(a|b|c|d|e)*"
 Const TAG_PTN2 As String = "[abcde]*"
という 2 パターンを、探索回数が極端に多くなるような文字列
 Dim htmlPart As String = StrDup(1000, "b"c)
に対して比較してみたところ、当方環境では
 TAG_PTN1 => 平均 0.168 ミリ秒
 TAG_PTN2 => 平均 0.068 ミリ秒
のように、やはり OR 分岐の方が倍以上遅い結果となっていました。


正規表現のパフォーマンスに関して、下記も参照してみてください。

https://msdn.microsoft.com/ja-jp/library/dsy130b4.aspx
https://msdn.microsoft.com/ja-jp/library/gg578045.aspx
引用返信 編集キー/
■76066 / inTopicNo.6)  Re[2]: 正規表現でフリーズ
□投稿者/ Hongliang (311回)-(2015/05/28(Thu) 16:02:09)
> そして、もう一つ例に挙げた OR 分岐 ですが…今回の場合、
>>Const TAG_PTN As String = "^<font(.|\s)+</font>$"
> の「(.|\s)」ですが、\s というのは空白文字で、. は任意文字ですよね。
>
> . には \s の意味も含まれるので、OR 探索は不要に見えるのですが、
> 自らが改善案として提示された "(.|\s)+" → ".+" ではマズイのでしょうか?

既定では . は \n 以外の文字にマッチです。
RegexOptions.Singlelineを指定すれば、 . を \n を含む任意の文字にマッチするように意味を変更させられますが。
引用返信 編集キー/
■76067 / inTopicNo.7)  Re[3]: 正規表現でフリーズ
□投稿者/ 魔界の仮面弁士 (352回)-(2015/05/28(Thu) 16:06:11)
No76066 (Hongliang さん) に返信
> 既定では . は \n 以外の文字にマッチです。
> RegexOptions.Singlelineを指定すれば、 . を \n を含む任意の文字にマッチするように意味を変更させられますが。

ナルホドです。フォローありがとうございます。
引用返信 編集キー/
■76068 / inTopicNo.8)  Re[4]: 正規表現でフリーズ
□投稿者/ なちゃ (43回)-(2015/05/28(Thu) 16:27:02)
バックトラックで大変なことになってるみたいですね。
試してみたところ、すでに書かれてますが、マッチしない場合に空白文字数で2^nオーダの時間がかかってるようですね。
具体的になにが起こってるのかはまだ把握できてないですが、ちょっと興味がありますね(やや不可解な気がしないでもない)。

解決策は、これも出てますがSingleLineオプションが一番簡単だと思います。
SingleLineを指定しないなら、[\s\S]とかでいける気がします。
解決済み
引用返信 編集キー/
■76070 / inTopicNo.9)  Re[5]: 正規表現でフリーズ
□投稿者/ なちゃ (44回)-(2015/05/28(Thu) 16:32:01)
No76068 (なちゃ さん) に返信
> 試してみたところ、すでに書かれてますが、マッチしない場合に空白文字数で2^nオーダの時間がかかってるようですね。

ちょっと補足、試したのは実際の今回の正規表現で、実際に空白数の倍々の時間がかかっていたということです。
解決済み
引用返信 編集キー/
■76071 / inTopicNo.10)  Re[6]: 正規表現でフリーズ
□投稿者/ 化學兄弟 (3回)-(2015/05/28(Thu) 17:04:19)
皆さま、誠にありがとうございます。
再現し、かつ提供できるようなサンプルが用意できないのですが、
バックトラックというものを甘く見てはいけないということですね。

取り急ぎ、今回の件については、SingleLineを指定する、または[\s\S]を使うことで
対応できましたのでご報告いたします。

色々とありがとうございました。
もっと勉強します。

すでに解決済みがついているようですが、改めて解決済みとさせていただきます。
今後ともよろしくお願い申し上げます。
解決済み
引用返信 編集キー/
■76073 / inTopicNo.11)  Re[7]: 正規表現でフリーズ
□投稿者/ なちゃ (45回)-(2015/05/28(Thu) 18:31:09)
> すでに解決済みがついているようですが、改めて解決済みとさせていただきます。

あああ、すみません、解決済みつけてしまったのは私の間違いです。

私の前にすでに解決済みがついているものと勘違いしてました。
※解決済みを間違って外してしまうことが多いので、忘れないようにと思いながらチェックしたんですが、今回はそもそも元からついてなかったという…
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -