uniVocity-parsers:速度最快功能最全的CSV开发库之一

univocity-parsers

uniVocity-parsers是一组可靠快速的Java解析器集合,包含多种文件类型的处理接口,为开发新解析器提供一个坚实的框架。

介绍

该项目最初由 uniVocity软件 开发。这是一家澳大利亚公司,开发了Java商业数据集成 API uniVocity

在使用中发现,解析器在灵活性、大数据和多种输入格式方面存在明显不足。此外,对新格式进行扩展支持方面也十分困难。

于是,我们决定从头开始构建新的架构,旨在方便地为新解析器扩展提供更好的性能和更佳的灵活性。

解析器(Parser)

uniVocity-parsers 目前支持的解析器:

  • CSV文件(你能找到的最快的Java解析器)
  • 固定宽度格式文件
  • TSV文件

我们会不断加入更多的解析器。CSV解析器可以处理很多分隔符格式,比如竖线分隔、CSV的子类型等。接下来,uniVocity-parsers会支持更多格式。如果有需要支持的格式,可以向 parsers@univocity.com 发邮件。我们会根据大家的要求加入新的解析器。

我们还提供了每个类的说明,可以根据你的需要自己创建自定义解析器。我们还为构建解析器提供商业支持(可以向 support@univocity.com 发邮件)。

安装

这里下载jar文件。

也可以使用maven,在 pom.xml 中添加下列依赖:

需求背景

uniVocity-parsers实现了以下功能需求:

  1. 支持读写表格格式的文本文件,尤其是
    • 1.1 CSV文件
    • 1.2 固定宽度文件
    • 1.3 TSV文件
  2. 支持通用非标准函数,例如
    • 2.1 File注释
    • 2.2 部分读取
    • 2.3 跳跃读取
  3. 列选择
  4. 基于注解映射,带数据转换功能
  5. 处理边界情况,例如字段内容支持多行和换行
  6. 并行处理输入

下面是非功能性需求:

  1. 快速、灵活
  2. 无外部依赖
  3. 易于使用
  4. 为不同的解析器提供一致性接口
  5. 灵活可配置
  6. 超级快速,内存占用低:是的,在细节之处进行优化。
  7. 提供灵活可扩展的架构:用大约200行代码完成自己的解析器。

示例

CSV读操作

下面的示例,会使用 example.csv 作为输入文件。这远没有看上去那么简单。一些现有的其它CSV解析器都无法正确读取下面的文件。

所有解析器都是java.io.Reader的实例,你可看到诸如 getReader("/examples/example.csv") 的调用。这是用来构建示例的帮助方法(源码参见这里):

让我们开始吧!

读取CSV中的所有行(最快速简便的方法)

输出:

读取CSV中所有行(基于迭代器 iterator)

跳过转义字符

在CSV文件中,值内部的引号必须跳过。例如,[\”] 的内容包含了一个引号。但如果引用的值内部已反斜线结尾要怎么处理?这种情况下需要跳过该转义符。比如下面展示的 escape.csv 内容:

要正确解析,需要定义CharToEscapeQuoteEscaping

这样,上面的例子会正确地解析为:

读取CSV中所有行(高级版)

要更好地控制解析过程,可以使用RowProcessoruniVocity-parsers会提供一些好用的默认实现,但是你也可以自己定义。

下面的示例中使用了 RowListProcessor,将文件中的内容读取后存储到List:

每一行包含:

你还可以使用ObjectRowProcessor,这个类会生成行内容对象。你可以使用Conversion接口进行转换。Conversions类提供了一些有用的默认值。方便起见,ObjectRowListProcessor用来将所有行对象存储到List。

转换后的输出:

使用注解映射Java Bean

使用Parsed注解可以将CSV文件中的属性与字段建立映射。可以用标题中声明的字段名映射属性,也可以用输入中的列索引号进行映射。

每个注解操作都可以映射一个Conversion,按照他们声明的顺序执行。

下面的示例使用这个csv文件测试:bean_test.csv

使用 BeanProcessor 和 BeanListProcessor创建注解类的实例:

这里是每个 TestBean 实例调用 toString() 方法的输出:

