Skip to content

Commit 5573c1b

Browse files
author
Sylvain Deschenes
committed
Merge remote-tracking branch 'qiangli/csv-63' into trunk
2 parents dd2cd54 + 1846538 commit 5573c1b

2 files changed

Lines changed: 40 additions & 13 deletions

File tree

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

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,10 @@ public final class CSVPrinter implements Flushable, Closeable {
3939
private final Appendable out;
4040
private final CSVFormat format;
4141

42-
/** True if we just began a new record. */
43-
private boolean newRecord = true;
42+
/** 0 if we just began a new record. */
43+
private int newRecord = 0;
44+
/** true if first value is empty */
45+
private boolean firstEmpty;
4446

4547
/**
4648
* Creates a printer that will print values to the given stream following the CSVFormat.
@@ -133,7 +135,7 @@ public void print(final Object value) throws IOException {
133135

134136
private void print(final Object object, final CharSequence value, final int offset, final int len)
135137
throws IOException {
136-
if (!newRecord) {
138+
if (newRecord > 0) {
137139
out.append(format.getDelimiter());
138140
}
139141
if (object == null) {
@@ -146,7 +148,7 @@ private void print(final Object object, final CharSequence value, final int offs
146148
} else {
147149
out.append(value, offset, offset + len);
148150
}
149-
newRecord = false;
151+
newRecord++;
150152
}
151153

152154
/*
@@ -219,18 +221,16 @@ private void printAndQuote(final Object object, final CharSequence value, final
219221
return;
220222
case MINIMAL:
221223
if (len <= 0) {
222-
// always quote an empty token that is the first
223-
// on the line, as it may be the only thing on the
224-
// line. If it were not quoted in that case,
225-
// an empty line has no tokens.
226-
if (newRecord) {
227-
quote = true;
224+
// mark quotes may be required for first empty value
225+
if (newRecord == 0) {
226+
firstEmpty = true;
227+
return;
228228
}
229229
} else {
230230
char c = value.charAt(pos);
231231

232232
// TODO where did this rule come from?
233-
if (newRecord && (c < '0' || (c > '9' && c < 'A') || (c > 'Z' && c < 'a') || (c > 'z'))) {
233+
if (newRecord == 0 && (c < '0' || (c > '9' && c < 'A') || (c > 'Z' && c < 'a') || (c > 'z'))) {
234234
quote = true;
235235
} else if (c <= COMMENT) {
236236
// Some other chars at the start of a value caused the parser to fail, so for now
@@ -318,7 +318,7 @@ public void printComment(final String comment) throws IOException {
318318
if (!format.isCommentMarkerSet()) {
319319
return;
320320
}
321-
if (!newRecord) {
321+
if (newRecord > 0) {
322322
println();
323323
}
324324
out.append(format.getCommentMarker().charValue());
@@ -351,11 +351,20 @@ public void printComment(final String comment) throws IOException {
351351
* If an I/O error occurs
352352
*/
353353
public void println() throws IOException {
354+
// if the line only contains an empty value
355+
if (newRecord == 1 && firstEmpty
356+
&& (format.getQuoteMode() == null || format.getQuoteMode() == QuoteMode.MINIMAL)) {
357+
final char quoteChar = format.getQuoteCharacter().charValue();
358+
out.append(quoteChar);
359+
out.append(quoteChar);
360+
}
361+
354362
final String recordSeparator = format.getRecordSeparator();
355363
if (recordSeparator != null) {
356364
out.append(recordSeparator);
357365
}
358-
newRecord = true;
366+
newRecord = 0;
367+
firstEmpty = false;
359368
}
360369

361370
/**

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,24 @@ public void testPrintCustomNullValues() throws IOException {
597597
printer.close();
598598
}
599599

600+
@Test
601+
public void testPrintFirstEmptyValue() throws IOException {
602+
final StringWriter sw = new StringWriter();
603+
final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT);
604+
printer.printRecord("", "b", "c");
605+
assertEquals(",b,c" + recordSeparator, sw.toString());
606+
printer.close();
607+
}
608+
609+
@Test
610+
public void testPrintOnlyEmptyValue() throws IOException {
611+
final StringWriter sw = new StringWriter();
612+
final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT);
613+
printer.printRecord("");
614+
assertEquals("\"\"" + recordSeparator, sw.toString());
615+
printer.close();
616+
}
617+
600618
@Test
601619
public void testParseCustomNullValues() throws IOException {
602620
final StringWriter sw = new StringWriter();

0 commit comments

Comments
 (0)