【问题标题】:Java String.split() regex for handling escaped delimeter and escaped escape characters用于处理转义分隔符和转义转义字符的 Java String.split() 正则表达式
【发布时间】:2014-04-27 19:15:55
【问题描述】:
String testString = "a\\,b\\\\,c,d\\\\\\,e,f\\\\g";
String[] splitedString = test.split(PATTERN_STRING);
for (String string : splitedString) {
    System.out.println(string);
}

这里我有一个字符串,它将字符串列表编码为字符串,其中转义字符为 \,分隔符为 ,
注意:(示例中的反斜杠因 Java 代码而加倍)
反斜杠和逗号在原始字符串和结果字符串用逗号合并。我需要一个正则表达式将此字符串拆分为原始字符串列表。
所以以字符串为例

"a\,b\\,c,d\\\,e,f\\g"
我需要得到这样的字符串:
"a\,b\\"
"c"
"d\\\,e"
"f\\g"

所以拆分的逻辑很简单:仅当反斜杠的数量为偶数时才使用分隔符逗号拆分:0,2,4...仅在这种情况下,此逗号是分隔符。如果逗号前的反斜杠数量是奇数,则它是转义逗号,不应发生拆分。

任何人都可以帮助我为这种情况使用适当的正则表达式吗?

编辑
我知道这个正则表达式:(?<!\\\\), 将有助于用逗号分割字符串,而逗号之前没有反斜杠。但在我的情况下,如果逗号之前的斜线数量是偶数,我也需要拆分。

感谢任何帮助。

【问题讨论】:

标签: java regex string


【解决方案1】:

如果必须拆分,那么您可以尝试类似

split("(?<!(?<!\\\\)\\\\(\\\\{2}){0,1000000000}),")

我使用{0,1000000000} 而不是*,因为Java 中的look-behind 需要有明显的最大长度,而1000000000 似乎已经足够好,除非你可以有超过1000000000 连续\\ in你的文字。


如果它不必是split,那么你可以使用

Matcher m = Pattern.compile("(\\G.*?(?<!\\\\)(\\\\{2})*)(,|(?<!\\G)$)",
        Pattern.DOTALL).matcher(testString);
while (m.find()) {
    System.out.println(m.group(1));
}

\\G 表示上一个匹配的结束,或者如果这是 Matcher 的第一次迭代并且没有字符串 ^ 的上一个匹配开始。


但最快且不太难实现的方法是编写自己的解析器,它会使用escaped 之类的标志来表示当前检查的字符已用\ 转义。

public static List<String> parse(String text) {
    List<String> tokens = new ArrayList<>();
    boolean escaped = false;
    StringBuilder sb = new StringBuilder();

    for (char ch : text.toCharArray()) {
        if (ch == ',' && !escaped) {
            tokens.add(sb.toString());
            sb.delete(0, sb.length());
        } else {
            if (ch == '\\')
                escaped = !escaped;
            else
                escaped = false;
            sb.append(ch);
        }
    }

    if (sb.length() > 0) {
        tokens.add(sb.toString());
        sb.delete(0, sb.length());
    }

    return tokens;
}

所有方法的演示:

String testString = "a\\,b\\\\,c,d\\\\\\,e,f\\\\g";
String[] splitedString = testString
        .split("(?<!(?<!\\\\)\\\\(\\\\{2}){0,1000000000}),");
for (String string : splitedString) {
    System.out.println(string);
}

System.out.println("-----");
Matcher m = Pattern.compile("(\\G.*?(?<!\\\\)(\\\\{2})*)(,|(?<!\\G)$)",
        Pattern.DOTALL).matcher(testString);
while (m.find()) {
    System.out.println(m.group(1));
}

System.out.println("-----");
for (String s : parse(testString))
    System.out.println(s);

输出:

a\,b\\
c
d\\\,e
f\\g
-----
a\,b\\
c
d\\\,e
f\\g
-----
a\,b\\
c
d\\\,e
f\\g

【讨论】:

    【解决方案2】:

    试试,

    我只是通过使用字符串操作来实现你的逻辑。

    String string = "a\\,b\\\\,c,d\\\\\\,e,f\\\\g";
    String finalString = "";
    for(String i : string.split(",")){
        int count = 0;
        for(int j=0;j<i.length();j++)
            if(i.charAt(j) == '\\')
                count++;    
        finalString+=(count%2==0)?","+i+"$":","+i;
    } 
    for(String finalAns: finalString.split("\\$")){
        System.out.println(finalAns.replaceFirst(",", ""));
    }
    

    输出:

    a\,b\\
    c
    d\\\,e
    f\\g
    

    【讨论】:

      【解决方案3】:

      这将为您解决问题:

      String[] splitedString = testString.split("(?<=[a-z])(\\\\{2})*,");
      

      这里,它使用字符串中的偶数(0 是偶数)\,后跟, 作为分割分隔符。

      使用lookbehind,检查\之前的最后一个字符是否是字母。

      【讨论】:

      • 这也将消耗,之前的最后几个\,所以第一个结果将是"a\,b"而不是"a\,b\\"
      猜你喜欢
      • 2011-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-05
      • 1970-01-01
      • 1970-01-01
      • 2010-12-22
      相关资源
      最近更新 更多