From a32b30414b3a1da61ba6d6b847a473f54481815a Mon Sep 17 00:00:00 2001 From: SheetJS Date: Mon, 7 Mar 2022 20:17:32 -0500 Subject: [PATCH] CSV omit trailing record separator [ci skip] --- .spelling | 1 + CHANGELOG.md | 2 ++ bits/90_utils.js | 3 ++- bits/97_node.js | 7 +++---- test.js | 26 +++++++++++++------------- tests/core.js | 26 +++++++++++++------------- 6 files changed, 34 insertions(+), 31 deletions(-) diff --git a/.spelling b/.spelling index bc68c78..25c47ea 100644 --- a/.spelling +++ b/.spelling @@ -1,5 +1,6 @@ # xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com SheetJS +sheetjs js-xlsx xls xlsb diff --git a/CHANGELOG.md b/CHANGELOG.md index 55b44ac..cde25af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 changes may not be included if they are not expected to break existing code. +* CSV output omits trailing record separator + ## v0.18.2 * Hotfix for unicode processing of XLSX exports diff --git a/bits/90_utils.js b/bits/90_utils.js index cfd2043..e4924b0 100644 --- a/bits/90_utils.js +++ b/bits/90_utils.js @@ -126,12 +126,13 @@ function sheet_to_csv(sheet/*:Worksheet*/, opts/*:?Sheet2CSVOpts*/)/*:string*/ { var colinfo/*:Array*/ = o.skipHidden && sheet["!cols"] || []; var rowinfo/*:Array*/ = o.skipHidden && sheet["!rows"] || []; 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) { if ((rowinfo[R]||{}).hidden) continue; row = make_csv_row(sheet, r, R, cols, fs, rs, FS, o); if(row == null) { continue; } if(o.strip) row = row.replace(endregex,""); - out.push(row + RS); + out.push((w++ ? RS : "") + row); } delete o.dense; return out.join(""); diff --git a/bits/97_node.js b/bits/97_node.js index ff3a309..eb93878 100644 --- a/bits/97_node.js +++ b/bits/97_node.js @@ -18,7 +18,7 @@ if(has_buf && typeof require != 'undefined') (function() { var rowinfo/*:Array*/ = o.skipHidden && sheet["!rows"] || []; 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 BOM = false; + var BOM = false, w = 0; stream._read = function() { if(!BOM) { BOM = true; return stream.push("\uFEFF"); } 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); if(row != null) { if(o.strip) row = row.replace(endregex,""); - stream.push(row + RS); - break; + return stream.push((w++ ? RS : "") + row); } } - if(R > r.e.r) return stream.push(null); + return stream.push(null); }; return stream; }; diff --git a/test.js b/test.js index 8f5f0e4..8e2487e 100644 --- a/test.js +++ b/test.js @@ -1453,8 +1453,8 @@ describe('write features', function() { 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([[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:"Sheet2"}), "5,6\n7,8\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"); 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); else it('before', bef); 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)); }); 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)); }); 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}); delete get_cell(_ws,"C3").w; delete get_cell(_ws,"C3").z; assert.equal(baseline, X.utils.sheet_to_csv(_ws, {dateNF:"YYYYMMDD"})); }); 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})); }); 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})); }); it('should handle various line endings', function() { @@ -2066,25 +2066,25 @@ describe('CSV', 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"]; assert.equal(X.utils.sheet_to_csv(ws), baseline); assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), baseline); ws["!rows"] = [null,{hidden:true},null,null]; 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"]; }); 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"]; assert.equal(X.utils.sheet_to_csv(ws), baseline); assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), baseline); ws["!cols"] = [null,{hidden:true},null,null]; 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]; - 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"]; }); }); @@ -2264,11 +2264,11 @@ describe('HTML', function() { var html = "
AB
12
34
46
"; it('HTML string', function() { 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() { 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"); }); }); }); diff --git a/tests/core.js b/tests/core.js index ff54cd3..b43e7ba 100644 --- a/tests/core.js +++ b/tests/core.js @@ -1453,8 +1453,8 @@ describe('write features', function() { 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([[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:"Sheet2"}), "5,6\n7,8\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"); 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); else it('before', bef); 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)); }); 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)); }); 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}); delete get_cell(_ws,"C3").w; delete get_cell(_ws,"C3").z; assert.equal(baseline, X.utils.sheet_to_csv(_ws, {dateNF:"YYYYMMDD"})); }); 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})); }); 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})); }); it('should handle various line endings', function() { @@ -2066,25 +2066,25 @@ describe('CSV', 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"]; assert.equal(X.utils.sheet_to_csv(ws), baseline); assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), baseline); ws["!rows"] = [null,{hidden:true},null,null]; 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"]; }); 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"]; assert.equal(X.utils.sheet_to_csv(ws), baseline); assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), baseline); ws["!cols"] = [null,{hidden:true},null,null]; 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]; - 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"]; }); }); @@ -2264,11 +2264,11 @@ describe('HTML', function() { var html = "
AB
12
34
46
"; it('HTML string', function() { 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() { 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"); }); }); });