1
public static final String PATTERN = "(?<=(^|,))(([^\",]+)|\"([^\"]*)\")(?=($|,))";
public static void main(String[] args) {
    String line = ",1234,ABC";
    Matcher matcher = Pattern.compile(PATTERN).matcher(line);
    while (matcher.find()) {
        if (matcher.group(3) != null) {
            System.out.println(matcher.group(3));
        } else {
            System.out.println(matcher.group(4));
        }
    }
}

上記のプログラムを使用して文字列を解析しました",1234,ABC"。解析後、次のように 3 つのトークンを取得する必要があります。

  1. 空の文字列、つまり ""
  2. 1234
  3. ABC

Java 1.6 では動作するようですが、Java 1.5 では動作しません。

Java 1.4以降、正規表現はJavaにあるのに、なぜこのような問題に直面しているのでしょうか?

4

2 に答える 2

5

これは、少なくとも JRE 1.5 Update 18 まで、および JRE 1.6 Update 32 (私がテストした 2 つのバージョン) より前の Java クラス ライブラリ (Sun の実装、Oracle に引き継がれたもの) のバグです。

(?<=pattern)いくつかのテストの後、正の後読みと負の後読みの実装にバグがいくつかあります(?<!pattern)1,2後読みの非キャプチャ グループ内で、交互に区切られた異なる幅3のパターンがある場合に、エンジンがバックトラックする方法に関係している可能性があります。|

後読みで項目の順序を入れ替えるとうまくいく場合もあります4が、付録 2 は、常にうまくいくとは限らないことを示しています。

今のところ、後読みから代替を抽出することが可能な解決策のようです。例: 代替のある後読み(?<=pat1|pat2|pat3)は に変換され(?:(?<=pat1)|(?<=pat2)|(?<=pat3))ます。後読みがなくなるまで繰り返し|ます。以下で使用したテストケースでは正しい結果が得られるようです。

したがって、問題の正規表現の場合、これが回避策です (元の正規表現が正しいと仮定します)。

"(?:^|(?<=,))(?:([^\",]+)|\"([^\"]*)\")(?:$|(?=,))"

先読みに問題がある場合に備えて、ユースケースで結果が同じであるため、非キャプチャグループにも置き換えます。(テストではバグがあることはまだ明らかにされていませんが、念のためです。) 完全にはわかりませんが、少なくとも と については、エンジンが正しく動作することを信頼できると思い(?<=,)ます(?=,)。私はまた、捕獲グループの数を減らす自由を持っているので、それらを詳しく数えてください.

付録

  1. 入力文字列",abc,1234"と正規表現"(?<=^|[,.])"および"(?<!^|[,.])". JRE 1.5u18 と JRE 1.6u32 では結果が異なっていました。肯定的な後読みの場合"(?<=^|[,.])"、JRE 1.6u32 の出力と比較して、JRE 1.5u18 の出力では位置 1 の一致が欠落しています。代わりに、JRE 1.5u18 の場合、位置 1 は否定的な後読みの結果に表示され"(?<!^|[,.])"ますが、JRE 1.6u32 の出力には含まれません。

    肯定的な後読みと否定的な後読みは互いに正反対であるため、この補完的な動作を見ることはそれほど驚くことではありません。

  2. ",abc,."入力文字列と正規表現を使用した別のテスト"(?<=,abc|[,.])"。JRE 1.6u32 と比較して、JRE 1.5u18 の結果リストには位置 1 の一致は表示されません。

    "(?<=[,.]|,abc)"JRE 1.6u32 と比較して、JRE 1.5u18 の結果では、位置 4 の一致が欠落しています。

  3. 異なる幅に限定されない場合がありますが、私がテストした場合です。

  4. 質問の正規表現をこの入力",1234,ABC,\"sdfsdf,sdf\",sdfskhkf,"でスワップ^,て交互に機能させることができます。つまり、に変更(?<=(^|,))(?<=(,|^))ます。

于 2013-01-19T15:06:26.923 に答える
1
String line = ",1234,ABC";
String[]arr= line.split(",");
System.out.println("arr.length = " + arr.length);
for(String s : arr)
{
   System.out.println("s = \"" + s+"\"");
}

出力は次のとおりです。

arr.length = 3
s = ""
s = "1234"
s = "ABC"
于 2013-01-19T12:43:08.063 に答える