使用注解自定义转换

Conversion的实现类都可以被Parsed注解标记的字段使用。 下面这个类将分隔后的字符串(在读取时)转换为一组单词,(在写入时)把一组单词转换为带分隔符的字符串。要实现这个功能,需要为你的类引入带varargs的构造函数, 初始化时用String... args作为参数:

回到之前的例子,让我们为example.csv记录中的Car对象进行修改。现在,我们想要对 description 字段的内容进行提取,转为一组单词。方法如下:

uniVocity-parsers会用给定的参数创建WordsToSetConversion的实例。现在,让我们用之前的 BeanListProcessor 解析文件并生成 Car 列表。

到这里,会得到带有描述的car对象。输出:

主从关系文件读操作

使用MasterDetailProcessorMasterDetailListProcessor可以生成MasterDetailRecord对象。一个简单的主从关系文件:master_detail.csv

每个MasterDetailRecord存储了记录的主信息以及关联的详细信息。

打印主信息和详细信息,输出:

固定宽度文件解析

CSV文件解析的所有功能都可以在固定宽度文件解析器上找到(同样也适用于后面介绍的其它解析器)。

在固定宽度example.txt文件中,我们为没有文字的空格加上了下划线 (‘_’)。在解析器配置中,我们设置下划线作为填充:

只需要初始化一个不同的解析器:

使用FixedWidthFieldLengths在输入中定义每个字段的长度。有了这个信息,就可以创建FixedWidthParserSettings

输出:

接下来与CSV解析器一样。可以用RowProcessor处理注解、转换、主从记录和任何你希望加入的功能。

我们在FixedWidthParserExamples.java文件中创建了固定宽度文件解析示例。

TSV文件解析

要解析TSV文件,只要使用一个TsvParser。正如我们提到的,所有解析器的API基本上是一致的。输入:

解析器代码:

输出:

列选择

在不关心某些列的情况下,解析整个文档是对CPU和内存资源的浪费。uniVocity-parsers可以选择你关心的列,其它的值会直接忽略。

下面的示例可以在SettingsExamples中找到:

例如example.csv文件:

选择:

输出:

可以通过索引达到同样的效果。

你也可以保持原来带所有列的行信息,但只输出你关心的值:

输出:

按列读取

uniVocity-parsers 1.3.0版本开始,引入一些特殊类型的RowProcessor,支持按列非按行读取数据:

为了避免大文件读取带来的内存问题,我们引入了下面的列处理器。处理完给定的行信息后,会返回对应列的值:

下面是使用示例:

从CSV文件中解析列

让我们看看输出。每行都显示了列名和对应的值:

为固定宽度输入批量列处理

下面每3行打印一组输出。由于我们有5行内容,最后一批打印2行:

读取TSV文件转换成Object对象

现在,打印列索引和对应的值:

并行处理列

在uniVocity-parsers 1.4.0中,可像线程那样对行进行处理。所有的工作就是将RowProcessor包装为ConcurrentRowProcessor

注意,这么做并不总会加快处理速度。uniVocity-parsers经过了高度优化,通常顺序执行会比并发执行更快。推荐在使用这个功能钱对自己的使用场景进行分析,而不是盲目并行。

解析单个字符串

如果想要从文件读取每一行内容,然后逐条进行处理,可以使用 parseLine(String) 方法。 下面的例子用来解析TSV内容:

输出:

配置

每个解析器都有自己的配置类,但是很多配置选项对所有解析器是通用的。下面的脚本展示了如何使用每个选项:

上面的设置输出结果如下:

固定宽度设置

根据上述配置解析器会对example.txt给出以下输出:

由于 recordEndsOnNewline = true,第3行和第4行被看做不同的记录,而非一条多行记录。进一步的解释:第4行,第一列的值为‘air’,第二列的值为’moon’,第三列的值为’roof, loaded_4799.00_’。

格式设置

