我们正在处理包含带有非封闭双引号条目的行的csv文件。这些炸毁了csv解析器,所以我试图整理一个正则表达式来识别这些行,这样我们就可以在尝试处理它们之前从文件中删除它们。
在下面的示例中,csv解析器到达第2行并包含第3行中第一个双引号之前的所有内容,然后再尝试关闭令牌然后爆炸,因为在“结束”双引号之后有非空格字符下一个逗号。
示例第1行,一些数据,“好行”,处理得很好,很开心
示例第2行,一些数据,“坏线,处理不良,不开心
示例第3行,一些数据,“好行”,在此之前死亡,不开心
我想做的事情如下:
.*,"[^(",)]*[\r\n]
我们的想法是在行结束之前找到一行后跟“没有实例”的任何内容。
但序列的否定并不奏效。怎么样这样呢?
注意:
由于人们一直建议基本上检查偶数双引号,因此值得注意的是,单个双引号csv条目可能包含一个独立的双引号(例如......,“Measurement:1'2”“,...) 。
您可以使用:
int count = str.length() - str.replaceAll("\\"","").length();
if (count % 2 == 0) {
// do what you want
}
根据您当前的要求(包括您对"Measurement: 1' 2""
的关注,这将选择坏线:
^.*(?:^|,)[^",]*"(?:[^",]*(?:"[^",]*")?)+(?:$|,.*)
^
锚定在弦乐的顶部.*(?:^|,)
会占用字符串顶部的任何字符或逗号[^",]*(?:"[^",]*")?
匹配既不是“或逗号”的字符,也可以是一组平衡的引号:"[^",]*"
关于转义双引号的说明
在你的输入中,你可能有包含转义双引号的双引号字符串,如下所示:"abc\"de"
如果是这样的话,我们需要将双引号字符串(?:"[^",]*")
的表达式替换为更实用的字符:(?:"(?:\\"|[^"])*")
因此整个正则表达式将成为:
^.*(?:^|,)[^",]*"(?:[^",]*(?:"(?:\\"|[^"])*")?)+(?:$|,.*)
这样的事情应该有效:
^[^"]*("[^"]*"[^"]*)*[^"]*$
您看到的[^"]*
遍布整个地方意味着“任意数量的非引号字符”。
("[^"]*"[^"]*)*
将匹配成对的引号,而[^"]*
s将匹配最终引号之前和之后的未加引号的文本。
^
和$
锚点确保我们匹配整条线,而不仅仅是它的一部分。
基本上:如果有偶数引号,它将匹配。如果有奇数引号,则会失败。
正在行动的正则表达式的Here's an example。
如果您正在使用的任何解决方案都有选项,那么有一个更简单的方法不涉及正则表达式。只需计算CSV行中双引号的数量即可。如果它很奇怪,该行的引号不匹配。
这是一个正则表达式,其他人给了我框架,最终使用了一些修改:
这将匹配任何后跟的内容,“在两者之间有或没有空格,最终没有跟随”,(也有潜在的空格),最后以换行结束。
.*,[\s]*"(?!.*"[\s]*,).*\n
由于存在许多边缘情况,正则表达式并不能真正可靠地工作。您应该尝试使用univocity-parsers,因为它是我所知道的唯一可以正确处理未转义引号的CSV解析器。
它为您提供以下选项:
null
。像这样使用它:
CsvParserSettings settings = new CsvParserSettings();
settings.setUnescapedQuoteHandling(UnescapedQuoteHandling.STOP_AT_DELIMITER);
CsvParser parser = new CsvParser(settings);
for(String row[] : parser.iterate(input)){
System.out.println(Arrays.toString(row));
}
希望能帮助到你。默认情况下,它使用STOP_AT_DELIMITER
设置运行。
免责声明:我是这个图书馆的作者。它是开源和免费的(Apache 2.0许可证)