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

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

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

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

因此,如果有一个本地支持字符串分隔符的库,那将是很好的,因此不需要预处理。 这意味着CSV现在标准为“CharSequence-Separated Values”数据。 🙂

这是一个很好的问题。 在我查看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. 使用“自己动手”来首先validation数据。 拆分每一行并certificate它包含必要数量的字段。
  2. 使用“字段忽略”方法来解析经过validation的数据,确保已经指定了正确数量的字段。

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

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

这些解决方案都不适用于我,因为他们都假设您可以将整个CSV文件存储在内存中,以便轻松replaceAll类型的操作。

我知道它很慢,但我选择了Scanner 。 它具有惊人数量的function,并使用您想要的任何字符串作为记录分隔符来滚动您自己的简单CSV阅读器。 它还允许您解析非常大的CSV文件(之前我已经完成了10GB的单个文件),因为您可以一次读取一个记录。

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

我更喜欢更快的解决方案,但我找不到支持它的库。 自2017年初以来,FasterXML已经有一个开放的票据来添加这个function: https : //github.com/FasterXML/jackson-dataformats-text/issues/14