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

Posted

技术标签:

【中文标题】使用 String.split() 将带有引号的 csv 文件拆分为文本分隔符【英文标题】:Splitting a csv file with quotes as text-delimiter using String.split() 【发布时间】:2013-03-22 06:14:50 【问题描述】:

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

Sachin,,M,"Maths,Science,English",Need to improve in these subjects.

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

如果可能的话,现在如何使用String.split() 在逗号分隔符上拆分上述值?

【问题讨论】:

为什么要坚持使用String.split?这个例子有更好的选择吗? 【参考方案1】:

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

String[] res = str.split(",(?=([^\"]|\"[^\"]*\")*$)");

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

尽管如此,使用简单的非正则表达式解析器可能更容易。

【讨论】:

用于读取 csv 文件,它工作正常。如果你有这种类型的格式 987663,seepzBranch,"Seepz mumbai,andheri","near infra, flat no 23,raghilla mall thane",seepz, 【参考方案2】:
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));

输出:

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

【讨论】:

我花了一段时间才弄清楚这个正则表达式在做什么。如果解释它与后跟偶数个引号(或没有引号)的逗号匹配,那对我有很大帮助。所以这是可行的,因为逗号的内引号(即我们不想匹配/拆分的引号)在它们和行尾之间应该有奇数个引号。还可能值得注意的是,如果数据可以在其中转义引号,我认为这将不起作用。 这样做 s.split(',(?=([^\"]*\"[^\"]*\")*[^\"]*$)', -1 ) 如果你想在最后保留空字符串。***.com/questions/13939675/… 非常有帮助。在javascript中执行此操作时,我需要将?:添加到内部组,因此完整表达式变为s.split(/,(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/); @Ashton 这很奇怪......您最好发布一个包含完整详细信息的新问题。显示您尝试解析的整个字符串以及您正在使用的模式和结果。据我所知,此答案中的模式应该只匹配并以逗号分隔。 正则表达式的解释/可视化regexper.com/#(%3F%3D(%5B%5E%5C%22%5D*%5C%22%5B%5E%5C%22%5D*%5C%22)*%5B%5E%5C%22%5D*% 24)【参考方案3】:

由于您的问题/要求并不复杂,因此可以使用自定义方法,该方法的执行速度提高 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;
   

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

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.]

【讨论】:

-1 这没有回答问题,它专门要求使用String.split() 的解决方案。顺便说一句,由对 java 知之甚少的人编写的代码的标志之一是使用Vector 请解释为什么在这种情况下使用 ArrayList 而不是 Vector 会更有利(除了由于线程安全而导致的性能损失)。此外,你的礼貌可能需要一些工作,这是粗鲁的人的标志之一。 我没有粗鲁;只是事实。这里有一个小提示...Vector 不是线程安全的。这是一个破碎的课程,这就是为什么没有人,我真的是说没有人,在现实世界中使用它。只有初学者使用它,我的猜测是因为讲义已经过时了十年,特别是因为提倡使用 Vector 的讲师在学术界花费了太多时间来保持联系和古老的格言“如果你做不到它,教它”仍然适用。 啊哈,我自己找到了关于矢量是遗留问题的答案。谢谢,不打算再使用它了,与 regex+split 相比,您确实帮助我提高了解决方案的速度。是的,最初的问题要求拆分,但有时为通过 google 等找到此问题的人提供替代方案会有所帮助。想象一下对于这种特定情况,拆分超过 100 万或 1000 万条记录的时间差异。 嗯,速度不是一切。我坚信“代码越少越好”(原因很多——这里太多讨论)。但与其编写自己的代码(如果不使用 split()),我会先查看现有的库,对于 CSV 解析有很多。

以上是关于使用 String.split() 将带有引号的 csv 文件拆分为文本分隔符的主要内容,如果未能解决你的问题,请参考以下文章

Python - 如何在拆分字符串时忽略双引号中的空格? [复制]

为啥 String.split 需要转义管道分隔符?

在引号外的逗号上拆分

将带有单引号的字符串从 Java 插入 Postgresql

使用 innerhtml 编写带有 A LOT 引号的 html

java String.split()用法