关于正则表达式:Java:拆分以逗号分隔的字符串,但忽略引号中的逗号

Java: splitting a comma-separated string but ignoring commas in quotes

我有一根类似这样的绳子:

1
foo,bar,c;qual="baz,blurb",d;junk="quux,syzygy"

我想用逗号分割,但我需要忽略引号中的逗号。我该怎么做?似乎regexp方法失败了;我想当我看到一个引号时,我可以手动扫描并输入一个不同的模式,但是使用以前存在的库会更好。(编辑:我想我指的是已经是JDK的一部分或者已经是ApacheCommons等常用库的一部分的库。)

上述字符串应拆分为:

1
2
3
4
foo
bar
c;qual="baz,blurb"
d;junk="quux,syzygy"

注意:这不是一个csv文件,它是包含在一个整体结构更大的文件中的单个字符串。


试试:

1
2
3
4
5
6
7
8
9
public class Main {
    public static void main(String[] args) {
        String line ="foo,bar,c;qual="baz,blurb",d;junk="quux,syzygy"";
        String[] tokens = line.split(",(?=(?:[^"]*"[^"]*")*[^"]*$)", -1);
        for(String t : tokens) {
            System.out.println("
>"+t);
        }
    }
}

输出:

1
2
3
4
> foo
> bar
> c;qual="baz,blurb"
> d;junk="quux,syzygy"

在其他的话:分裂的逗号,逗号,如果只有零或甚至提前quotes号码信息。

最近,一位friendlier对眼睛:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Main {
    public static void main(String[] args) {
        String line ="foo,bar,c;qual="baz,blurb",d;junk="quux,syzygy"";

        String otherThanQuote =" [^"]";
        String quotedString = String.format("
" %s* "", otherThanQuote);
        String regex = String.format("
(?x)"+ // enable comments, ignore white spaces
               "
,                        "+ // match a comma
               "
(?=                      "+ // start positive look ahead
               "
 (?:                    "+ //   start non-capturing group 1
               "
   %s*                  "+ //     match 'otherThanQuote' zero or more times
               "
   %s                   "+ //     match 'quotedString'
               "
 )*                     "+ //   end group 1 and repeat it zero or more times
               "
 %s*                    "+ //   match 'otherThanQuote'
               "
 $                      "+ // match the end of the string
               "
)                        ", // stop positive look ahead
                otherThanQuote, quotedString, otherThanQuote);

        String[] tokens = line.split(regex, -1);
        for(String t : tokens) {
            System.out.println("
>"+t);
        }
    }
}

这产生的一样的第一个实例。

编辑

作为上述mikefhay城市"的评论:

I prefer using Guava's Splitter, as it has saner defaults (see discussion above about empty matches being trimmed by String#split(), so I did:

1
Splitter.on(Pattern.compile(",(?=(?:[^"]*"[^"]*")*[^"]*$)"))


在正则表达式中做样的将军,这种状态依赖于一个简单的词法分析器(相信我在这个案例是更简单的比,Word可能会使它的冰可能一声)清洁液,特别是与regards两maintainability,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
String input ="foo,bar,c;qual="baz,blurb",d;junk="quux,syzygy"";
List<String> result = new ArrayList<String>();
int start = 0;
boolean inQuotes = false;
for (int current = 0; current < input.length(); current++) {
    if (input.charAt(current) == '"') inQuotes = !inQuotes; // toggle state
    boolean atLastChar = (current == input.length() - 1);
    if(atLastChar) result.add(input.substring(start));
    else if (input.charAt(current) == ',' && !inQuotes) {
        result.add(input.substring(start, current));
        start = current + 1;
    }
}

如果你不在乎的commas保在《quotes这你可以简化方法(没有行动的启动负荷指标,没有特殊字符的实例)在你的城市replacing commas quotes什么别的城市,然后说:commas分裂

1
2
3
4
5
6
7
8
9
10
11
String input ="foo,bar,c;qual="baz,blurb",d;junk="quux,syzygy"";
StringBuilder builder = new StringBuilder(input);
boolean inQuotes = false;
for (int currentIndex = 0; currentIndex < builder.length(); currentIndex++) {
    char currentChar = builder.charAt(currentIndex);
    if (currentChar == '"') inQuotes = !inQuotes; // toggle state
    if (currentChar == ',' && inQuotes) {
        builder.setCharAt(currentIndex, ';'); // or '?', and replace later
    }
}
List<String> result = Arrays.asList(builder.toString().split(","));


