Skip to content

Commit 190390b

Browse files
committed
[CSV-171] Negative numeric values in the first column are always quoted
in minimal mode.
1 parent 1023690 commit 190390b

3 files changed

Lines changed: 60 additions & 13 deletions

File tree

src/changes/changes.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
</properties>
4040
<body>
4141
<release version="1.5" date="2016-MM-DD" description="Bug fix release">
42+
<action issue="CSV-171" type="fix" dev="ggregory" due-to="Gary Gregory, Michael Graessle, Adrian Bridgett">Negative numeric values in the first column are always quoted in minimal mode.</action>
4243
<action issue="CSV-187" type="update" dev="ggregory" due-to="Gary Gregory">Update platform requirement from Java 6 to 7.</action>
4344
<action issue="CSV-189" type="add" dev="ggregory" due-to="Peter Holzwarth, Gary Gregory">CSVParser: Add factory method accepting InputStream.</action>
4445
<action issue="CSV-190" type="add" dev="ggregory" due-to="Gary Gregory">Add convenience API CSVFormat.print(File, Charset)</action>

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,8 +1035,8 @@ private void printAndQuote(final Object object, final CharSequence value, final
10351035
} else {
10361036
char c = value.charAt(pos);
10371037

1038-
// TODO where did this rule come from?
1039-
if (newRecord && (c < '0' || c > '9' && c < 'A' || c > 'Z' && c < 'a' || c > 'z')) {
1038+
// RFC4180 (https://tools.ietf.org/html/rfc4180) TEXTDATA = %x20-21 / %x23-2B / %x2D-7E
1039+
if (newRecord && (c < 0x20 || c > 0x21 && c < 0x23 || c > 0x2B && c < 0x2D || c > 0x7E)) {
10401040
quote = true;
10411041
} else if (c <= COMMENT) {
10421042
// Some other chars at the start of a value caused the parser to fail, so for now

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

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@
5353
*/
5454
public class CSVPrinterTest {
5555

56+
private static final char DQUOTE_CHAR = '"';
57+
private static final char BACKSLASH_CH = '\\';
5658
private static final char QUOTE_CH = '\'';
5759
private static final int ITERATIONS_FOR_RANDOM_TEST = 50000;
5860

@@ -184,13 +186,13 @@ private String randStr() {
184186
ch = ',';
185187
break;
186188
case 6:
187-
ch = '"';
189+
ch = DQUOTE_CHAR;
188190
break;
189191
case 7:
190192
ch = '\'';
191193
break;
192194
case 8:
193-
ch = '\\';
195+
ch = BACKSLASH_CH;
194196
break;
195197
default:
196198
ch = (char) r.nextInt(300);
@@ -296,7 +298,7 @@ public void testEscapeBackslash1() throws IOException {
296298
try (final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(QUOTE_CH))) {
297299
printer.print("\\");
298300
}
299-
assertEquals("'\\'", sw.toString());
301+
assertEquals("\\", sw.toString());
300302
}
301303

302304
@Test
@@ -306,7 +308,6 @@ public void testEscapeBackslash2() throws IOException {
306308
printer.print("\\\r");
307309
}
308310
assertEquals("'\\\r'", sw.toString());
309-
310311
}
311312

312313
@Test
@@ -324,7 +325,7 @@ public void testEscapeBackslash4() throws IOException {
324325
try (final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(QUOTE_CH))) {
325326
printer.print("\\\\");
326327
}
327-
assertEquals("'\\\\'", sw.toString());
328+
assertEquals("\\\\", sw.toString());
328329
}
329330

330331
@Test
@@ -333,7 +334,52 @@ public void testEscapeBackslash5() throws IOException {
333334
try (final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(QUOTE_CH))) {
334335
printer.print("\\\\");
335336
}
336-
assertEquals("'\\\\'", sw.toString());
337+
assertEquals("\\\\", sw.toString());
338+
}
339+
340+
@Test
341+
public void testEscapeNull1() throws IOException {
342+
StringWriter sw = new StringWriter();
343+
try (final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withEscape(null))) {
344+
printer.print("\\");
345+
}
346+
assertEquals("\\", sw.toString());
347+
}
348+
349+
@Test
350+
public void testEscapeNull2() throws IOException {
351+
StringWriter sw = new StringWriter();
352+
try (final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withEscape(null))) {
353+
printer.print("\\\r");
354+
}
355+
assertEquals("\"\\\r\"", sw.toString());
356+
}
357+
358+
@Test
359+
public void testEscapeNull3() throws IOException {
360+
StringWriter sw = new StringWriter();
361+
try (final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withEscape(null))) {
362+
printer.print("X\\\r");
363+
}
364+
assertEquals("\"X\\\r\"", sw.toString());
365+
}
366+
367+
@Test
368+
public void testEscapeNull4() throws IOException {
369+
StringWriter sw = new StringWriter();
370+
try (final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withEscape(null))) {
371+
printer.print("\\\\");
372+
}
373+
assertEquals("\\\\", sw.toString());
374+
}
375+
376+
@Test
377+
public void testEscapeNull5() throws IOException {
378+
StringWriter sw = new StringWriter();
379+
try (final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withEscape(null))) {
380+
printer.print("\\\\");
381+
}
382+
assertEquals("\\\\", sw.toString());
337383
}
338384

