【问题标题】:Parsing CSV in java在java中解析CSV
【发布时间】:2026-02-23 23:15:02
【问题描述】:

我有这种奇怪的情况,我必须横向阅读。所以我得到了一个 csv 文件,其中包含水平格式的数据。如下:

CompanyName,RunDate,10/27/2010,11/12/2010,11/27/2010,12/13/2010,12/27/2010....

在 RunDate 之后显示的所有日期都是运行日期字段的值,我必须在我的系统中为该公司更新该字段。日期值不是固定数字,它们可以是单个值到 10 到 n 个数字。所以我需要读取所有这些值并在系统中更新。我是用 Java 写的。

【问题讨论】:

  • 情况并没有你想象的那么奇怪 :)
  • OpenCSV 这样的库处理 CSV 文件的所有奇怪情况(换行、分隔等)。
  • 即使没有出现“奇怪”的情况,使用库将 (1) 减少解析错误的机会; (2) 提供更多功能; (3) 产生一个可扩展的解决方案; (4) 轻松集成对未来 CSV 文件的解析(如果需要)。

标签: java date csv


【解决方案1】:

String,split(",") 不太可能工作。
它会拆分嵌入逗号的字段(“Foo, Inc.”),即使它们是 CSV 行中的单个字段。

如果公司名称是:
公司, Inc.
或更糟:
乔的“好、快、便宜”的食物


