Node.js で csv-stringify による CSV 出力をする際、項目(レコード)に何種類かの文字があった場合はダブルクォートで括るようにしたい。
csv-stringify が大体勝手にやってくれるが、現時点の最新版 v5.1.2 では改行の一部の取扱いが期待通りでなかった。
オプションで何とかできないだろうかと ドキュメント を確認したが、それらしいものが見当たらない。
ソースコードを眺めてみると、 lib/index.js にある quoted_match
が、 正規表現でマッチした場合にクォートさせることに使えるように見える。
npm には Test with RunKit
のボタンがついているので、これで動作確認してみた。
まずは、行デリミタを CRLF にして、項目内に CRLF (\r\n
) が含まれたらダブルクォートされることを確認。
var stringify = require('csv-stringify');
var should = require('should');
input = [ [ '1', '2\r\nx', '3', '4' ], [ 'a', 'b', 'c', 'd' ] ];
stringify(input,
{
record_delimiter: 'windows'
},
function(err, output){
output.should.eql('1,"2\r\nx",3,4\r\na,b,c,d\r\n');
});
次に、問題である項目内に LF (\n
) が含まれる場合にクォートされないことを確認。
input = [ [ '1', '2\r\nx', '3\ny', '4' ], [ 'a', 'b', 'c', 'd' ] ];
stringify(input,
{
record_delimiter: 'windows'
},
function(err, output){
output.should.eql('1,"2\r\nx","3\ny",4\r\na,b,c,d\r\n');
});
これは期待通り失敗した。
いよいよ quoted_match
を使って LF が含まれたときにのみクォートさせるようにする。
LF の前に CR がなければ LF にマッチする、という否定後読みで実装してみる。
input = [ [ '1', '2\r\nx', '3\ny', '4' ], [ 'a', 'b', 'c', 'd' ] ];
stringify(input,
{
record_delimiter: 'windows',
quoted_match: /(?<!\r)\n/
},
function(err, output){
output.should.eql('1,"2\r\nx","3\ny",4\r\na,b,c,d\r\n');
});
成功。
ただ、 クォートされる条件の部分 を見れば、いくつかのクォートされる条件のうちのいずれかに合致したらクォートされるという実装なので、行デリミタが CRLF であろうと quoted_match
では CRLF へのマッチを回避する必要はなかった。
単純に以下で良かった。
input = [ [ '1', '2\r\nx', '3\ny', '4' ], [ 'a', 'b', 'c', 'd' ] ];
stringify(input,
{
record_delimiter: 'windows',
quoted_match: /\n/
},
function(err, output){
output.should.eql('1,"2\r\nx","3\ny",4\r\na,b,c,d\r\n');
});
というわけで、場合によってはプルリクエストだそうかなと考えたが、やりたかったことは提供されている機能で実現できることがわかった。