【问题标题】:Split string which contains escaped delimiters拆分包含转义分隔符的字符串
【发布时间】:2014-05-03 13:34:21
【问题描述】:

分隔符是|

转义字符是\

字符串例如是"A|B\|C\\|D\\\|E|\\\\F"

我想得到数组: {"A", "B|C\", "D\|E", "\\F"}

所以分隔符可以转义,但转义字符也可以转义。有人知道如何用 Java 解析这个吗?

谢谢。

编辑: 我创建了这个看起来很糟糕的解决方案。至少它工作得很好,并且可以定义转义字符、分隔符以及是否应该轻松删除空字符串。

解决方案(Eggyal 贴出更好的,往下看):

private List<String> parseString(String string, String delimiter, boolean removeEmpty) {
    String escapingChar = "\\";
    String escapingCharInRegexp = "\\\\";
    boolean begined = false;
    List<String> parsed = new ArrayList<String>();
    List<Integer> begins = new ArrayList<Integer>();
    List<Integer> ends = new ArrayList<Integer>();
    List<Integer> delimitersPositions = new ArrayList<Integer>();
    List<String> explodedParts = new ArrayList<String>();
    int i;
    for(i = 0; i < string.length(); i++) {
        if( ( string.substring(i, i+1).equals(escapingChar) || string.substring(i, i+1).equals(delimiter) ) && !begined ) {
            begins.add(i);
            begined = true;
            if( i + 1 == string.length() ) {
                begined = false;
                ends.add(i+1);
            }
        } else if( ( !string.substring(i, i+1).equals(escapingChar) && !string.substring(i, i+1).equals(delimiter) && begined ) ) {
            begined = false;
            ends.add(i);
        } else if( begined && string.substring(begins.get(begins.size()-1), i).indexOf(delimiter) != -1 ) {
            begined = false;
            ends.add(i);
            begined = true;
            begins.add(i);
        } 
        if( ( i + 1 == string.length() && begined ) ) {
            begined = false;
            ends.add(i+1);
        }
    }
    List<Integer> toRemove = new ArrayList<Integer>();
    for( i = 0; i < begins.size(); i++ ) {
        if( string.substring(begins.get(i), ends.get(i)).indexOf(delimiter) == -1 ) {
            toRemove.add(i);
        }
    }
    for( i = 0; i < toRemove.size(); i++ ) {
        begins.remove(toRemove.get(i)-i);
        ends.remove(toRemove.get(i)-i);
    }       
    for( i = 0; i < begins.size(); i++ ) {
        if( ( ends.get(i) - begins.get(i) ) % 2 != 0 ) {
            delimitersPositions.add(ends.get(i)-1);
        }
    }       
    for( i = 0; i <= delimitersPositions.size(); i++ ) {
        int start = (i == 0) ? 0 : delimitersPositions.get(i-1)+1;
        int end = ( i != delimitersPositions.size()) ? delimitersPositions.get(i) : string.length();
        if( removeEmpty ) {
            if( !string.substring(start, end).equals("") ) {
                explodedParts.add(string.substring(start, end));
            }
        } else {
            explodedParts.add(string.substring(start, end));

        }
    }
    for (i = 0; i < explodedParts.size(); i++)
        parsed.add(explodedParts.get(i).replaceAll(escapingCharInRegexp+"(.)", "$1"));

    return parsed;
}

【问题讨论】:

    标签: java parsing escaping delimiter


    【解决方案1】:
    static final char ESCAPING_CHAR = '\\';
    
    private List<String> parseString(final String  str,
                                     final char    delimiter,
                                     final boolean removeEmpty)
      throws IOException
    {
      final Reader        input  = new StringReader(str);
      final StringBuilder part   = new StringBuilder();
      final List<String>  result = new ArrayList<String>();
    
      int c;
      do {
        c = input.read();                // get the next character
    
        if (c != delimiter) {            // so long as it isn't a delimiter...
          if (c == ESCAPING_CHAR)        //   if it's an escape
            c = input.read();            //     use the following character instead
    
          if (c >= 0) {                  //   only if NOT at end of string...
            part.append((char) c);       //     append to current part
            continue;                    //     move on to next character
          }
        }
    
        /* we're at either a real delimiter, or end of string => part complete */
    
        if (part.length() > 0 || !removeEmpty) { // keep this part?
          result.add(part.toString());   // add current part to result
          part.setLength(0);             // reset for next part
        }
      } while (c >= 0);                  // repeat until end of string found
    
      return result;
    }
    

    【讨论】:

    • 谢谢,但例如 A|\|返回 [A]。我希望 [A, |] 因为第二个分隔符被转义所以它是普通字符。
    • @1daemon1:我的错。无论其内容如何,​​它都没有将最后一部分添加到结果中。请参阅我修改后的答案。
    • 非常棒。这个效果很好,看起来不错。非常感谢。
    • 也感谢您的解释。
    【解决方案2】:

    因为您既要拆分 非转义,所以每个过程都需要单独的步骤:

    String[] terms = input.split("(?<=[^\\\\]|[^\\\\]\\\\\\\\)\\|");
    for (int i = 0; i < terms.length; i++)
        terms[i] = terms[i].replaceAll("\\\\(.)", "$1");
    

    这是一些测试代码:

    public static void main(String[] args) {
        String input = "A|B\\|C\\\\|D\\\\\\|E|\\\\\\\\F";
        String[] terms = input.split("(?<=[^\\\\]|[^\\\\]\\\\\\\\)\\|");
        for (int i = 0; i < terms.length; i++)
            terms[i] = terms[i].replaceAll("\\\\(.)", "$1");
        System.out.println(input);
        System.out.println(Arrays.toString(terms));
    }
    

    输出:

    A|B\|C\\|D\\\|E|\\\\F
    [A, B|C\, D\|E, \\F]
    

    【讨论】:

    • 谢谢,有趣的解决方案。但是例如这个输入 "\\\\|\\\\\\\\|\\\\\\\\\\\\" 返回 [\|\\|\\] 我希望 [\, \ \, \\]
    • 我在帖子中添加了解决方案。
    【解决方案3】:

    Java 中没有像您提到的“\|”那样的转义序列。 这会导致编译时错误。

    【讨论】:

    • 我的字符串已经写成它见编译器。上面的示例在 Java 代码中如下所示: String example = "A|B\\|C\\\\|D\\\\\\|E|\\\\\\\\F"; .我需要的转义只是一些“规则”如何在数组和字符串之间解析。
    最近更新 更多