如何在Java中读取CSV文件-Iterator和Decorator的案例研究

在本文中,我将讨论如何使用Apache Common CSV读取CSV(逗号分隔值)文件。 从这个案例研究中,我们将学习如何在设计模式的上下文中使用IteratorDecorator来提高不同情况下的可重用性。 但是在开始之前,我想我必须先回答两个问题。

  1. 如果有太多关于如何读取CSV文件的DIY帖子,我为什么需要第三方库?
    的确,当您使用Google“ java csv解析器”时,您将获得一些相关的帖子。 但是,即使您是初学者,也不会对这些肤浅的方法感到满意。 当然使用BufferedReaderStringsplit()将成功解析一个典型的CSV文件,但是除了使它多余之外,您将不会从中学到任何东西。 另一方面,就像我将在下面显示的那样,使用和研究Apache Common CSV将教您Design Pattern中的几个主题,例如迭代器和装饰器。
  2. 为什么选择Apache Common CSV,而不是其他?
    据我所知,Sourceforge或Google代码上还有其他几个库。 但是,如果您仔细研究他们的代码细节,请原谅我的批评,但是它们都不是灵活且易于管理的:有些过于简单,无法满足用户的各种要求;有些则过于简单。 其他人则太复杂且难以使用。 此外,我遇到的大多数人都没有商业友好型许可证。 您知道,有时确实会使用户感到恐惧。

Apache Common CSV仍在沙箱中,这意味着当前没有官方下载和稳定版本。 但是, 夜间构建可能可用。

使用迭代器隐藏基础表示

让我从一个示例CSV文件开始,其中每个记录位于单独的一行中,并以换行符分隔。 第一行是标题,其中包含与文件中的字段相对应的两个名称COL1COL2 。 文件的其余部分包含三个记录,各字段之间用逗号分隔。

COL1,COL2
a,b
c,d
e,f

使用Apache Common CSV读取此文件的代码是:

public void test() throws FileNotFoundException, IOException {
  CSVParser parser = new CSVParser(
      new FileReader("test.csv"), 
      CSVFormat.DEFAULT.withHeader());
  for (CSVRecord record : parser) {
    System.out.printf("%s\t%s\n", 
      record.get("COL1"), 
      record.get("COL2"));
  }
  parser.close();
}

CSVParser用于根据指定的格式解析CSV文件。 在这里,我使用默认的CSVFormat以及设置不带参数的withHeader() 。 这样,解析器就可以将CSV文件的第一行作为标题,并使record.get("COL1")有效。 CSVParser提供了一种读取记录的迭代方式。 在这里,我们遇到了第一个设计模式Iterator 。 它提供了一种顺序访问CSV文件记录而不暴露其底层表示的方法,例如如何跳过注释行以及如何将列名映射到字段值。 对于每个记录,我们使用CSVRecord.get(String name)来按字段名称检索字段值。

CSVRecord提供了多种访问字段值的方式:按名称或按索引。 如果不确定该字段是否有值或为空, CSVRecord.isSet(String name)可以在之前调用CSVRecord.isSet(String name) 。 如果只想检查解析器是否定义了名称,则调用CSVRecord.isMapped(String name)

使用装饰器允许不同的行为

CSVFormat.DEFAULTCSVFormat.RFC4180遵循RFC4180格式。 因此,用双引号引起来的字段也可以处理,例如

"COL1","COL2"
"a","b"
"c","d"
"e","f"

RFC4180中 ,CSV文件中的字段应以逗号分隔。 但是通常,该库可以处理任意分隔符,例如TAB或空格。 为了使代码可重复使用,该库提供了一种创建自己的CSVFormat的方法

CSVFormat format = CSVFormat.newFormat(',')
    .withQuoteChar('"')
    .withHeader();

上面的格式与CSVFormat.DEFAULT相同。 在这里,我们遇到了另一个设计模式Decorator ,它允许将行为静态或动态地添加到单个对象中,而不会影响同一类中其他对象的行为。 在CSVFormat的情况下,每个withXXX()方法都返回一个新的CSVFormat ,它与调用方相同,但修改了一个属性。 这里的问题可能是为什么不只返回自引用this ? 我认为这是因为后面的方法将使以下代码失败

CSVFormat format = CSVFormat.newFormat(',');
CSVFormat format1 = format.withQuoteChar('"');
CSVFormat format2 = format.withHeader();

如果仅返回thisformat1将等于format2 ,这绝对是我们现在所期望的。

CSVFormat提供了非常灵活的方式来指定CSV格式。 可以在其javadoc中找到详细信息,该文档有据可查。 我们可以设置定界符,注释开始标记,引号字符等。因此,对于以下CSV文件,其中用TAB分隔字段,并以#开头注释,

COL1    COL2
# comments
a       b
c       d
e       f

我们可以创建一个格式

CSVFormat format = CSVFormat.newFormat('\t')
    .withCommentStart('#')
    .withIgnoreEmptyLines(true)
    .withNullString("")
    .withHeader();

总之,开始使用Apache Common CSV来统一一个通用和简单的界面,以便在ASL许可下读写CSV文件。 它仍然在沙箱中,但是可以灵活地满足不同的需求。 最后,我想强调的是,阅读复杂的代码确实有助于提高编程技能。 因此,强烈建议您阅读此项目源代码,它非常简单但功能强大。


翻译自: https://www.javacodegeeks.com/2013/10/how-to-read-csv-files-in-java-a-case-study-of-iterator-and-decorator.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值