CSV omit trailing record separator [ci skip]

This commit is contained in:
SheetJS 2022-03-07 20:17:32 -05:00
parent 467020fc69
commit a32b30414b
6 changed files with 34 additions and 31 deletions

@ -1,5 +1,6 @@
# xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com # xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com
SheetJS SheetJS
sheetjs
js-xlsx js-xlsx
xls xls
xlsb xlsb

@ -4,6 +4,8 @@ This log is intended to keep track of backwards-incompatible changes, including
but not limited to API changes and file location changes. Minor behavioral but not limited to API changes and file location changes. Minor behavioral
changes may not be included if they are not expected to break existing code. changes may not be included if they are not expected to break existing code.
* CSV output omits trailing record separator
## v0.18.2 ## v0.18.2
* Hotfix for unicode processing of XLSX exports * Hotfix for unicode processing of XLSX exports

@ -126,12 +126,13 @@ function sheet_to_csv(sheet/*:Worksheet*/, opts/*:?Sheet2CSVOpts*/)/*:string*/ {
var colinfo/*:Array<ColInfo>*/ = o.skipHidden && sheet["!cols"] || []; var colinfo/*:Array<ColInfo>*/ = o.skipHidden && sheet["!cols"] || [];
var rowinfo/*:Array<ColInfo>*/ = o.skipHidden && sheet["!rows"] || []; var rowinfo/*:Array<ColInfo>*/ = o.skipHidden && sheet["!rows"] || [];
for(var C = r.s.c; C <= r.e.c; ++C) if (!((colinfo[C]||{}).hidden)) cols[C] = encode_col(C); for(var C = r.s.c; C <= r.e.c; ++C) if (!((colinfo[C]||{}).hidden)) cols[C] = encode_col(C);
var w = 0;
for(var R = r.s.r; R <= r.e.r; ++R) { for(var R = r.s.r; R <= r.e.r; ++R) {
if ((rowinfo[R]||{}).hidden) continue; if ((rowinfo[R]||{}).hidden) continue;
row = make_csv_row(sheet, r, R, cols, fs, rs, FS, o); row = make_csv_row(sheet, r, R, cols, fs, rs, FS, o);
if(row == null) { continue; } if(row == null) { continue; }
if(o.strip) row = row.replace(endregex,""); if(o.strip) row = row.replace(endregex,"");
out.push(row + RS); out.push((w++ ? RS : "") + row);
} }
delete o.dense; delete o.dense;
return out.join(""); return out.join("");

@ -18,7 +18,7 @@ if(has_buf && typeof require != 'undefined') (function() {
var rowinfo/*:Array<RowInfo>*/ = o.skipHidden && sheet["!rows"] || []; var rowinfo/*:Array<RowInfo>*/ = o.skipHidden && sheet["!rows"] || [];
for(var C = r.s.c; C <= r.e.c; ++C) if (!((colinfo[C]||{}).hidden)) cols[C] = encode_col(C); for(var C = r.s.c; C <= r.e.c; ++C) if (!((colinfo[C]||{}).hidden)) cols[C] = encode_col(C);
var R = r.s.r; var R = r.s.r;
var BOM = false; var BOM = false, w = 0;
stream._read = function() { stream._read = function() {
if(!BOM) { BOM = true; return stream.push("\uFEFF"); } if(!BOM) { BOM = true; return stream.push("\uFEFF"); }
while(R <= r.e.r) { while(R <= r.e.r) {
@ -27,11 +27,10 @@ if(has_buf && typeof require != 'undefined') (function() {
row = make_csv_row(sheet, r, R-1, cols, fs, rs, FS, o); row = make_csv_row(sheet, r, R-1, cols, fs, rs, FS, o);
if(row != null) { if(row != null) {
if(o.strip) row = row.replace(endregex,""); if(o.strip) row = row.replace(endregex,"");
stream.push(row + RS); return stream.push((w++ ? RS : "") + row);
break;
} }
} }
if(R > r.e.r) return stream.push(null); return stream.push(null);
}; };
return stream; return stream;
}; };

26
test.js

@ -1453,8 +1453,8 @@ describe('write features', function() {
var wb = X.utils.book_new(); var wb = X.utils.book_new();
X.utils.book_append_sheet(wb, X.utils.aoa_to_sheet([[1,2],[3,4]]), "Sheet1"); X.utils.book_append_sheet(wb, X.utils.aoa_to_sheet([[1,2],[3,4]]), "Sheet1");
X.utils.book_append_sheet(wb, X.utils.aoa_to_sheet([[5,6],[7,8]]), "Sheet2"); X.utils.book_append_sheet(wb, X.utils.aoa_to_sheet([[5,6],[7,8]]), "Sheet2");
assert.equal(X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet1"}), "1,2\n3,4\n"); assert.equal(X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet1"}), "1,2\n3,4");
assert.equal(X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet2"}), "5,6\n7,8\n"); assert.equal(X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet2"}), "5,6\n7,8");
assert.throws(function() { X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet3"}); }); assert.throws(function() { X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet3"}); });
}); });
}); });
@ -2030,7 +2030,7 @@ describe('CSV', function() {
if(typeof before != 'undefined') before(bef); if(typeof before != 'undefined') before(bef);
else it('before', bef); else it('before', bef);
it('should generate csv', function() { it('should generate csv', function() {
var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,\n"; var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,";
assert.equal(baseline, X.utils.sheet_to_csv(ws)); assert.equal(baseline, X.utils.sheet_to_csv(ws));
}); });
it('should handle FS', function() { it('should handle FS', function() {
@ -2042,18 +2042,18 @@ describe('CSV', function() {
assert.equal(X.utils.sheet_to_csv(ws, {RS:";"}).replace(/[;]/g,"\n"), X.utils.sheet_to_csv(ws)); assert.equal(X.utils.sheet_to_csv(ws, {RS:";"}).replace(/[;]/g,"\n"), X.utils.sheet_to_csv(ws));
}); });
it('should handle dateNF', function() { it('should handle dateNF', function() {
var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,20140219,0.3\n,,,\nbaz,,qux,\n"; var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,20140219,0.3\n,,,\nbaz,,qux,";
var _ws = X.utils.aoa_to_sheet(data, {cellDates:true}); var _ws = X.utils.aoa_to_sheet(data, {cellDates:true});
delete get_cell(_ws,"C3").w; delete get_cell(_ws,"C3").w;
delete get_cell(_ws,"C3").z; delete get_cell(_ws,"C3").z;
assert.equal(baseline, X.utils.sheet_to_csv(_ws, {dateNF:"YYYYMMDD"})); assert.equal(baseline, X.utils.sheet_to_csv(_ws, {dateNF:"YYYYMMDD"}));
}); });
it('should handle strip', function() { it('should handle strip', function() {
var baseline = "1,2,3\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n\nbaz,,qux\n"; var baseline = "1,2,3\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n\nbaz,,qux";
assert.equal(baseline, X.utils.sheet_to_csv(ws, {strip:true})); assert.equal(baseline, X.utils.sheet_to_csv(ws, {strip:true}));
}); });
it('should handle blankrows', function() { it('should handle blankrows', function() {
var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\nbaz,,qux,\n"; var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\nbaz,,qux,";
assert.equal(baseline, X.utils.sheet_to_csv(ws, {blankrows:false})); assert.equal(baseline, X.utils.sheet_to_csv(ws, {blankrows:false}));
}); });
it('should handle various line endings', function() { it('should handle various line endings', function() {
@ -2066,25 +2066,25 @@ describe('CSV', function() {
}); });
}); });
it('should handle skipHidden for rows if requested', function() { it('should handle skipHidden for rows if requested', function() {
var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,\n"; var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,";
delete ws["!rows"]; delete ws["!rows"];
assert.equal(X.utils.sheet_to_csv(ws), baseline); assert.equal(X.utils.sheet_to_csv(ws), baseline);
assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), baseline); assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), baseline);
ws["!rows"] = [null,{hidden:true},null,null]; ws["!rows"] = [null,{hidden:true},null,null];
assert.equal(X.utils.sheet_to_csv(ws), baseline); assert.equal(X.utils.sheet_to_csv(ws), baseline);
assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), "1,2,3,\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,\n"); assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), "1,2,3,\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,");
delete ws["!rows"]; delete ws["!rows"];
}); });
it('should handle skipHidden for columns if requested', function() { it('should handle skipHidden for columns if requested', function() {
var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,\n"; var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,";
delete ws["!cols"]; delete ws["!cols"];
assert.equal(X.utils.sheet_to_csv(ws), baseline); assert.equal(X.utils.sheet_to_csv(ws), baseline);
assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), baseline); assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), baseline);
ws["!cols"] = [null,{hidden:true},null,null]; ws["!cols"] = [null,{hidden:true},null,null];
assert.equal(X.utils.sheet_to_csv(ws), baseline); assert.equal(X.utils.sheet_to_csv(ws), baseline);
assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), "1,3,\nTRUE,,sheetjs\nfoo,2/19/14,0.3\n,,\nbaz,qux,\n"); assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), "1,3,\nTRUE,,sheetjs\nfoo,2/19/14,0.3\n,,\nbaz,qux,");
ws["!cols"] = [{hidden:true},null,null,null]; ws["!cols"] = [{hidden:true},null,null,null];
assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), "2,3,\nFALSE,,sheetjs\nbar,2/19/14,0.3\n,,\n,qux,\n"); assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), "2,3,\nFALSE,,sheetjs\nbar,2/19/14,0.3\n,,\n,qux,");
delete ws["!cols"]; delete ws["!cols"];
}); });
}); });
@ -2264,11 +2264,11 @@ describe('HTML', function() {
var html = "<table><thead><tr><th>A</th><th>B</th></tr></thead><tbody><tr><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td></tr></tbody><tfoot><tr><th>4</th><th>6</th></tr></tfoot></table>"; var html = "<table><thead><tr><th>A</th><th>B</th></tr></thead><tbody><tr><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td></tr></tbody><tfoot><tr><th>4</th><th>6</th></tr></tfoot></table>";
it('HTML string', function() { it('HTML string', function() {
var ws = X.read(html, {type:'string'}).Sheets.Sheet1; var ws = X.read(html, {type:'string'}).Sheets.Sheet1;
assert.equal(X.utils.sheet_to_csv(ws), "A,B\n1,2\n3,4\n4,6\n"); assert.equal(X.utils.sheet_to_csv(ws), "A,B\n1,2\n3,4\n4,6");
}); });
if(domtest) it('DOM', function() { if(domtest) it('DOM', function() {
var ws = X.utils.table_to_sheet(get_dom_element(html)); var ws = X.utils.table_to_sheet(get_dom_element(html));
assert.equal(X.utils.sheet_to_csv(ws), "A,B\n1,2\n3,4\n4,6\n"); assert.equal(X.utils.sheet_to_csv(ws), "A,B\n1,2\n3,4\n4,6");
}); });
}); });
}); });

