エスケープされたコンマを無視しながら、コンマ区切りの文字列を分割するにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/820172

  •  03-07-2019
  •  | 
  •  

質問

追加のパラメーターを取得するStringUtils.commaDelimitedListToStringArray関数の拡張バージョンを記述する必要があります:エスケープ文字。

so my:

commaDelimitedListToStringArray("test,test\\,test\\,test,test", "\\")

戻る必要があります:

["test", "test,test,test", "test"]



私の現在の試みは、正規表現を使用してString.split()を使用して文字列を分割することです:

String[] array = str.split("[^\\\\],");

ただし、返される配列は次のとおりです。

["tes", "test\,test\,tes", "test"]

アイデアはありますか

役に立ちましたか?

解決

正規表現

[^\\],

は、「バックスラッシュとそれに続くカンマ以外の文字に一致する」という意味です。 - t はバックスラッシュではない文字であるため、これが t、などのパターンが一致する理由です。

ネガティブルックビハインドを使用して、< code>、の前に \ が付いていない場合、前の文字をキャプチャせずに、

(?<!\\),

(ところで、これを読みやすくするためにバックスラッシュを二重にエスケープしていないことに注意してください)

他のヒント

試してください:

String array[] = str.split("(?<!\\\\),");

基本的に、これはコンマの前に2つのバックスラッシュが付いている場合を除き、コンマでの分割を意味します。これは、ゼロ幅アサーションのネガティブルックビハインドと呼ばれます。

将来の参考のために、ここに私が完成した完全なメソッドがあります:

public static String[] commaDelimitedListToStringArray(String str, String escapeChar) {
    // these characters need to be escaped in a regular expression
    String regularExpressionSpecialChars = "/.*+?|()[]{}\\";

    String escapedEscapeChar = escapeChar;

    // if the escape char for our comma separated list needs to be escaped 
    // for the regular expression, escape it using the \ char
    if(regularExpressionSpecialChars.indexOf(escapeChar) != -1) 
        escapedEscapeChar = "\\" + escapeChar;

    // see http://stackoverflow.com/questions/820172/how-to-split-a-comma-separated-string-while-ignoring-escaped-commas
    String[] temp = str.split("(?<!" + escapedEscapeChar + "),", -1);

    // remove the escapeChar for the end result
    String[] result = new String[temp.length];
    for(int i=0; i<temp.length; i++) {
        result[i] = temp[i].replaceAll(escapedEscapeChar + ",", ",");
    }

    return result;
}

matt bが言ったように、 [^ \\]、はコンマの前の文字を区切り文字の一部として解釈します。

"test\\\\\\,test\\\\,test\\,test,test"
  -(split)->
["test\\\\\\,test\\\\,test\\,tes" , "test"]

drvdijkが言ったように、(?&lt;!\\)、はエスケープされたバックスラッシュを誤って解釈します。

"test\\\\\\,test\\\\,test\\,test,test"
  -(split)->
["test\\\\\\,test\\\\,test\\,test" , "test"]
  -(unescape commas)->
["test\\\\,test\\,test,test" , "test"]

バックスラッシュもエスケープできると期待しています...

"test\\\\\\,test\\\\,test\\,test,test"
  -(split)->
["test\\\\\\,test\\\\" , "test\\,test" , "test"]
  -(unescape commas and backslashes)->
["test\\,test\\" , "test,test" , "test"]

drvdijkは(?&lt; =(?&lt;!\\\\)(\\\\\\\\){0,100})を提案しました。最大100個のバックスラッシュを使用できます。これで十分ですが、なぜ制限があるのですか?より効率的な方法はありますか(貪欲な後読みではありません)?無効な文字列はどうですか?

一般的な解決策をしばらく探してから、自分で書いた...アイデアは、リスト要素に一致するパターンに従って(区切り文字に一致するのではなく)分割することです。

私の答えは、エスケープ文字をパラメーターとして受け取りません。

public static List<String> commaDelimitedListStringToStringList(String list) {
    // Check the validity of the list
    // ex: "te\\st" is not valid, backslash should be escaped
    if (!list.matches("^(([^\\\\,]|\\\\,|\\\\\\\\)*(,|$))+")) {
        // Could also raise an exception
        return null;
    }
    // Matcher for the list elements
    Matcher matcher = Pattern
            .compile("(?<=(^|,))([^\\\\,]|\\\\,|\\\\\\\\)*(?=(,|$))")
            .matcher(list);
    ArrayList<String> result = new ArrayList<String>();
    while (matcher.find()) {
        // Unescape the list element
        result.add(matcher.group().replaceAll("\\\\([\\\\,])", "$1"));
    }
    return result;
}

パターンの説明(エスケープなし):

(?&lt; =(^ |、))は文字列の開始または

([^ \\、] | \\、| \\\\)* \、 \\ または \ でも

でもない文字

(?=(、| $))は文字列の終わりまたは

です

パターンは単純化される場合があります。

3つの解析( matches + find + replaceAll )でも、この方法はdrvdijkによって提案された方法よりも高速に見えます。特定のパーサーを作成することで最適化できます。

また、特殊な文字が1つだけの場合、エスケープ文字を使用する必要があるのは、単純に2文字にすることです...

public static List<String> commaDelimitedListStringToStringList2(String list) {
    if (!list.matches("^(([^,]|,,)*(,|$))+")) {
        return null;
    }
    Matcher matcher = Pattern.compile("(?<=(^|,))([^,]|,,)*(?=(,|$))")
                    .matcher(list);
    ArrayList<String> result = new ArrayList<String>();
    while (matcher.find()) {
        result.add(matcher.group().replaceAll(",,", ","));
    }
    return result;
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top