使用String.split()将带引号的csv文件拆分为文本分隔符

Far*_*018 51 java csv split

我有一个逗号分隔文件,其中许多行类似于下面的一行.

Sachin,,M,"Maths,Science,English",Need to improve in these subjects.
Run Code Online (Sandbox Code Playgroud)

引号用于转义用于表示多个值的分隔符逗号.

现在如何使用String.split()if来分割逗号分隔符上的上述值?

Ach*_*Jha 172

public static void main(String[] args) {
    String s = "Sachin,,M,\"Maths,Science,English\",Need to improve in these subjects.";
    String[] splitted = s.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)");
    System.out.println(Arrays.toString(splitted));
}
Run Code Online (Sandbox Code Playgroud)

输出:

[Sachin, , M, "Maths,Science,English", Need to improve in these subjects.]
Run Code Online (Sandbox Code Playgroud)

  • +1这是一个非常酷的正则表达式.我之前没见过,但它确实有效!我觉得这太好了我会奖励你一个赏金:):注意:赏金过程需要很多天才能完成) (15认同)
  • 我花了一段时间才弄清楚这个正则表达式在做什么.如果有一个解释是它匹配逗号后跟偶数引号(或没有引号),那将对我有很大的帮助.所以这是有效的,因为逗号的内部引号(即我们不想匹配/拆分的引号)应该在它们和行尾之间有奇数引号.值得注意的是,我认为如果数据可以逃脱其中的引用,这将不起作用. (6认同)
  • 这样做 s.split(',(?=([^\"]*\"[^\"]*\")*[^\"]*$)', -1) 如果你想保留空字符串最后。http://stackoverflow.com/questions/13939675/java-string-split-i-want-it-to-include-the-empty-strings-at-the-end (2认同)

Men*_*los 17

由于您的问题/要求并不是那么复杂,因此可以使用自定义方法,其执行速度提高20倍以上并产生相同的结果.这是基于数据大小和解析行数的变量,对于使用正则表达式的更复杂问题是必须的.

import java.util.Arrays;
import java.util.ArrayList;
public class SplitTest {

public static void main(String[] args) {

    String s = "Sachin,,M,\"Maths,Science,English\",Need to improve in these subjects.";
    String[] splitted = null;

 //Measure Regular Expression
    long startTime = System.nanoTime();
    for(int i=0; i<10; i++)
    splitted = s.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)");
    long endTime =   System.nanoTime();

    System.out.println("Took: " + (endTime-startTime));
    System.out.println(Arrays.toString(splitted));
    System.out.println("");


    ArrayList<String> sw = null;        
 //Measure Custom Method
            startTime = System.nanoTime();
    for(int i=0; i<10; i++)
    sw = customSplitSpecific(s);
    endTime =   System.nanoTime();

    System.out.println("Took: " + (endTime-startTime));
    System.out.println(sw);         
}

public static ArrayList<String> customSplitSpecific(String s)
{
    ArrayList<String> words = new ArrayList<String>();
    boolean notInsideComma = true;
    int start =0, end=0;
    for(int i=0; i<s.length()-1; i++)
    {
        if(s.charAt(i)==',' && notInsideComma)
        {
            words.add(s.substring(start,i));
            start = i+1;                
        }   
        else if(s.charAt(i)=='"')
        notInsideComma=!notInsideComma;
    }
    words.add(s.substring(start));
    return words;
}   
Run Code Online (Sandbox Code Playgroud)

}

在我自己的电脑上,这会产生:

Took: 6651100
[Sachin, , M, "Maths,Science,English", Need to improve in these subjects.]

Took: 224179
[Sachin, , M, "Maths,Science,English", Need to improve in these subjects.]
Run Code Online (Sandbox Code Playgroud)

  • 请解释为什么在这种情况下使用ArrayList而不是Vector会更有利(除了由于线程安全性而导致的性能损失).此外,你的礼貌可以使用一些工作,这是一个粗鲁的人的标志之一. (9认同)
  • 啊哈,我自己找到了关于向量是传统的答案。谢谢,不要再打算使用它了,与regex + split相比,您确实帮助我提高了解决方案的速度。是的,原始问题要求拆分,但对于通过google等找到此问题的人来说也有替代选择有时会有所帮助。请想象一下针对这种特定情况拆分100万或1000万条记录的时间差异。 (3认同)
  • 我并没有无礼;仅仅是事实。这里有一个小提示...“Vector”*不是*线程安全的。这是一个损坏的类,这就是为什么没有人,我真的是说*没有人*,在现实世界中使用它。只有完全的初学者才使用它,我的猜测是因为讲义已经过时了十年,特别是因为提倡使用 Vector 的讲师在学术界花费了太多时间来保持联系以及古老的格言“如果你做不到”它,教它”仍然成立。 (2认同)
  • 嗯,速度并不是一切。我坚信“代码越少越好”(原因有很多——太多了,无法在此讨论)。但我不会编写自己的代码(如果不使用 `split()`),我会首先查看现有的库,对于 CSV 解析,有很多库。 (2认同)

How*_*ard 6

如果您的字符串都是格式良好的,则可以使用以下正则表达式:

String[] res = str.split(",(?=([^\"]|\"[^\"]*\")*$)");
Run Code Online (Sandbox Code Playgroud)

该表达式确保仅在逗号处发生拆分,该逗号后跟偶数(或零)引号(因此不在此类引号内).

然而,使用简单的非正则表达式解析器可能更容易.