根据*:    (http://en.wikipedia.org/wiki/Comma-separated_values)

嵌入逗号的字段必须用双引号括起来。

   1997,Ford,E350,"Super, luxurious truck"

嵌入双引号字符的字段必须用双引号字符括起来,并且每个嵌入的双引号字符必须由一对双引号字符表示。

   1997,Ford,E350,"Super ""luxurious"" truck"


更糟糕的是,引用的字段可能嵌入了换行符(换行符;“\n”):

嵌入换行符的字段必须用双引号括起来。

   1997,Ford,E350,"Go get one now  
   they are going fast"



这演示了 String,split(",") 解析逗号的问题:

CSV 行是:

a,b,c,"Company, Inc.", d, e,"Joe's ""Good, Fast, and Cheap"" Food", f, 10/11/2010,1/1/2011, g , h, 我


// Test String.split(",") against CSV with
// embedded commas and embedded double-quotes in
// quoted text strings:
//
// Company names are:
//        Company, Inc.
//        Joe's "Good, Fast, and Cheap" Food
//
// Which should be formatted in a CSV file as:
//        "Company, Inc."
//        "Joe's ""Good, Fast, and Cheap"" Food"
//
//
public class TestSplit {
    public static void TestSplit(String s, String splitchar) {
        String[] split_s    = s.split(splitchar);

        for (String seg : split_s) {
            System.out.println(seg);
        }
    }


    public static void main(String[] args) {
        String csvLine = "a,b,c,\"Company, Inc.\", d,"
                            + " e,\"Joe's \"\"Good, Fast,"
                            + " and Cheap\"\" Food\", f,"
                            + " 10/11/2010,1/1/2011, h, i";

        System.out.println("CSV line is:\n" + csvLine + "\n\n");
        TestSplit(csvLine, ",");
    }
}


产生以下内容:


D:\projects\TestSplit>javac TestSplit.java

D:\projects\TestSplit>java  TestSplit
CSV line is:
a,b,c,"Company, Inc.", d, e,"Joe's ""Good, Fast, and Cheap"" Food", f, 10/11/2010,1/1/2011, g, h, i


a
b
c
"Company
 Inc."
 d
 e
"Joe's ""Good
 Fast
 and Cheap"" Food"
 f
 10/11/2010
1/1/2011
 g
 h
 i

D:\projects\TestSplit>



该 CSV 行 应该 被解析为:


a
b
c
"Company, Inc."
 d
 e
"Joe's ""Good, Fast, and Cheap"" Food"
 f
 10/11/2010
1/1/2011
 g
 h
 i

【讨论】:

    【解决方案2】:

    正如其他人建议的拆分和解析,您可以使用opencsv

    对于简单的数据,用“,”分割它们并解析它,使用 List 来添加所有这些值。

    【讨论】:

    • -1 这将不处理字段中带有逗号的字段,这是完全有效的 CSV。在“,”上拆分在一个简单的情况下有效,但在现实情况下只是偶尔。
    • @Dave DeLong 你能详细说明你的评论吗
    • life.java 考虑这条 csv 行:"Hello,",my,name,is,Dave。它有 5 个字段:Hello,mynameisDave。您的建议将产生 6:"Hello"mynameisDave
    • @Dave Delong,是的,没错,但我现在建议盲目地为三个语句中的每一个编写代码。我刚刚给了他/她的基本想法
    【解决方案3】:

    CSV 文件是一个以\n 结尾的文件,每一列都可以通过以下方式分隔:

    • 逗号或
    • 标签\t

    我建议你有一个读取 CSV 文件的BufferedReader 并使用readLine() 方法读取行。

    在每一行中,使用String.split(arg),其中arg 将是您的逗号或制表符\t 以拥有一个列数组......从那里,您知道该怎么做。

    【讨论】:

    • C 中的 CSV 代表逗号 - google 代表 TSV 代表“制表符分隔值”
    • @Stephen P,确实如此,但是是什么阻止了任何人将选项卡放入 CSV 文件中?
    【解决方案4】:

    到目前为止,我发现的关于 CSV 解析主题的最有用的页面如下:

    http://secretgeek.net/csv_trouble.asp

    基本上,找一个成熟的库来为你做这件事,因为 csv 解析看起来很棘手。

    【讨论】:

      【解决方案5】:

      使用 java.util.Scanner - 您可以调用 useDelimiter() 将逗号作为分隔符,并使用 next() 读取新标记。 Scanner 可以直接从您的文件中创建,也可以从文件中读取字符串。

      【讨论】:

      • 这不处理列中的逗号
      【解决方案6】:

      你真的应该试试 univocity-parsers 因为它的 CSV 解析器具有许多功能来处理各种极端情况(非转义引号、混合行分隔符、BOM 编码文件等),这也是 fastest CSV libraries 周围的一种。

      解析文件的简单示例:

      CsvParserSettings settings = new CsvParserSettings(); //heaps of options here, check the docs
      CsvParser parser = new CsvParser(settings);
      
      //loads everything into memory, simple but can be slow.
      List<String[]> allRows = parser.parseAll(new File("/path/to/your.csv"));
      
      //parse iterating over each row
      for(String[] row : parser.iterate(new File("/path/to/your.csv"))){
          //process row here
      }
      
      //and many other possibilities: Java bean processing, column selection, format detection, etc.
      

      披露:我是这个库的作者。它是开源免费的(Apache V2.0 许可)。

      【讨论】:

        【解决方案7】:

        首先将整行读入一个字符串。然后,您使用 String.split(...) 函数获取您使用的定界符为“,”的行上的所有标记。 (或者当您使用正则表达式时它是“\”?)

        【讨论】:

        • 您可以拨打String.split(",")
        • 谢谢,我会尽量记住这一点,我很少使用正则表达式。
        • 人们可能会将此视为“如何解析 csv”的答案,当列本身包含逗号时,这不起作用
        【解决方案8】:

        为了一次获取一个值,使用StringTokenizer。用StringTokenizer(str, ",")构造它。(不推荐)

        使用字符串类的split() 方法,它将所有标记加载到一个数组中。

        使用DateFormat 类来解析每个日期——特别是DateFormat.parse(String)

        【讨论】:

        • 来自StringTokenizer api:StringTokenizer 是一个遗留类,出于兼容性原因保留,但不鼓励在新代码中使用它。建议任何寻求此功能的人改用 String 的 split 方法或 java.util.regex 包。
        • :尴尬:我可能应该在推荐答案之前查看文档。
        • @Qwerky - 我讨厌他们丢掉了一个非常好的课程 - 但你是对的。
        • 我的猜测是它不是线程安全的,但我根本没有研究过。
        • 这行不通,因为 CSV 格式比逗号分隔的值更复杂。
        【解决方案9】:

        java.time

        假设您使用 CSV 库来读取文件,并假设您从该库中获取单个值作为字符串:

            String valueFromCsvLibrary = "10/27/2010";
            try {
                LocalDate date = LocalDate.parse(valueFromCsvLibrary, dateFormatter);
                System.out.println("Parsed date: " + date);
            } catch (DateTimeParseException dtpe) {
                System.err.println("Not a valid date: " + dtpe);
            }
        
        Parsed date: 2010-10-27
        

        您应该更愿意在代码中将日期处理为 LocalDate(既不是字符串,也不是长期过时且设计不佳的 Date 类的实例)。

        尽管我没有经验,但我坚信我会使用一些开源 CSV 库。

        仅当您确定 CSV 文件不包含引号、虚线、值中的逗号或其他复杂情况,并且出于某种原因您选择手动解析时:

            String lineFromCsvFile = "CompanyName,RunDate,10/27/2010,11/12/2010,11/27/2010,12/13/2010,12/27/2010";
            String[] values = lineFromCsvFile.split(",");
            if (values[1].equals("RunDate")) {
                for (int i = 2; i < values.length; i++) {
                    LocalDate date = LocalDate.parse(values[i], dateFormatter);
                    System.out.println("Parsed date: " + date);
                }
            }
        
        Parsed date: 2010-10-27
        Parsed date: 2010-11-12
        Parsed date: 2010-11-27
        Parsed date: 2010-12-13
        Parsed date: 2010-12-27
        

        异常处理如前所述,无需重复。

        【讨论】: