エスケープされたコンマを無視しながら、コンマ区切りの文字列を分割するにはどうすればよいですか?
質問
追加のパラメーターを取得する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;
}