分享一个http:/ / / / /项目javacsv

http:/ / / / github.com pupi1985 javacsv重装上阵。 (叉的图书馆,将原有的输出允许生两个有Windows

当终结者的线槽运行Windows)

opencsv.sourceforge.net http:/ / / / /

CSV文件的Java API

你能recommend A(Java图书馆的阅读和写作possibly)CSV文件吗?

Java库的应用程序的两个或两个convert CSV格式的XML文件。


我不建议的正则表达式从巴特答案A解析,找到更好的解决方案,在这种特殊情况下(如费边建议)。在这里我想和自己的解析解的正则表达式的执行中发现,有:

  • 解析多阿姨比冰与分裂与正则表达式的后向引用~20小时的短字符串的阿姨,阿姨~ 40小时长的字符串。
  • 正则表达式fails找到空字符串逗号后的货物。这虽然不是在原来的问题,这是我的要求。
  • 我下面的解决方案和测试。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    String tested ="foo,bar,c;qual="baz,blurb",d;junk="quux,syzygy",";
    long start = System.nanoTime();
    String[] tokens = tested.split(",(?=([^"]*"[^"]*")*[^"]*$)");
    long timeWithSplitting = System.nanoTime() - start;

    start = System.nanoTime();
    List<String> tokensList = new ArrayList<String>();
    boolean inQuotes = false;
    StringBuilder b = new StringBuilder();
    for (char c : tested.toCharArray()) {
        switch (c) {
        case ',':
            if (inQuotes) {
                b.append(c);
            } else {
                tokensList.add(b.toString());
                b = new StringBuilder();
            }
            break;
        case '"
    ':
            inQuotes = !inQuotes;
        default:
            b.append(c);
        break;
        }
    }
    tokensList.add(b.toString());
    long timeWithParsing = System.nanoTime() - start;

    System.out.println(Arrays.toString(tokens));
    System.out.println(tokensList.toString());
    System.out.printf("Time with splitting:\t%10d
    ",timeWithSplitting);
    System.out.printf("Time with parsing:\t%10d
    ",timeWithParsing);

    当然你是自由的保护开关的两个两个转变——在这段,如果你感觉uncomfortable与其丑。注:当时缺乏突破后的开关与分离器。我选择了两个StringBuilder StringBuffer的城市设计,而不是两个增长的速度,在线程安全的冰无关。


    我不impatient和选择等待回答。参考它不看,硬做一些这样的(这对我厂的应用,不需要担心escaped quotes,AA的东西在冰quotes有限的几个约束两种形式):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    final static private Pattern splitSearchPattern = Pattern.compile("[",]");
    private List<String> splitByCommasNotInQuotes(String s) {
        if (s == null)
            return Collections.emptyList();

        List<String> list = new ArrayList<String>();
        Matcher m = splitSearchPattern.matcher(s);
        int pos = 0;
        boolean quoteMode = false;
        while (m.find())
        {
            String sep = m.group();
            if ("
    "".equals(sep))
            {
                quoteMode = !quoteMode;
            }
            else if (!quoteMode &&",".equals(sep))
            {
                int toPos = m.start();
                list.add(s.substring(pos, toPos));
                pos = m.end();
            }
        }
        if (pos < s.length())
            list.add(s.substring(pos));
        return list;
    }

    (运动什么的行为:他们两个escaped quotes城市寻找反斜杠也。)


    你是在那annoying边界区域,regexps几乎不会做(AS)已经市巴特芒,逃避的生活会使硬quotes),和一个袋子,全类解析器似乎矫枉过正。

    如果你在更大的复杂性可能需要的任何时间很快会去图书馆找一个解析器。例如,这一个


    一个简单的类(?!\"),(?!\")观望。这应该是不匹配的,,"围绕的城市。


    而不是使用进位和其他疯狂的正则表达式,就上了《quotes第一。这是quote,对于每一个分组,分组与__IDENTIFIER_1replace,或一些其他的指标,分组,和映射图(两个字符串,字符串。

    当你在逗号分割,全mapped replace函数标识符与原始的字符串值。


    会做一些这样的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    boolean foundQuote = false;

    if(charAtIndex(currentStringIndex) == '"')
    {
       foundQuote = true;
    }

    if(foundQuote == true)
    {
       //do nothing
    }

    else

    {
      string[] split = currentString.split(',');  
    }