Skip to content

Commit f7c2ca2

Browse files
committed
CSV-135 - Char escape doesn't work
Ensure escape chars are escaped when using quote mode
1 parent 53fa8ad commit f7c2ca2

2 files changed

Lines changed: 31 additions & 8 deletions

File tree

src/main/java/org/apache/commons/csv/CSVFormat.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1443,6 +1443,10 @@ private void printWithQuotes(final Object object, final CharSequence value, fina
14431443

14441444
final char delimChar = getDelimiter();
14451445
final char quoteChar = getQuoteCharacter().charValue();
1446+
// If escape char not specified, default to the quote char
1447+
// This avoids having to keep checking whether there is an escape character
1448+
// at the cost of checking against quote twice
1449+
final char escapeChar = isEscapeCharacterSet() ? getEscapeCharacter().charValue() : quoteChar;
14461450

14471451
QuoteMode quoteModePolicy = getQuoteMode();
14481452
if (quoteModePolicy == null) {
@@ -1480,7 +1484,7 @@ private void printWithQuotes(final Object object, final CharSequence value, fina
14801484
} else {
14811485
while (pos < end) {
14821486
c = value.charAt(pos);
1483-
if (c == LF || c == CR || c == quoteChar || c == delimChar) {
1487+
if (c == LF || c == CR || c == quoteChar || c == delimChar || c == escapeChar) {
14841488
quote = true;
14851489
break;
14861490
}
@@ -1522,14 +1526,11 @@ private void printWithQuotes(final Object object, final CharSequence value, fina
15221526
// the need for encapsulation.
15231527
while (pos < end) {
15241528
final char c = value.charAt(pos);
1525-
if (c == quoteChar) {
1529+
if (c == quoteChar || c == escapeChar) {
15261530
// write out the chunk up until this point
1527-
1528-
// add 1 to the length to write out the encapsulator also
1529-
out.append(value, start, pos + 1);
1530-
// put the next starting position on the encapsulator so we will
1531-
// write it out again with the next string (effectively doubling it)
1532-
start = pos;
1531+
out.append(value, start, pos);
1532+
out.append(escapeChar); // now output the escape
1533+
start = pos; // and restart with the matched char
15331534
}
15341535
pos++;
15351536
}

src/test/java/org/apache/commons/csv/CSVPrinterTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,4 +1512,26 @@ public void testPrintReaderWithoutQuoteToAppendable() throws IOException {
15121512
}
15131513
assertEquals(content, sb.toString());
15141514
}
1515+
1516+
private void tryFormat(List<String> l, Character quote, Character escape, String expected) throws IOException {
1517+
CSVFormat format = CSVFormat.DEFAULT.withQuote(quote).withEscape(escape).withRecordSeparator(null);
1518+
Appendable out = new StringBuilder();
1519+
CSVPrinter printer = new CSVPrinter(out, format);
1520+
printer.printRecord(l);
1521+
printer.close();
1522+
assertEquals(expected, out.toString());
1523+
}
1524+
1525+
@Test
1526+
public void testCSV135() throws IOException {
1527+
List<String> l = new LinkedList<String>();
1528+
l.add("\"\""); // ""
1529+
l.add("\\\\"); // \\
1530+
l.add("\\\"\\"); // \"\
1531+
tryFormat(l, null, null, "\"\",\\\\,\\\"\\"); // "",\\,\"\ (unchanged)
1532+
tryFormat(l, '"', null, "\"\"\"\"\"\",\\\\,\"\\\"\"\\\""); // """""",\\,"\""\" (quoted, and embedded DQ doubled)
1533+
tryFormat(l, null, '\\', "\"\",\\\\\\\\,\\\\\"\\\\"); // "",\\\\,\\"\\ (escapes escaped, not quoted)
1534+
tryFormat(l, '"', '\\', "\"\\\"\\\"\",\"\\\\\\\\\",\"\\\\\\\"\\\\\""); // "\"\"","\\\\","\\\"\\" (quoted, and embedded DQ & escape escaped)
1535+
tryFormat(l, '"', '"', "\"\"\"\"\"\",\\\\,\"\\\"\"\\\""); // """""",\\,"\""\" (quoted, embedded DQ escaped)
1536+
}
15151537
}

0 commit comments

Comments
 (0)