带有字符串分隔符(多字符)的Java CSV解析器

问题描述 投票:0回答:6

是否有任何Java开源库支持CSV的多字符(即长度> 1的字符串)分隔符(定界符)?

根据定义,CSV = 以单个字符 (',') 作为分隔符的逗号分隔值数据。然而,存在许多其他单字符替代方案(例如制表符),使 CSV 代表“字符分隔值”数据(本质上是 DSV:分隔符分隔值数据)。

主要的 CSV Java 开源库(例如,OpenCSV)几乎支持任何字符作为分隔符,但不支持字符串(多字符)分隔符。因此,对于用“|||”等字符串分隔的数据除了预处理输入以将字符串转换为单字符分隔符之外,没有其他选择。从那时起,数据可以被解析为单字符分隔值。

因此,如果有一个原生支持字符串分隔符的库就好了,这样就不需要预处理了。这意味着 CSV 现在代表“CharSequence-Separated Values”数据。 :-)

java csv delimiter separator
6个回答
5
投票

这是个好问题。这个问题对我来说并不明显,直到我查看了 javadocs 并意识到 opencsv 仅支持字符作为分隔符,而不是字符串......

这里有一些建议的解决方法(Groovy 中的示例可以转换为 java)。

忽略隐式中间字段

继续使用 OpenCSV,但忽略空字段。显然这是一个作弊行为,但它对于解析行为良好的数据来说效果很好。

    CSVParser csv = new CSVParser((char)'|')

    String[] result = csv.parseLine('J||Project report||"F, G, I"||1')

    assert result[0] == "J"
    assert result[2] == "Project report"
    assert result[4] == "F, G, I"
    assert result[6] == "1"

    CSVParser csv = new CSVParser((char)'|')

    String[] result = csv.parseLine('J|||Project report|||"F, G, I"|||1')

    assert result[0] == "J"
    assert result[3] == "Project report"
    assert result[6] == "F, G, I"
    assert result[9] == "1"

自己动手

使用 Java String tokenizer 方法。

    def result = 'J|||Project report|||"F, G, I"|||1'.tokenize('|||')

    assert result[0] == "J"
    assert result[1] == "Project report"
    assert result[2] == "\"F, G, I\""
    assert result[3] == "1"

这种方法的缺点是您无法忽略引号字符或转义分隔符..

更新

为什么不将上述两种方法结合在一个两步过程中,而不是预处理数据,改变其内容:

  1. 使用“自己动手”首先验证数据。分割每一行并证明它包含所需数量的字段。
  2. 使用“字段忽略”方法来解析经过验证的数据,确保已指定正确数量的字段。

效率不是很高,但可能比编写自己的 CSV 解析器更容易:-)


2
投票

2022年openCSV版本5.7.1仍然不支持多字符分隔符。

解决方案-使用appache commons-csv,1.9.0版本支持多字符分隔符!

CSVFormat.Builder.create().setDelimiter(分隔符);


1
投票

这些解决方案都不适合我,因为它们都假设您可以将整个 CSV 文件存储在内存中,从而可以轻松执行

replaceAll
类型操作。

我知道这很慢,但我还是选择了

Scanner
。它具有数量惊人的功能,并且可以使用您想要的任何字符串作为记录分隔符来滚动您自己的简单 CSV 阅读器。它还可以让您解析非常大的 CSV 文件(我以前做过 10GB 的单个文件),因为您可以一次读取一个记录。

Scanner s = new Scanner(inputStream, "UTF-8").useDelimiter(">|\n");

我更喜欢更快的解决方案,但我发现没有库支持它。自 2017 年初以来,FasterXML 已开放申请添加此功能:https://github.com/FasterXML/jackson-dataformats-text/issues/14


1
投票

使用分隔符 || 的解决方法:在所需列之间添加虚拟字段

public class ClassName {
    @CsvBindByPosition(position = 0)
    private String column1;
    @CsvBindByPosition(position = 1)
    private String dummy1;
    @CsvBindByPosition(position = 2)
    private String column2;
    @CsvBindByPosition(position = 3)
    private String dummy2;
    @CsvBindByPosition(position = 4)
    private String column3;
    @CsvBindByPosition(position = 5)
    private String dummy5;
    @CsvBindByPosition(position = 6)
    private String column4;
}
And then parse them using 
List<ClassName> responses = new CsvToBeanBuilder<ClassName>(new FileReader("test.csv"))
                .withType(ClassName.class)
                .withSkipLines(1) // to skip header
                .withSeparator('|')
                // to parse || , we use |
                .build()
                .parse();

0
投票

尝试univocity-parsers,它支持多字符分隔符并且具有最佳性能。

对于 commons-csv:如果输入流是 gzip 流,commons-csv 1.10.0 将错误地解析由多个字符分隔的列,因此请谨慎使用。


-1
投票

尝试opencsv

它可以完成您需要的一切,包括(尤其是)处理带引号的值中的嵌入分隔符(例如

"a,b", "c"
解析为
["a,b", "c"]

我已经成功使用它并且我喜欢它。

编辑:

由于 opencsv 仅处理单字符分隔符,因此您可以解决此问题:

String input;
char someCharNotInInput = '|';
String delimiter = "abc"; // or whatever
input.replaceAll(delimiter, someCharNotInInput);
new CSVReader(input, someCharNotInInput); // etc
// Put it back into each value read
value.replaceAll(someCharNotInInput, delimiter); // in case it's inside delimiters
© www.soinside.com 2019 - 2024. All rights reserved.