所有解析器都有默认格式,下面这些属性适用于所有的解析器:

  • lineSeparator (默认为System.getProperty(“line.separator”);):标识行结束的字符数组,可能包含1个或2个字符。通过这个参数,可以处理不同操作系统中编辑的文件。当然,你也可以自定义行分隔符,比如”#$”。
  • normalizedNewline(默认为 \n): 表示行分隔符序列(例如 Windows下是 \r\n )。这个设置会被 parser/writer 用来处理可移植的行分隔符。
    • 在解析过程中,如果发现 lineSeparator 中定义的字符,会自动替换为normalizedNewline 字符。
    • 在写入过程中,normalizedNewline 会替换为 lineSeparator 序列。
  • comment (默认为 #): 如果第一个字符匹配了注释符,那么这一行会作为注释不做解析。

CSV格式

  • delimiter (默认为 ,):区分输入中的字段。
  • quote (默认为 ): 引号,字段内容包含在引号中(例如 ” a , b ” 会被解析为 a , b)。
  • quoteEscape (默认为 ):用来取消转义符,当转义符作为字段值出现时需要用该字符取消转义(例如” “” a , b “” ” 会解析为 " a , b ")。

固定宽度格式

除了默认的格式定义,固定宽度文件格式还包含:

  • padding (默认为 ‘ ‘):填写空白的地方的字符。

TSV格式

TSV格式可以设置默认转义符,比如包含了 \n、\r、\t 和 \.的值。

  • escapeChar (默认\): 用来转义TSV文件中的特殊字符。

写操作

正如你在WriterExamples.java中看到的那样,写操作非常直观。所有需要做的就是创建一个 java.io.Writer 示例(将你提供的内容写入到输出的资源中)并且准备好写入时采用的配置。

简单快捷的写操作示例

可以用下面3行代码将数据作为CSV格式写入:

得到下面输出:

如果要将相同的内容写为固定宽度格式的文件,只要创建一个 FixedWidthWriter 实例。其余的代码和上面一样。

以后介绍的其它writer/parser用法与上面相同。

写TSV文件示例

下面的示例与CSV文件完全一样,需要做的只是初始化新的writer:

输出:

逐行带注释的写操作

输出:

列选择的写操作

在保持输出格式一致性的同时,可以“透明地”将一些字段写入CSV文件。假设你有一个CSV文件包含了5列,但是只有3列有数据而且顺序不同。只需要为文件配置标题信息并选择需要写值的字段即可。

输出:

带值转换的写操作(使用ObjectRowWriterProcessor)

所有writer都有一个settings对象,可以接受 RowWriterProcessor 实例。writer可以调用带processRecord”前缀的方法,为输入的内容调用 RowWriterProcessor

下面的例子中,会为行对象中的每个元素执行 ObjectRowWriterProcessor 进行自定义值转换。该对象在将元素写入钱会调用一组 Conversion 方法。

输出:

带注解的Java Bean写操作

如果为类的字段使用 com.univocity.parsers.annotations 包中定义的注解,可以通过 BeanWriterProcessor 将字段直接映射到输出。

RowWriterProcessor 是一个接口,可以“知道”如何将给定对象银蛇到值序列。默认的情况下,uniVocity-parsers会提供 BeanWriterProcessor 将带注解的bean映射到行。

下面的示例展示了如何编写 TestBean 实例:

上面的代码执行结果如下:

逐个写入值

写入数据时,如果没有整行信息,可以逐行定义写值。

输出:

// 行分隔符确保了像MacOS和Windows这样的系统    // 能够正确处理文件(MacOS使用’\r’;Windows使用uses ‘\r\n’)

开源地址:https://github.com/uniVocity/univocity-parsers

打赏支持我整理更多优质资源,谢谢!

打赏编辑

打赏支持我整理更多优质资源,谢谢!

任选一种支付方式

1 2 收藏

资源整理者简介:唐尤华

做自己喜欢的,编程、喝茶、看世界 个人主页 · 贡献了108个资源 · 18 ·    


直接登录

推荐关注

按分类快速查找

关于资源导航
  • 伯乐在线资源导航收录优秀的工具资源。内容覆盖开发、设计、产品和管理等IT互联网行业相关的领域。目前已经收录 1439 项工具资源。
    推送伯乐头条热点内容微信号:jobbole 分享干货的技术类微信号:iProgrammer