【问题标题】:CSV parsing with Commons CSV - Quotes within quotes causing IOException使用 Commons CSV 解析 CSV - 引号内的引号导致 IOException
【发布时间】:2017-11-25 14:31:11
【问题描述】:

我正在使用 Commons CSV 解析与电视节目相关的 CSV 内容。其中一个节目的节目名称包含双引号;

116,6,2,29 Sep 10,""JJ" (60 min)","http://www.tvmaze.com/episodes/4855/criminal-minds-6x02-jj"

节目名称是“JJ”(60 分钟),它已经用双引号括起来了。这是抛出一个 IOException java.io.IOException: (line 1) invalid char between encapsulated token and delimiter。

    ArrayList<String> allElements = new ArrayList<String>();
    CSVFormat csvFormat = CSVFormat.DEFAULT;
    CSVParser csvFileParser = new CSVParser(new StringReader(line), csvFormat);

    List<CSVRecord> csvRecords = null;

    csvRecords = csvFileParser.getRecords();

    for (CSVRecord record : csvRecords) {
        int length = record.size();
        for (int x = 0; x < length; x++) {
            allElements.add(record.get(x));
        }
    }

    csvFileParser.close();
    return allElements;

CSVFormat.DEFAULT 已设置 withQuote('"')

我认为这个 CSV 格式不正确,因为“”JJ“(60 分钟)”应该是“”“JJ”“(60 分钟)”——但是有没有办法让 commons CSV 来处理这个或做我需要手动修复这个条目吗?

附加信息:其他节目名称在 CSV 条目中包含空格和逗号,并放在双引号内。

【问题讨论】:

    标签: java csv double-quotes apache-commons-csv


    【解决方案1】:

    我认为在同一个标​​记中同时包含引号和空格会使解析器感到困惑。试试这个:

    CSVFormat csvFormat = CSVFormat.DEFAULT.withQuote('"').withQuote(' ');
    

    这应该可以解决它。


    示例

    对于您的输入行:

    String line = "116,6,2,29 Sep 10,\"\"JJ\" (60 min)\",\"http://www.tvmaze.com/episodes/4855/criminal-minds-6x02-jj\"";
    

    输出是(并且没有抛出异常):

    [116, 6, 2, 29 Sep 10, ""JJ" (60 min)", "http://www.tvmaze.com/episodes/4855/criminal-minds-6x02-jj"]
    

    【讨论】:

    • withQuote('"') 已由 DEFAULT 设置。不幸的是,该空格是合法条目,并且包含在 CSV 的其他各种行中,因此我无法将其视为引号字符.
    • @mhollander38 空间在我的格式下仍然是合法的。我将添加一个示例和输出。
    • 两个withQuote() 调用应该如何工作?后者不会覆盖第一个吗?
    【解决方案2】:

    引用主要允许字段包含分隔符。如果字段中嵌入的引号没有被转义,这将不起作用,因此使用引号没有任何意义。如果您的示例值为“JJ”,60 分钟,解析器如何知道逗号是字段的一部分?数据格式无法可靠地处理嵌入的逗号,因此如果您希望能够做到这一点,最好更改源以生成符合 RFC 的 csv 格式。

    否则,看起来数据源只是用引号将非数字字段括起来,并用逗号分隔每个字段,因此解析器需要执行相反的操作。您可能应该将数据视为逗号分隔,并自己使用 removeStart/removeEnd 去除前导/尾随引号。

    您可能会使用 CSVFormat .withQuote(null),或者忘记它而只使用 String .split(',')

    【讨论】:

    • 设置 withQuote(null) 确实绕过了 IO 异常,但是,它会传递不理想的引号,这也意味着显示像“我爱你,汤米布朗(60 分钟) ” 并没有通过“我爱你并且失去了名字的其余部分。我在我的问题中并不清楚许多节目名称包含逗号并由来源放在引号内。
    • 在这种情况下,有什么办法可以调整输入格式吗?如果您想继续使用逗号作为分隔符而不是数据中不存在的内容(例如制表符),但又不想生成符合 rfc 的 csv,那么解析器将如何知道如何解析名为:“JJ”,60分钟
    • @mic univocity-parsers 是我所知道的唯一可以解析此类输入的库,请参阅我的答案。
    【解决方案3】:

    这里的问题是引号没有正确转义。您的解析器无法处理。试试univocity-parsers,因为这是我所知道的唯一可以处理引用值内的非转义引号的java解析器。它也比 Commons CSV 快 4 倍。试试这个代码:

    //configure the parser to handle your situation
    CsvParserSettings settings = new CsvParserSettings();
    settings.setUnescapedQuoteHandling(STOP_AT_CLOSING_QUOTE);
    
    //create the parser
    CsvParser parser = new CsvParser(settings);
    
    //parse your line
    String[] out = parser.parseLine("116,6,2,29 Sep 10,\"\"JJ\" (60 min)\",\"http://www.tvmaze.com/episodes/4855/criminal-minds-6x02-jj\"");
    
    for(String e : out){
        System.out.println(e);
    }
    

    这将打印:

    116
    6
    2
    29 Sep 10
    "JJ" (60 min)
    http://www.tvmaze.com/episodes/4855/criminal-minds-6x02-jj
    

    希望对你有帮助。

    披露:我是这个库的作者,它是开源免费的(Apache 2.0 许可)

    【讨论】:

      【解决方案4】:

      您可以使用 withEscape('\\') 忽略引号内的引号

      CSVFormat csvFormat = CSVFormat.DEFAULT.withEscape('\\')

      参考:https://commons.apache.org/proper/commons-csv/apidocs/org/apache/commons/csv/CSVFormat.html

      【讨论】:

        【解决方案5】:

        不需要特殊的解析器:只需在双引号前添加双引号即可:

        116,6,2,29 Sep 10,"""JJ"" (60 min)",...
        

        这一切都在 RFC 4180 中指定

           7.  If double-quotes are used to enclose fields, then a double-quote
           appearing inside a field must be escaped by preceding it with
           another double quote.  For example:
        
           "aaa","b""bb","ccc"
        

        这已经被CSVFormat #DEFAULT实现了。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-05-13
          • 2016-07-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-06-26
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多