339385
@Test
@@ -490,7 +536,7 @@ public void testJdbcPrinterWithResultSetMetaData() throws IOException, ClassNotF
490536
@Test
491537
@Ignore
492538
public void testJira135_part1() throws IOException {
493-
final CSVFormat format = CSVFormat.DEFAULT.withRecordSeparator('\n').withQuote('"').withEscape('\\');
539+
final CSVFormat format = CSVFormat.DEFAULT.withRecordSeparator('\n').withQuote(DQUOTE_CHAR).withEscape(BACKSLASH_CH);
494540
final StringWriter sw = new StringWriter();
495541
final List<String> list = new LinkedList<>();
496542
try (final CSVPrinter printer = new CSVPrinter(sw, format)) {
@@ -506,7 +552,7 @@ public void testJira135_part1() throws IOException {
506552
@Test
507553
@Ignore
508554
public void testJira135_part2() throws IOException {
509-
final CSVFormat format = CSVFormat.DEFAULT.withRecordSeparator('\n').withQuote('"').withEscape('\\');
555+
final CSVFormat format = CSVFormat.DEFAULT.withRecordSeparator('\n').withQuote(DQUOTE_CHAR).withEscape(BACKSLASH_CH);
510556
final StringWriter sw = new StringWriter();
511557
final List<String> list = new LinkedList<>();
512558
try (final CSVPrinter printer = new CSVPrinter(sw, format)) {
@@ -522,7 +568,7 @@ public void testJira135_part2() throws IOException {
522568
@Test
523569
@Ignore
524570
public void testJira135_part3() throws IOException {
525-
final CSVFormat format = CSVFormat.DEFAULT.withRecordSeparator('\n').withQuote('"').withEscape('\\');
571+
final CSVFormat format = CSVFormat.DEFAULT.withRecordSeparator('\n').withQuote(DQUOTE_CHAR).withEscape(BACKSLASH_CH);
526572
final StringWriter sw = new StringWriter();
527573
final List<String> list = new LinkedList<>();
528574
try (final CSVPrinter printer = new CSVPrinter(sw, format)) {
@@ -538,7 +584,7 @@ public void testJira135_part3() throws IOException {
538584
@Test
539585
@Ignore
540586
public void testJira135All() throws IOException {
541-
final CSVFormat format = CSVFormat.DEFAULT.withRecordSeparator('\n').withQuote('"').withEscape('\\');
587+
final CSVFormat format = CSVFormat.DEFAULT.withRecordSeparator('\n').withQuote(DQUOTE_CHAR).withEscape(BACKSLASH_CH);
542588
final StringWriter sw = new StringWriter();
543589
final List<String> list = new LinkedList<>();
544590
try (final CSVPrinter printer = new CSVPrinter(sw, format)) {
@@ -567,7 +613,7 @@ public void testMultiLineComment() throws IOException {
567613
@Test
568614
public void testMySqlNullOutput() throws IOException {
569615
Object[] s = new String[] { "NULL", null };
570-
CSVFormat format = CSVFormat.MYSQL.withQuote('"').withNullString("NULL").withQuoteMode(QuoteMode.NON_NUMERIC);
616+
CSVFormat format = CSVFormat.MYSQL.withQuote(DQUOTE_CHAR).withNullString("NULL").withQuoteMode(QuoteMode.NON_NUMERIC);
571617
StringWriter writer = new StringWriter();
572618
try (final CSVPrinter printer = new CSVPrinter(writer, format)) {
573619
printer.printRecord(s);

0 commit comments

Comments
 (0)