在存在转义引号时,在引号外部,或在括号外部的逗号处分割。

3

是否可以按以下条件拆分字符串?

  • 按逗号(即逗号)拆分
  • 在每个元素上,忽略第一个“和最后一个”之间的逗号
  • 在每个元素上,忽略第一个(和最后一个)之间的逗号

例如:

String source = "to_char(DATE, 'YYYY,MM,DD'), 'I am sad :(', to_char(DATE, ('YYYY(MM,DD)')), to_char(DATE, ('YYYY,MM,DD)')), to_char(DATE, ('YYYY(MM,DD')), NAME, to_char(DATE, '(YYYY)MM,DD'), CITY || ', (UK)', CITY || ', US''s CITY', CITY || ', UK'";

String[] expected = new String[]{
"to_char(DATE, 'YYYY,MM,DD')", 
"'I am sad :('",
"to_char(DATE, ('YYYY(MM,DD)'))", // brackets within quotes within brackets
"to_char(DATE, ('YYYY,MM,DD)'))", // missing open bracket in quotes
"to_char(DATE, ('YYYY(MM,DD'))", // missing close bracket in quotes
"NAME", 
"to_char(DATE, '(YYYY)MM,DD')", 
"CITY || ', (UK)'", 
"CITY || ', US''s CITY'", // escape a single quote in quotes
"CITY || ', UK'"
};

String[] result = splitElements(source);
assert expected.equals(result);

前两个要点可以通过在存在转义引号的情况下按逗号分割来实现。

在处理SQL时,这将非常有用。例如,分割项目,附加,插入,添加项目等。

提前感谢您。


您介意再给出第二和第三个要点的另一个例子吗? - ninesalt
2个回答

6

我知道这有点长,但相对比较简单,只要记住有多少个括号以及是在引号内还是外面。

String[] splitElements(String source) {
    int parencount = 0;
    boolean q = false;
    List<String> l = new ArrayList<>();
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < source.length(); i++) {
        char c = source.charAt(i);
        switch (c) {
            case ',':
                if (!q && parencount == 0) {
                    l.add(sb.toString());
                    sb.setLength(0);
                } else {
                    sb.append(c);
                }
                break;

            case '(':
                if(!q) parencount++;
                sb.append(c);
                break;

            case ')':
                if(!q) parencount--;
                sb.append(c);
                break;

            case '\'':
                q = ! q;
                sb.append(c);
                break;

            default:
                sb.append(c);
                break;
        }
    }
    String last = sb.toString();
    l.add(last);
    String sa[] = l.toArray(new String[l.size()]);
    return sa;
}

3
不要使用sb = new StringBuilder()来重置构建器,只需调用sb.setLength(0)即可。 - Andreas
如果在引号内有右括号,至少就SQL语法而言,这将会有问题。 - RealSkeptic
1
具有不平衡括号字符的字符串字面值将扰乱逻辑。添加 if (!q) round parencount++parencount-- - Andreas
现在你做得很好。最后的评论:不需要在开头将其复制到数组中,只需使用char c = source.charAt(i)。另外:定义l - Andreas
@WillShackleford 我运行了几个测试,似乎总是错过数组列表中的最后一个项目。 - Max
显示剩余9条评论

1
你可以使用Java类Stringsplit方法来完成此操作,同时结合以下正则表达式:
(?<!\([^\(\)']{0,100}),(?![^\(\)']*\))(?=(?:'[^']*'|[^'])*$)

解释:

说明:

(?<!\([^\(\)']{0,100}),(?![^\(\)']*\))

任何未被(...)包围的,,请注意通常需要有限定符的负向预测,这里我随意使用{0,100}
(?=(?:'[^']*'|[^'])*$)

使用 lookahead 来确保从 , 到字符串末尾计数的 ' 数量为偶数 (正则表达式: (?:'[^']*')*$) 或者有由任意字符组成的字符串直到字符串结尾,该字符串不包含 ' (正则表达式: [^']*$)。
策略:
  1. 使用负向前瞻和负向后顾来确保 , 不在 (...) 中,
  2. 使用 lookahead 来确保只有从 , 到字符串末尾计数的成对 '
代码示例:
public class Main{

    public static void main(String[] args) {

        String source = "to_char(DATE, 'YYYY,MM,DD'), to_char(DATE, ('YYYY(MM,DD)')), " +
                        "to_char(DATE, ('YYYY,MM,DD)')), to_char(DATE, ('YYYY(MM,DD')), " +
                        "NAME, to_char(DATE, '(YYYY)MM,DD'), CITY || ', (UK)', " +
                        "CITY || ', US''s CITY', CITY || ', UK', " +
                        "'I am sad :(', to_char(DATE, 'YYYY,MM,DD')";

        String delimiters = "(?<!\\([^\\(\\)']{0,100}),(?![^\\(\\)']*\\))(?=(?:'[^']*'|[^'])*$)";

        String[] tokens = source.split(delimiters);

        for(String token : tokens) {
            System.out.println(token.trim());
        }
    }
}

输出:

to_char(DATE, 'YYYY,MM,DD')
to_char(DATE, ('YYYY(MM,DD)'))
to_char(DATE, ('YYYY,MM,DD)'))
to_char(DATE, ('YYYY(MM,DD'))
NAME
to_char(DATE, '(YYYY)MM,DD')
CITY || ', (UK)'
CITY || ', US''s CITY'
CITY || ', UK'
'I am sad :('
to_char(DATE, 'YYYY,MM,DD')

1
非常感谢。您的答案在我测试的所有用例中都有效。我必须说这真的很聪明。还要感谢您做出如此清晰的解释。 - Max
刚注意到这并不能解决“'我很难过:(',to_char(DATE,'YYYY,MM,DD')”的拆分问题,您能否看一下是否可以改进解决方案?谢谢。 - Max
1
非常感谢。这个很有效。我还更新了原帖,加入了这种情况。 - Max
是的,它仍然有效。我以为它无效了,但我错了。干杯 - Max
@Ming 如果它不能工作,请让他更新答案,这样以后这篇文章可以帮助其他人。 - fronthem
1
谢谢。我已经测试了你们两个的解决方案。我尝试接受你们两个的解决方案,但是Stackoverflow不允许我这样做:( - Max

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接