web-dev-qa-db-ja.com

RegExを使用して大きな文字列を解析しているときにjava.lang.StackOverflowError

これは私の正規表現です

((?:(?:'[^']*')|[^;])*)[;]

セミコロンの文字列をトークン化します。例えば、

Hello world; I am having a problem; using regex;

結果は3つの文字列です

Hello world
I am having a problem
using regex

しかし、大きな入力文字列を使用すると、このエラーが発生します

Exception in thread "main" Java.lang.StackOverflowError
at Java.util.regex.Pattern$GroupHead.match(Pattern.Java:4168)
at Java.util.regex.Pattern$Loop.match(Pattern.Java:4295)
at Java.util.regex.Pattern$GroupTail.match(Pattern.Java:4227)
at Java.util.regex.Pattern$BranchConn.match(Pattern.Java:4078)
at Java.util.regex.Pattern$CharProperty.match(Pattern.Java:3345)
at Java.util.regex.Pattern$Branch.match(Pattern.Java:4114)
at Java.util.regex.Pattern$GroupHead.match(Pattern.Java:4168)
at Java.util.regex.Pattern$Loop.match(Pattern.Java:4295)
at Java.util.regex.Pattern$GroupTail.match(Pattern.Java:4227)

これはどのように引き起こされ、どのように解決できますか?

35
Ali

残念ながら、Javaの組み込み正規表現サポートには、反復的な代替パス(つまり、(A|B)*)を含む正規表現で問題があります。これは再帰呼び出しにコンパイルされるため、非常に大きな文字列で使用するとStackOverflowエラーが発生します。

可能な解決策は、正規表現を書き直して代替表現を使用しないことですが、セミコロンで文字列をトークン化することを目標とする場合は、実際には複雑な正規表現は必要ありません。 String.split()を使用するだけです。 引数として単純な";"を使用します。

54
Jeen Broekstra

スタックをオーバーフローする正規表現を本当に使用する必要がある場合は、-Xss40mのようなものをJVMに渡すことにより、スタックのサイズを増やすことができます。

16
Andrew

繰り返しが少なくなるように、_+_の後に_[^;]_を追加すると役立つ場合があります。

「正規表現がこの時点まで一致した場合、バックトレースしないでください」という構造もありませんか?多分それも重宝します。 (更新: 所持量指定子 と呼ばれます))。

まったく異なる代替手段は、splitQuoted(char quote, char separator, CharSequence s)と呼ばれるユーティリティメソッドを記述して、文字列を明示的に反復し、奇数の引用符が見られたかどうかを記憶することです。その方法では、引用符付き文字列に引用符文字が含まれている場合、引用符文字をエスケープ解除する必要がある場合にも対処できます。

_'I'm what I am', said the fox; and he disappeared.
'I\'m what I am', said the fox; and he disappeared.
'I''m what I am', said the fox; and he disappeared.
_
7
Roland Illig