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

xj3cbfub  于 2022-12-06  发布在  Java
关注(0)|答案(5)|浏览(347)

是否有Java开源库支持CSV的多字符(即长度大于1的String)分隔符(定界符)?
根据定义,CSV =以单一字符(',')作为分隔符号的逗号分隔值数据。然而,也有许多其他的单一字符替代(例如,Tab),使CSV代表“字符分隔值”数据(本质上,DSV:分隔符分隔值数据)。
主要的Java CSV开源库(例如OpenCSV)几乎支持任何字符作为分隔符,但不支持字符串(多字符)分隔符。|||“除了预处理输入以将字符串转换为单字符分隔符之外,没有其他选择。从那时起,数据可以被解析为单字符分隔值。
因此,如果有一个支持字符串分隔符的库就更好了,这样就不需要预处理了。这意味着CSV现在代表“CharSequence-Separated Values”数据。

olqngx59

olqngx591#

这是一个很好的问题。这个问题对我来说并不明显,直到我看了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字符串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.首先使用“你自己的卷”来验证数据。分割每一行,并证明它包含所需数量的字段。
1.使用“字段忽略”方法解析已验证的数据,确保已指定正确的字段数。
效率不是很高,但可能比编写自己的CSV解析器更容易:-)

falq053o

falq053o2#

这些解决方案对我都不起作用,因为它们都假设您可以将整个CSV文件存储在内存中,从而允许轻松的replaceAll类型操作。
我知道它很慢,但我选择了Scanner。它有很多令人惊讶的特性,并且可以用任何字符串作为记录分隔符来滚动你自己的简单CSV读取器。它还可以让你解析非常大的CSV文件(我以前解析过10GB的单个文件),因为你可以一次读取一条记录。

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

我更喜欢一个更快的解决方案,但是我发现没有一个库支持它。FasterXML从2017年初就有一个开放的门票来添加这个功能:https://github.com/FasterXML/jackson-dataformats-text/issues/14

fhg3lkii

fhg3lkii3#

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

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();
jw5wzhpr

jw5wzhpr4#

在2022 openCSV版本5.7.1仍然不支持多字符分隔符。
解决方案-使用appachecommons-csv,版本1.9.0支持多字符分隔符!
创建().setDelimiter(分隔符);

20jt8wwn

20jt8wwn5#

请尝试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

相关问题