【问题标题】:How to avoid backslash before comma in CSVFormat如何在 CSVFormat 中避免逗号前的反斜杠
【发布时间】:2021-07-05 14:34:37
【问题描述】:

我在 java 中使用 CSVFormat 创建一个 CSV 文件,我在标题和值中面临的问题是每当字符串很长并且有逗号时,api 总是在逗号之前插入一个 \。结果,标头没有正确形成,并且 csv 文件中的值将下一个单元格用于 .我正在发布我所做的代码

   try (CSVPrinter csvPrinter = new CSVPrinter(out,
            CSVFormat.DEFAULT.withHeader("\""+SampleEnum.MY_NAME.getHeader()+"\"", "\""+SampleEnum.MY_TITLE.getHeader()+"\"",
                    "\""+SampleEnum.MY_ID.getHeader()+"\"", "\""+SampleEnum.MY_NUMBER.getHeader()+"\"", "\""+SampleEnum.MY_EXTERNAL_KEY.getHeader()+"\"",
                    "\""+SampleEnum.DATE.getHeader()+"\"","\""+SampleEnum.MY_ACTION.getHeader()+"\"",
                    "\"\"\""+SampleEnum.MY__DEFI.getHeader()+"\"\"\"", SampleEnum.MY_ACTION.getHeader(),
                    SampleEnum.CCHK.getHeader(), SampleEnum.DISTANCE_FROM_LOCATION.getHeader(),
                    SampleEnum.TCOE.getHeader(), SampleEnum.HGTR.getHeader(),SampleEnum._BLANK.getHeader(),
                    SampleEnum.LOCATION_MAP.getHeader(), SampleEnum.SUBMISSION_ID.getHeader())                      
                    .withDelimiter(',').withEscape('\\').withQuote('"').withTrim().withQuoteMode(QuoteMode.MINIMAL)
    )) {
        sampleModel.forEach(sf -> {
            try {
                csvPrinter.printRecord(sf.getMyName(),
                        sf.getMyTitle(),
                        sf.getMyID(),
                        sf.getMyNo(),

所以现在的问题是我得到这样的输出

"\"Name:\"","\"Title\"","\"ID #:\"","\"Store #:\"","\"Store #: External Key\"","\"Date:\"","\"\"\"It's performance  issue in detail to include dates,times, circumstances, etc.\"\"\""

我在每个逗号之前得到 \,当它出现在值中时,文本的下一部分将转移到下一个单元格。

我需要的输出是

"Name:","Title:","Employee ID #:","Store #:","Store #: CurrierKey","Date:","Stage of Disciplinary Action:","""Describe your view about the company, times, circumstances, etc.""",

我正在尝试 https://commons.apache.org/proper/commons-csv/jacoco/org.apache.commons.csv/CSVFormat.java.html 这个链接,但我无法理解修复。请帮忙。

【问题讨论】:

  • 你觉得.withEscape('\\') 做什么?
  • @talex 我用它来避免在我的输出中使用 Quotemode NONE 额外的双引号
  • @Matt 使用 CSVFormat.RFC4180.withHeader( 但结果相同
  • @Mandrek 我怀疑它也负责逃避你想避免的事情。

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


【解决方案1】:

发生这种情况是因为您使用的是 QuoteMode.NONE,它具有以下 Javadoc:

从不引用字段。当分隔符出现在数据中时,打印机会在其前面加上转义字符。如果未设置转义字符,格式验证会抛出异常。

您可以使用QuoteMode.MINIMAL 仅引用包含特殊字符的字段(例如字段分隔符、引号字符或行分隔符字符串的字符)。


我建议您使用CSVFormat.DEFAULT,然后如果您不能使用其他格式之一,请自行配置所有内容。检查反斜杠 (\) 是否真的是您的用例的正确转义字符。通常它是双引号 (")。此外,您可能希望从标题定义中删除所有双引号,因为它们会根据您的配置自动添加(如果需要)。

StringBuilder out = new StringBuilder();
try (CSVPrinter csvPrinter = new CSVPrinter(out,
        CSVFormat.DEFAULT
                .withHeader("AAAA", "BB\"BB", "CC,CC", "DD'DD")
                .withDelimiter(',')
                .withEscape('\\') // <- maybe you want '"' instead
                .withQuote('"').withRecordSeparator('\n').withTrim()
                .withQuoteMode(QuoteMode.MINIMAL)
)) {
    csvPrinter.printRecord("WWWW", "XX\"XX", "YY,YY", "ZZ'ZZ");
}
System.out.println(out);
AAAA,"BB\"BB","CC,CC",DD'DD
WWWW,"XX\"XX","YY,YY",ZZ'ZZ

在您的edit 之后,您似乎希望所有字段都用双引号作为转义字符引用。因此,您可以像这样使用QuoteMode.ALL.withEscape('"')

StringBuilder out = new StringBuilder();
try (CSVPrinter csvPrinter = new CSVPrinter(out,
        CSVFormat.DEFAULT
                .withHeader("AAAA", "BB\"BB", "CC,CC", "\"DD\"", "1")
                .withDelimiter(',')
                .withEscape('"')
                .withQuote('"').withRecordSeparator('\n').withTrim()
                .withQuoteMode(QuoteMode.ALL)
)) {
    csvPrinter.printRecord("WWWW", "XX\"XX", "YY,YY", "\"DD\"", "2");
}
System.out.println(out);
"AAAA","BB""BB","CC,CC","""DD""","1"
"WWWW","XX""XX","YY,YY","""DD""","2"

在您的comment 中,您声明您只需要在需要时使用双引号,并且仅在一个字段中使用三引号。然后,您可以按照第一个示例中的建议使用QuoteMode.MINIMAL.withEscape('"')。当您用双引号将该字段的输入括起来时,会生成三引号(一次是因为有一个特殊字符并且需要引用该字段,第二个是因为您添加了显式 " 而第三个是为了逃避你的明确报价)。

StringBuilder out = new StringBuilder();
try (CSVPrinter csvPrinter = new CSVPrinter(out,
        CSVFormat.DEFAULT
                .withHeader("AAAA", "BB\"BB", "CC,CC", "\"DD\"", "1")
                .withDelimiter(',')
                .withEscape('"')
                .withQuote('"').withRecordSeparator('\n').withTrim()
                .withQuoteMode(QuoteMode.MINIMAL)
)) {
    csvPrinter.printRecord("WWWW", "XX\"XX", "YY,YY", "\"DD\"", "2");
}
System.out.println(out);
AAAA,"BB""BB","CC,CC","""DD""",1
WWWW,"XX""XX","YY,YY","""DD""",2

根据chat,您希望完全控制标题是否包含引号。没有QuoteMode 和转义字符的组合可以提供所需的结果。因此,我建议您手动构建标题:

StringBuilder out = new StringBuilder();
try (CSVPrinter csvPrinter = new CSVPrinter(out,
        CSVFormat.DEFAULT
                .withDelimiter(',').withEscape('"')
                .withQuote('"').withRecordSeparator('\n').withTrim()
                .withQuoteMode(QuoteMode.MINIMAL))
) {
    out.append(String.join(",", "\"AAAA\"", "\"BBBB\"", "\"CC,CC\"", "\"\"\"DD\"\"\"", "1"));
    out.append("\n");
    csvPrinter.printRecord("WWWW", "XX\"XX", "YY,YY", "\"DD\"", "2");
}
System.out.println(out);
"AAAA","BBBB","CC,CC","""DD""",1
WWWW,"XX""XX","YY,YY","""DD""",2

【讨论】:

  • 我已经更新了我的帖子,应用了您的更改,但是得到了一个在每个值中附加了反斜杠的输出
  • @Mandrek 我添加了一个示例并扩展了解释。
  • 我不太清楚您所说的 “是否可以编辑我的帖子” 是什么意思。我的回答解决了您的问题,我的示例可用于在您自己的代码中应用相同的原则。
  • 我的输出需要在输出中添加双引号和三引号的多个组合,例如我已经更新了我的示例输出,请查看更新后的帖子
  • @Mandrek 我再次扩展了我的答案以解决您的最新编辑以及如何生成三引号。
猜你喜欢
  • 1970-01-01
  • 2014-03-09
  • 1970-01-01
  • 2021-05-04
  • 1970-01-01
  • 2017-05-07
  • 2018-01-03
  • 2021-12-09
  • 1970-01-01
相关资源
最近更新 更多