簡體   English   中英

Java正則表達式在堆棧溢出時死亡:需要更好的版本

[英]Java regex dies on stack overflow: need a better version

我工作的一個JMD(Java的降價) (的渣口MarkDownSharp ),但我在遇到一個特別的正則表達式的問題。 對於文件Markdown_Documentation_Syntax.text,這個正則表達式會死掉:

private static final String BLOCK_TAGS_1 = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del";
private static final String BLOCKS_NESTED_PATTERN = String.format("" +
        "(" +                      // save in $1
        "^" +                      // start of line (with MULTILINE)
        "<(%s)" +                  // start tag = $2
        "\\b" +                    // word break
        "(.*\\n)*?" +              // any number of lines, minimally matching
        "</\\2>" +                 // the matching end tag
        "[ \\t]*" +                // trailing spaces/tags
        "(?=\\n+|\\Z)" +           // followed by a newline or end of
        ")", BLOCK_TAGS_1);

這意味着:

(^<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b(.*\n)*?</\2>[ \t]*(?=\n+|\Z))

此模式正在查找錨定到行開頭的接受塊標記,后跟任意數量的行,然后由匹配標記后跟換行符或字符串終止符終止。 這會產生:

java.lang.StackOverflowError
    at java.util.regex.Pattern$Curly.match(Pattern.java:3744)
    at java.util.regex.Pattern$GroupHead.match(Pattern.java:4168)
    at java.util.regex.Pattern$LazyLoop.match(Pattern.java:4357)
    at java.util.regex.Pattern$GroupTail.match(Pattern.java:4227)
    at java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3366)
    at java.util.regex.Pattern$Curly.match0(Pattern.java:3782)
    at java.util.regex.Pattern$Curly.match(Pattern.java:3744)
    at java.util.regex.Pattern$GroupHead.match(Pattern.java:4168)
    at java.util.regex.Pattern$LazyLoop.match(Pattern.java:4357)
        ...

這可以通過增加Java的堆棧空間來處理(默認為oss / ss IIRC的128k / 400k),但無論如何上面的表達式都很慢。

所以我正在尋找能夠做得更好的正則表達大師(或者至少用這種模式解釋性能問題)。 C#版本有點慢,但工作正常。 PHP似乎也沒有問題。

編輯:這是在Windows 7 64 Ultimate上運行的JDK6u17上。

這部分:

(.*\n)*?

因為嵌套*會涉及很多不必要的回溯,因為之后必須匹配的字符。

我只是在一些任意字符串上運行perl的快速基准測試,只需將該部分切換為13-15%即可

(?>.*\n)*?

這是非捕獲,獨立的子組。 這給你帶來兩個好處,它不再浪費時間捕獲匹配的字符串,更重要的是,它不再在最里面回溯.*這無論如何浪費時間。 沒有辦法只有那部分。*會產生有效的匹配,所以明確地將它全部或全部都沒有幫助。

但是,不知道在這種情況下這是否足夠改進。

雖然改進模式確實有幫助並且是可取的,但Java的模式匹配器是遞歸的,通常最好切換到迭代解決方案。

當我遇到類似問題時,我切換到jregex( http://jregex.sourceforge.net/ ),這對我有用。

現在使用改進的解決方案可以成功進行模式匹配,但如果給出10倍大的文本,它可能會失敗。

PS:很抱歉有一個老話題,但這個帖子在谷歌上排名很高,如果我把它放在這里會對人們有所幫助

子表達式: "(.*\\\\n)*?" (以及改進的接受答案版本: "(?>.*\\n)*?" ),都有問題:它們無法匹配寫在一行上的塊元素。 換句話說,他們不符合這個:

<div>one-liner</div>

如果這不是理想的行為,那么正確(並且更有效)的解決方案就是簡單地使用:

.*?

並打開單線模式。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM