【问题标题】:Regex to find missing double quote in csv正则表达式在 csv 中查找丢失的双引号
【发布时间】:2014-08-01 23:31:10
【问题描述】:

我们正在处理包含带有非闭合双引号条目的行的 csv 文件。这些会炸毁 csv 解析器,因此我正在尝试组合一个正则表达式来识别这些行,以便我们可以在尝试处理它们之前从文件中删除它们。

在以下示例中,csv 解析器到达第 2 行,并在尝试关闭标记之前包含直到第 3 行中第一个双引号的所有内容,然后因为“关闭”之后有非空白字符而爆炸下一个逗号前的双引号。

示例第 1 行,一些数据,“好行”,处理良好,快乐

示例第 2 行,一些数据,“坏行,处理不佳,不满意

示例第3行,一些数据,“好行”,在这里之前死了,不开心

我正在尝试做类似的事情:

.*,"[^(",)]*[\r\n]

这个想法是找到一个单行,其中任何内容都后跟 ," 没有 " 的实例,它在行结束之前跟随。

序列的否定虽然不起作用。这种事情是怎么做到的?

注意:

由于人们一直建议检查偶数个双引号,因此值得注意的是,单个双引号 csv 条目可能包含独立的双引号(例如 ...,"Measurement: 1' 2"",. ..)。

【问题讨论】:

  • 您可以使用简单的报价计数器。如果你的 (counter % 2) == 0,你很好
  • 那本来是不错的,但是是的,我将把它放在 Java 中。
  • 这个 Fede 的问题是你可以在引用的字段中包含一个双引号(例如 ...,"Measurement: 1' 2"",...
  • 这是一个真实的例子吗?所以唯一的 valid 结束引号后跟一个逗号(或行尾“?
  • ^(?:".*?"(?=,|$),?|[^",]*(?=,|$),?)+$ 怎么样?它仅匹配您的案例中的有效字符串。 Demo.

标签: java regex csv


【解决方案1】:

这样的事情应该可以工作:

^[^"]*("[^"]*"[^"]*)*[^"]*$

您看到到处重复的[^"]* 表示“任意数量的非引号字符”。
("[^"]*"[^"]*)* 将匹配成对的引号,而 [^"]*s 将匹配最终引号前后的未加引号的文本。
^$ 锚确保我们匹配整行,而不仅仅是其中的一部分。

本质上:如果有偶数个引号,它将匹配。如果有奇数个引号,就会失败。

Here's an example 的正则表达式。


如果您正在使用的任何解决方案都有选项,那么有一个更简单的方法,它不涉及正则表达式。只需计算 CSV 行中双引号的数量。如果奇怪,则该行的引号不匹配。

【讨论】:

  • 要从上面复制我的评论,问题在于您可以在带引号的字段中包含单个双引号(例如 ...,"Measurement: 1' 2"",...
【解决方案2】:

你可以使用:

int count = str.length() - str.replaceAll("\\"","").length();

if (count % 2 == 0) {
    // do what you want
}

【讨论】:

  • 要从上面复制我的评论,问题在于您可以在带引号的字段中包含单个双引号(例如 ...,"Measurement: 1' 2"",...
【解决方案3】:

根据您当前的要求(包括您对"Measurement: 1' 2"" 的关注,这将选择坏行:

^.*(?:^|,)[^",]*"(?:[^",]*(?:"[^",]*")?)+(?:$|,.*)
  1. ^ 定位在字符串的顶部
  2. .*(?:^|,) 会吃掉字符串或逗号之前的所有字符
  3. 我们匹配一个“...
  4. 并且,一次或多次,[^",]*(?:"[^",]*")? 匹配既不是 " 也不是逗号的字符,以及可选的平衡引号集:"[^",]*"
  5. 我们要么匹配字符串的结尾,要么匹配逗号和后面的任何内容

关于转义双引号的说明

您的输入中可能有包含转义双引号的双引号字符串,如下所示:"abc\"de" 如果是这样,我们需要用更可靠的内容替换双引号字符串 (?:"[^",]*") 的表达式: (?:"(?:\\"|[^"])*")

因此整个正则表达式将变为:

^.*(?:^|,)[^",]*"(?:[^",]*(?:"(?:\\"|[^"])*")?)+(?:$|,.*)

【讨论】:

  • 这不正确地匹配以下内容:citizens bank atm locations,"www.citizensbank.com/branchlocator/?search=north tonawanda, ny" 如果删除 'ny' 之前的逗号,则会导致灾难性的回溯。
  • 它还会导致灾难性的回溯,在 JavaScript 中只需 citizens bank atm locations,"www.citizensbank.com/bran"。不知何故,后面的逗号停止了回溯,但正如我所说,会导致误报。
【解决方案4】:

这是一个别人给我框架的正则表达式,最后做了一些修改:

这将匹配后面的任何内容,“之间有或没有空格,最终不是后面有一个”,(也有潜在的空白),最后以换行符结尾。

.*,[\s]*"(?!.*"[\s]*,).*\n

【讨论】:

    【解决方案5】:

    正则表达式并不能真正可靠地工作,因为有很多边缘情况。您应该尝试univocity-parsers,因为它是我所知道的唯一能够正确处理非转义引号的 CSV 解析器。

    它为您提供以下选项:

    • STOP_AT_CLOSING_QUOTE - 如果在输入中找到未转义的引号,则累积引号字符并继续将值解析为带引号的值,直到找到结束引号。

    • STOP_AT_DELIMITER - 如果在输入中找到未转义的引号,则将该值视为未加引号的值。这将使解析器累积所有字符,直到在输入中找到分隔符或行尾。

    • SKIP_VALUE - 如果在输入中找到未转义的引号,则为找到下一个分隔符解析的内容,一切都会产生null

    • RAISE_ERROR - 如果在输入中找到未转义的引号,则引发异常

    像这样使用它:

    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 许可证)

    【讨论】:

      猜你喜欢
      • 2013-10-01
      • 1970-01-01
      • 2019-04-15
      • 1970-01-01
      • 2015-01-22
      • 2013-08-27
      • 1970-01-01
      • 2015-10-13
      • 2011-09-15
      相关资源
      最近更新 更多