26
tests/core.js generated

@ -1453,8 +1453,8 @@ describe('write features', function() {
var wb = X.utils.book_new(); var wb = X.utils.book_new();
X.utils.book_append_sheet(wb, X.utils.aoa_to_sheet([[1,2],[3,4]]), "Sheet1"); X.utils.book_append_sheet(wb, X.utils.aoa_to_sheet([[1,2],[3,4]]), "Sheet1");
X.utils.book_append_sheet(wb, X.utils.aoa_to_sheet([[5,6],[7,8]]), "Sheet2"); X.utils.book_append_sheet(wb, X.utils.aoa_to_sheet([[5,6],[7,8]]), "Sheet2");
assert.equal(X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet1"}), "1,2\n3,4\n"); assert.equal(X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet1"}), "1,2\n3,4");
assert.equal(X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet2"}), "5,6\n7,8\n"); assert.equal(X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet2"}), "5,6\n7,8");
assert.throws(function() { X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet3"}); }); assert.throws(function() { X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet3"}); });
}); });
}); });
@ -2030,7 +2030,7 @@ describe('CSV', function() {
if(typeof before != 'undefined') before(bef); if(typeof before != 'undefined') before(bef);
else it('before', bef); else it('before', bef);
it('should generate csv', function() { it('should generate csv', function() {
var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,\n"; var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,";
assert.equal(baseline, X.utils.sheet_to_csv(ws)); assert.equal(baseline, X.utils.sheet_to_csv(ws));
}); });
it('should handle FS', function() { it('should handle FS', function() {
@ -2042,18 +2042,18 @@ describe('CSV', function() {
assert.equal(X.utils.sheet_to_csv(ws, {RS:";"}).replace(/[;]/g,"\n"), X.utils.sheet_to_csv(ws)); assert.equal(X.utils.sheet_to_csv(ws, {RS:";"}).replace(/[;]/g,"\n"), X.utils.sheet_to_csv(ws));
}); });
it('should handle dateNF', function() { it('should handle dateNF', function() {
var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,20140219,0.3\n,,,\nbaz,,qux,\n"; var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,20140219,0.3\n,,,\nbaz,,qux,";
var _ws = X.utils.aoa_to_sheet(data, {cellDates:true}); var _ws = X.utils.aoa_to_sheet(data, {cellDates:true});
delete get_cell(_ws,"C3").w; delete get_cell(_ws,"C3").w;
delete get_cell(_ws,"C3").z; delete get_cell(_ws,"C3").z;
assert.equal(baseline, X.utils.sheet_to_csv(_ws, {dateNF:"YYYYMMDD"})); assert.equal(baseline, X.utils.sheet_to_csv(_ws, {dateNF:"YYYYMMDD"}));
}); });
it('should handle strip', function() { it('should handle strip', function() {
var baseline = "1,2,3\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n\nbaz,,qux\n"; var baseline = "1,2,3\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n\nbaz,,qux";
assert.equal(baseline, X.utils.sheet_to_csv(ws, {strip:true})); assert.equal(baseline, X.utils.sheet_to_csv(ws, {strip:true}));
}); });
it('should handle blankrows', function() { it('should handle blankrows', function() {
var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\nbaz,,qux,\n"; var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\nbaz,,qux,";
assert.equal(baseline, X.utils.sheet_to_csv(ws, {blankrows:false})); assert.equal(baseline, X.utils.sheet_to_csv(ws, {blankrows:false}));
}); });
it('should handle various line endings', function() { it('should handle various line endings', function() {
@ -2066,25 +2066,25 @@ describe('CSV', function() {
}); });
}); });
it('should handle skipHidden for rows if requested', function() { it('should handle skipHidden for rows if requested', function() {
var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,\n"; var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,";
delete ws["!rows"]; delete ws["!rows"];
assert.equal(X.utils.sheet_to_csv(ws), baseline); assert.equal(X.utils.sheet_to_csv(ws), baseline);
assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), baseline); assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), baseline);
ws["!rows"] = [null,{hidden:true},null,null]; ws["!rows"] = [null,{hidden:true},null,null];
assert.equal(X.utils.sheet_to_csv(ws), baseline); assert.equal(X.utils.sheet_to_csv(ws), baseline);
assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), "1,2,3,\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,\n"); assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), "1,2,3,\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,");
delete ws["!rows"]; delete ws["!rows"];
}); });
it('should handle skipHidden for columns if requested', function() { it('should handle skipHidden for columns if requested', function() {
var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,\n"; var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,";
delete ws["!cols"]; delete ws["!cols"];
assert.equal(X.utils.sheet_to_csv(ws), baseline); assert.equal(X.utils.sheet_to_csv(ws), baseline);
assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), baseline); assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), baseline);
ws["!cols"] = [null,{hidden:true},null,null]; ws["!cols"] = [null,{hidden:true},null,null];
assert.equal(X.utils.sheet_to_csv(ws), baseline); assert.equal(X.utils.sheet_to_csv(ws), baseline);
assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), "1,3,\nTRUE,,sheetjs\nfoo,2/19/14,0.3\n,,\nbaz,qux,\n"); assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), "1,3,\nTRUE,,sheetjs\nfoo,2/19/14,0.3\n,,\nbaz,qux,");
ws["!cols"] = [{hidden:true},null,null,null]; ws["!cols"] = [{hidden:true},null,null,null];
assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), "2,3,\nFALSE,,sheetjs\nbar,2/19/14,0.3\n,,\n,qux,\n"); assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), "2,3,\nFALSE,,sheetjs\nbar,2/19/14,0.3\n,,\n,qux,");
delete ws["!cols"]; delete ws["!cols"];
}); });
}); });
@ -2264,11 +2264,11 @@ describe('HTML', function() {
var html = "<table><thead><tr><th>A</th><th>B</th></tr></thead><tbody><tr><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td></tr></tbody><tfoot><tr><th>4</th><th>6</th></tr></tfoot></table>"; var html = "<table><thead><tr><th>A</th><th>B</th></tr></thead><tbody><tr><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td></tr></tbody><tfoot><tr><th>4</th><th>6</th></tr></tfoot></table>";
it('HTML string', function() { it('HTML string', function() {
var ws = X.read(html, {type:'string'}).Sheets.Sheet1; var ws = X.read(html, {type:'string'}).Sheets.Sheet1;
assert.equal(X.utils.sheet_to_csv(ws), "A,B\n1,2\n3,4\n4,6\n"); assert.equal(X.utils.sheet_to_csv(ws), "A,B\n1,2\n3,4\n4,6");
}); });
if(domtest) it('DOM', function() { if(domtest) it('DOM', function() {
var ws = X.utils.table_to_sheet(get_dom_element(html)); var ws = X.utils.table_to_sheet(get_dom_element(html));
assert.equal(X.utils.sheet_to_csv(ws), "A,B\n1,2\n3,4\n4,6\n"); assert.equal(X.utils.sheet_to_csv(ws), "A,B\n1,2\n3,4\n4,6");
}); });
}); });
}); });