VB.NETで正規表現を使用した検索でフリーズする

このQ&Aのポイント
  • VB.NET(2003)ですが、Regexを使った正規表現での検索時に検索パターンによっては、プログラムがフリーズして固まります。なにか情報はないでしょうか?
  • VBプログラムファイル内のコメントを一気に検索するつもりで、 (".*?"|[^"'])*('.*?)\r\n とするとOKですが、 (".*?"|[^"']+)('.*?)\r\n とするとフリーズします。(+を一つ足した)
  • フリーズは、pMatches.Countの部分で起こっているようです。Matchesの変わりにMatchとNextMatchを使うと、順に検索結果が得られますが、最後の結果にNextMatchを実行したところで固まります。
回答を見る
  • ベストアンサー

VB.NET で正規表現を使用した検索でフリーズする

VB.NET(2003)ですが、Regexを使った正規表現での検索時に検索パターンによっては、プログラムがフリーズして固まります。なにか情報はないでしょうか? VBプログラムファイル内のコメントを一気に検索するつもりで、 (".*?"|[^"'])*('.*?)\r\n とするとOKですが、 (".*?"|[^"']+)*('.*?)\r\n とするとフリーズします。(+を一つ足した) プログラムは、 pMatches = Regex.Matches(src, pat) If pMatches.Count = 0 Then MsgBox("マッチしません") End If といった感じで、 src=対象テキストを全行取込んだ文字列、pat=検索パターンです。 フリーズは、pMatches.Countの部分で起こっているようです。 Matchesの変わりにMatchとNextMatchを使うと、順に検索結果が得られますが、最後の結果にNextMatchを実行したところで固まります。 フリーズ中、タスクマネージャで見ている限りではCPU=100%(HTでは50%)、となりますが、使用メモリー量は変化ありません。

質問者が選んだベストアンサー

  • ベストアンサー
  • UKY
  • ベストアンサー率50% (604/1207)
回答No.2

いやいや、バックトラッキングを甘く見ちゃいけないと思いますよ。 (a+)* という正規表現では、対象の文字列が一文字増えるたびにパタンの数が 2 倍になります。 手元で軽く実験して見ました。 (a+)*b という正規表現に対して、文字列 aaa....aaac を検索させると、a が 20 文字のときで約 1 秒、21 文字で約 2 秒、22 文字で約 4 秒、23 文字で約 8 秒、24 文字で約 15 秒でした。 それに対し、(a)*b という正規表現では a が 1000 文字でも 1 秒掛かりませんでした。 実際、(a+)*b のパタン数は a が 24 文字のとき 2 の 24 乗で 16777216 通りですが、(a)*b の方は 1000 文字でも 1001 * 1002 / 2 で 501501 通りしかありません。 ちなみに、(?>(a+)*b) のようにバックトラッキングを無効にすると、数千文字でも一瞬でした。

bikkuri
質問者

お礼

どうも甘く見ていたようです。 「ちなみ・・」のように (?>(".*?"|[^"']+))*('.*?)\r\n や (".*?"|(?>[^"']+))*('.*?)\r\n とすると検索できました。 よく考えると、パターンそのものは、テキストの先頭から最後のコメント部分までは全部、最長での一致範囲なのでバックトラックが発生しないが、最後のコメントからテキスト末尾にパターンが一致しないことを判定するのにバックトラックが発生して長考しているようです。 (".*?"|[^"']+)*(('.*?)\r\n|$) としてもフリーズしませんでした。 もうちょっと細部を確認したいので、来週まで保留にさせてください。

bikkuri
質問者

補足

perlでも実行して同様にフリーズすることを確認しました。 「バックトラックで長考している」で納得できました。 (?> ..)などで回避するようにします。 回答ありがとうございました。

その他の回答 (1)

  • UKY
  • ベストアンサー率50% (604/1207)
回答No.1

例えば、(a)*b という正規表現に対し aaac という文字列を検索させると、次のように処理が進みます。 (a)(a)(a)c (a)(a)ac (a)aac aaac * 演算子や + 演算子は繰り返しの回数を変化させて可能性のあるパタンを全て試します。上の例では繰り返しの回数は 4 パタンあります。 正規表現が (a)*b ではなく (a+)*b だと、次のようになります。 (aaa)c (aa)(a)c (aa)ac (a)(aa)c (a)(a)(a)c (a)(a)ac (a)aac aaac + を足しただけで可能性のあるパタンの数が増えています。ソースコードのように何千文字もある文字列を検索すれば、パタンの数は莫大になります。つまり、検索処理が半永久的に終わらなくなるのです。

bikkuri
質問者

補足

回答ありがとうございます。 「処理時間が非常にかかっている」というもの十分考えられるとおもいますが、今回の検索パターンではバックトラックはあまり発生しないと思いますので、それほどの時間はかからないはずと思います。(勝手な思い込みかもしれませんが) また、Matchで検索結果を順番に取得した場合、最後の検索結果までは特に問題なく実行できます。 フリーズするのは、最後のその次をNextMatchで取得する時に発生しているようです。 プログラムでは次のような感じです。 dim ma as match = Regex.Match(src, pat) do while ma.Success xxxxx ma = ma.NextMatch loop 最後の次をNextMatchで取得するとSuccess=Falseになっているはずですが、これを取得するタイミングで戻ってきません。 いくつかの対象テキストで試しましたが、このタイミングは同じでした。 (数行程度のテキストでは確かにOKでした。しかし30行程度でもNGです) 具体的なテキストとプログラムがあったほうが良さそうなので、用意してみます。

関連するQ&A

  • カンマ区切りの金額を検索するための、正規表現を教えてください

    PHP4を使っています。 表題のとおりなのですが、 カンマ区切りの金額を検索するための、正規表現を教えていただけないでしょうか? http://oraclesqlpuzzle.hp.infoseek.co.jp/regex/regex-3-14.html を参考に、 $pattern = '^(0|([1-9][0-9]{0,2}(,[0-9]{3}){0,2}))$'; if (preg_match("/$pattern/", $oneline_buffer, $matches)){ としているのですが、 なぜかpreg_matchにひっかかりません。 (preg_match の直前の行を通っていることは、確認済みです) どなたか、お助け願えれば幸いです。

    • ベストアンサー
    • PHP
  • VB.NETの正規表現の検索を教えてください。

    VB.NETの正規表現で文章中(HTMLソース)から番号を抜き取りたいのですが、 先頭の1つだけしか見つけられません。 htmlソース内に 番号:10000 番号:20000 番号:30000 番号:40000 という文字があるので、その後半の10000や20000を取得したいです。 下記のコードで実行すると、10000が4つ出力されてしまいます。 良い方法をご教示頂きたいです。よろしくお願いいたします。 Dim pagedata As String  <---htmlソース Dim reg As Regex reg = New Regex("番号:(?<datano>.+)", RegexOptions.Compiled) Dim DataNo As String m = reg.Match(pagedata) While m.Success = True DataNo = reg.Match(pagedata).Result("${datano}") m = m.NextMatch() Console.WriteLine(DataNo) End While

  • 時間の正規表現

    正規表現で、17:00:00という文字の17:00(時:分)の部分だけを見て、時間として認識させたいのですが、どのように記述すればいいのでしょうか? 以下のプログラムを作成したのですが、なぜかfalseが返されてしまって困っています。 教えてください。宜しくお願いいたします。 String str_time = "17:00:00"; String str = "([0-9]|([0-1][0-9]|[2][0-3]))[:][0-5][0-9]"; Pattern pat = Pattern.compile( str ) ; Matcher mat = pat.matcher( str_time ) ; if( mat.matches() ){ // 処理 }

    • ベストアンサー
    • Java
  • .NET のスマートな正規表現の記述の仕方

    VB8 (2005)で、正規表現の書き方ですが、抽出する場合は、 以下のようにしましたが、抽出せず、単にマッチしたかとうが If文で判別する方法が知りたいです。 Imports System.Text.RegularExpressions '正規表現 Dim dat As String = "TEST 01" Dim ptn As String 'パターン Dim r As Regex ' Dim dv As String '部分 '抽出 ptn = "^TEST ([0-9]+)$" r = New Regex(ptn, RegexOptions.IgnoreCase) For Each m As Match In r.Matches(dat) dv = m.Groups(1).Value() MsgBox("抽出=[" & dv & "]") Next あと、上のようにFor文で回していますが、Globalでなく、今回は1つだけの抽出で、しかも1つだけの()なので、これをFor文を使わずに直接取得する書き方はあるのでしょうか? それ以前に、.NETの正規表現の書き方が一般的でなければ、ご指導お願い致します。

  • WSHの正規表現について

    WindowsXPのCACLSコマンドの出力結果(テキストにリダイレクト)に改行や復帰が入っていて、空行の削除をしたいのです。 下記のプログラムを作ったのですが、復帰コードが 入っている箇所がとれないのです。どなたかご教授お願い致します。。。 Set fso = CreateObject("Scripting.FileSystemObject") Set src = fso.GetFolder("C:\WORK") Set regEx1 = New RegExp srhStr1 = "^ *$" regEx1.Pattern = srhStr1 regEx1.Global = True regEx1.IgnoreCase = True Set TextFile = fso.OpenTextFile("cacls結果.txt") Do Until TextFile.AtEndOfStream tmpLine = TextFile.ReadLine If regEx1.Test(tmpLine) Then else srhLine = srhLine & tmpLine & vbNewLine End If Loop WScript.Echo srhLine

  • 正規表現について

    パターンマッチのところでつまずいてしまっています。 例えば、 マッチがマッチをすったらマッチになった という文面があったとします。(適当です) $txt = 'マッチがマッチをすったらマッチになった'; if($txt =~ m/$txt/s){$txtb = $';} print "$txtb"; としたら、 がマッチをすったらマッチになった と出てくると思います。 これを、 になった と表示させる為に、最後の「マッチ」にパターンマッチをさせる方法はありませんでしょうか? ネット上のHPを検索しましたが、どうしても見つけられませんでした。 何とぞ宜しくお願いします。

    • ベストアンサー
    • Perl
  • 正規表現を教えてください。

    正規表現を教えてください。 C#で正規表現を用いた文字列検索を勉強中なのですが、うまくマッチさせることができません。教えていただけないでしょうか。 検索対象の文字列は下のようになります。 キーワード;値; 文字列中に該当するキーワードが存在する場合に、その値を取得するプログラムを考えています。しかし、私の正規表現では2つ目の「;」を検出してしまい、キーワードのみを取得することが出来ずに困っています。 見当違いの表記をしているとは思うのですがどなたかお助けください。 match = Regex.Match(line, "^(.*);"); C#初心者なものです。

  • VB.NETで正規表現を教えてください。URL文字を取得

    VB.NETなのですが、 htmlソースを変数に入れて、その中からURLを抽出して、 1つずつ保存していくという動作をさせたいのですが、 どうしてもURLを一つずつ取得することができません。 Dim r As Regex Dim m As Match Dim JpgFile As String r = New Regex("http://.+\.jpg") m = r.Match(html) If m.Success = False Then   Else JpgFile = m.Value ここで1つずつ保存 End If 現在、ここまで出来たのですが、 これだと、変数html内に複数のjpgのURLがあると 最初のURLの1文字目から、最後のURLの末尾までを一度に 取得してしまいます。 URLの文字列はhttp://で始まっていて、終わりは・・・.jpg" になっていたり、"がなかったりします。 >で閉じる前に同じタグ内にWidthなどサイズを指定している場合もあります。 また、上記のようにタグ内に書かれていなくて、クリッカブルリンクになっていないURLの場合もありそれも取得したいです。 一つずつ取得する方法や一度に配列に読み込む方法などがありましたら、 ご教示頂けると助かります。 よろしくお願い致します。

  • VB2005のRegexで全角文字のマッチ

    VB2005のRegexで、インターネットのソースコードを取得し”次の10件"というキーワードの位置を取得しようとしていますが、どうもソースコードの時点で、全角文字(2バイト文字)が認識されていないようです。どうすれば、マッチできるでしょうか?教えてください。 仮に、下記のルーチンに、Debug.Print(inputString)を入れて表示させてみると、全角の表示がされていません。 よろしくお願いいたします。 Sub DumpHrefs(ByVal inputString As String) Dim r1 As Regex Dim m1 As Match r1 = New Regex("次の10件≫") m1 = r1.Match(inputString) While m1.Success Debug.WriteLine("次の10件" & " at " & m1.Groups(1).Index.ToString())  m1 = m1.NextMatch()  End While End Sub

  • VB.NETの正規表現をVBAで記述するには

    VB2010.NETのコードをExcel2010のVBAのコードに置き換える作業をしていますが、List1のようにVB.NETのコードには、Inports System.Text.RegularExpressions で.NET正規表現パッケージが使われて、この部分をVBAではどのように記述すればよいのでしょうか。 自分なりにList2のようにしてみましたが、Dim M As MatchのMatchの部分で「ユーザ定義型は定義されていません」というコンパイルエラーが発生します。 参照設定にはSystem.Text.RegularExpressionsが見当たりませんが何を指定すればよいのでしょうか。 Microsoft VBScript Regular Expressions 5.5のRegExpオブジェクトでは後読みができないので大変困っています。 よろしくお願いします。(Windows7) ---List1:VB.NET(正常)--------------- Option Explicit On Option Strict On Imports System.Text.RegularExpressions Module Module1  Sub Main()   Dim SampleText As String = "今日は西暦2014年6月20日です"   Dim M As Match = Regex.Match(SampleText, "(?<=西暦)\d+")   If Not M.Success Then    Console.WriteLine("no match")   Else    Dim MatchedText As String = M.Value    Dim MatchedFrom As Integer = M.Index    Dim MatchedLen As Integer = M.Length    MsgBox("matched [" & MatchedText & "]" & _        " from char#" & MatchedFrom.ToString() & _        " for " & MatchedLen.ToString() & " chars.")   End If  End Sub End Module ---List2:VBA(このコードではエラーになる)----- Option Explicit Sub test()  Dim SampleText As String  Dim M As Match  Dim R As New Regex  Dim MatchedText As String  Dim MatchedFrom As Integer  Dim MatchedLen As Integer  SampleText = "今日は西暦2014年6月20日です"  R = Regex("(?<=西暦)\d+")  M = R.Match(SampleText)  If Not M.Success Then   MsgBox ("no match")  Else   MatchedText = M.Value   MatchedFrom = M.Index   MatchedLen = M.Length   MsgBox("matched [" & MatchedText & "]" & _       " from char#" & MatchedFrom.ToString() & _       " for " & MatchedLen.ToString() & " chars.")  End If End Sub ------------------------------------------

専門家に質問してみよう