【问题标题】:split a string at comma but avoid escaped comma and backslash以逗号分隔字符串,但避免转义逗号和反斜杠
【发布时间】:2014-03-09 00:32:31
【问题描述】:

我想用逗号"," 分割一个字符串。该字符串包含转义的逗号"\," 和转义的反斜杠"\\"。开头和结尾的逗号以及连续的几个逗号应该导致空字符串。

所以",,\,\\,," 应该变成"""""\,\\"""""

请注意,我的示例字符串将反斜杠显示为单个 "\"。 Java 字符串会使它们翻倍。

我尝试了几个包,但没有成功。我的最后一个想法是编写自己的解析器。

【问题讨论】:

  • 这是my answer 来自另一个具有类似要求的问题。它处理连续多个\ 的情况。但是,正如 fge 所建议的,您最好使用库,因为我的代码是在不了解 CSV 格式的极端情况的情况下编写的。
  • 感谢您的建议。我会看看它。尽管如此,我希望我的项目对其他工件的依赖项尽可能少(番石榴和 Apache Commons 都可以)。可能这个问题是唯一需要该库的问题。所以我宁愿不使用它。

标签: java regex string escaping


【解决方案1】:

在这种情况下,自定义函数对我来说听起来更好。试试这个:

public String[] splitEscapedString(String s) {
    //Character that won't appear in the string.
    //If you are reading lines, '\n' should work fine since it will never appear.
    String c = "\n";
    StringBuilder sb = new StringBuilder();
    for(int i = 0;i<s.length();++i){
        if(s.charAt(i)=='\\') {
            //If the String is well formatted(all '\' are followed by a character),
            //this line should not have problem.
            sb.append(s.charAt(++i));                
        }
        else {
            if(s.charAt(i) == ',') {
                sb.append(c);
            }
            else {
                sb.append(s.charAt(i));
            }
        }
    }
    return sb.toString().split(c);
}

【讨论】:

    【解决方案2】:

    不要使用.split(),而是查找(未转义的)逗号之间的所有匹配项:

    List<String> matchList = new ArrayList<String>();
    Pattern regex = Pattern.compile(
        "(?:         # Start of group\n" +
        " \\\\.      # Match either an escaped character\n" +
        "|           # or\n" +
        " [^\\\\,]++ # Match one or more characters except comma/backslash\n" +
        ")*          # Do this any number of times", 
        Pattern.COMMENTS);
    Matcher regexMatcher = regex.matcher(subjectString);
    while (regexMatcher.find()) {
        matchList.add(regexMatcher.group());
    } 
    

    结果:["", "", "\\,\\\\", "", ""]

    我使用了possessive quantifier (++) 以避免由于嵌套量词导致的过度回溯。

    【讨论】:

      【解决方案3】:

      当然,专用库是一个好主意,但以下将起作用

          public static String[] splitValues(final String input) {
              final ArrayList<String> result = new ArrayList<String>();
              // (?:\\\\)* matches any number of \-pairs
              // (?<!\\) ensures that the \-pairs aren't preceded by a single \
              final Pattern pattern = Pattern.compile("(?<!\\\\)(?:\\\\\\\\)*,");
              final Matcher matcher = pattern.matcher(input);
              int previous = 0;
              while (matcher.find()) {
                  result.add(input.substring(previous, matcher.end() - 1));
                  previous = matcher.end();
              }
              result.add(input.substring(previous, input.length()));
              return result.toArray(new String[result.size()]);
          }
      

      想法是找到,,前缀为无或偶数\(即未转义,),因为,是模式的最后一部分,位于end()-1之前,

      针对除null-input 之外我能想到的大多数可能性对功能进行了测试。如果您更喜欢处理List&lt;String&gt;,当然可以更改退货;我刚刚采用了split() 中实现的模式来处理转义。

      使用此函数的示例类:

      import java.util.ArrayList;
      import java.util.regex.Matcher;
      import java.util.regex.Pattern;
      
      public class Print {
          public static void main(final String[] args) {
              String input = ",,\\,\\\\,,";
              final String[] strings = splitValues(input);
              System.out.print("\""+input+"\" => ");
              printQuoted(strings);
          }
      
          public static String[] splitValues(final String input) {
              final ArrayList<String> result = new ArrayList<String>();
              // (?:\\\\)* matches any number of \-pairs
              // (?<!\\) ensures that the \-pairs aren't preceded by a single \
              final Pattern pattern = Pattern.compile("(?<!\\\\)(?:\\\\\\\\)*,");
              final Matcher matcher = pattern.matcher(input);
              int previous = 0;
              while (matcher.find()) {
                  result.add(input.substring(previous, matcher.end() - 1));
                  previous = matcher.end();
              }
              result.add(input.substring(previous, input.length()));
              return result.toArray(new String[result.size()]);
          }
      
          public static void printQuoted(final String[] strings) {
              if (strings.length > 0) {
                  System.out.print("[\"");
                  System.out.print(strings[0]);
                  for(int i = 1; i < strings.length; i++) {
                      System.out.print("\", \"");
                      System.out.print(strings[i]);
                  }
                  System.out.println("\"]");
              } else {
                  System.out.println("[]");
              }
          }
      }
      

      【讨论】:

        【解决方案4】:

        我使用下面的解决方案来处理带有引号(' 和 ")和转义(\)字符的通用字符串拆分器。

        public static List<String> split(String str, final char splitChar) {
            List<String> queries = new ArrayList<>();
            int length = str.length();
            int start = 0, current = 0;
            char ch, quoteChar;
            
            while (current < length) {
                ch=str.charAt(current);
                // Handle escape char by skipping next char
                if(ch == '\\') {
                    current++;
                }else if(ch == '\'' || ch=='"'){ // Handle quoted values
                    quoteChar = ch;
                    current++;
                    while(current < length) {
                        ch = str.charAt(current);
                        // Handle escape char by skipping next char
                        if (ch == '\\') {
                            current++;
                        } else if (ch == quoteChar) {
                            break;
                        }
                        current++;
                    }
                }else if(ch == splitChar) { // Split sting
                    queries.add(str.substring(start, current + 1));
                    start = current + 1;
                }
                current++;
            }
            // Add last value
            if (start < current) {
                queries.add(str.substring(start));
            }
            return queries;
        }
        
        public static void main(String[] args) {
        
            String str = "abc,x\\,yz,'de,f',\"lm,n\"";
            List<String> queries = split(str, ',');
            System.out.println("Size: "+queries.size());
            for (String query : queries) {
                System.out.println(query);
            }
        }
        

        得到结果

        Size: 4
        abc,
        x\,yz,
        'de,f',
        "lm,n"
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-03-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多