はじめに
Node.js (NestJS) で csv を読み込むには、使用できるライブラリが4つほどあります。
備忘と比較のため、ここではそれぞれのコードを記載していきます。
項番 | 名称 | 参考サイト |
---|---|---|
1 | csv-parse | npmjs - csv-parse, CSV for Node.js |
2 | csv-parser | npmjs - csv-parser, OpenBase - csv-parser |
3 | csv | npmjs - csv, 初心者のためのCSV読み込み・JSON変換まとめ! |
4 | fast-csv | npmjs - fast-csv, Fast-CSV - Methods |
<どのライブラリを使用するか>
一番メジャーな「① csv-parse」を使用するのが無難そうですが、Stream を使用する場合の記述が長くなります(私の確認した範囲で)。
比較サイトを見ると、どのライブラリも一定数ダウンロードされており、更新も頻繁にされていることから、使いやすいものを使用すれば良いと思います。
<実行環境>
フレームワークは NeatJS を使用します(そのため TypeScript で記述します)。
名称 | バージョン |
---|---|
Node.js | 14.17.6 |
NestJS | 8.2.0 |
TypeScript | 4.5.4 |
<使用する CSV ファイル>
以下のファイルを共通して使用します(フォルダは自由に指定してください)。
品名,区分,単価,購入数,購入日
にんじん,野菜,70,3,2020/10/20
りんご,果物,150,2,2020/10/22
みかん,果物,40,10,2020/10/30
キャベツ,野菜,180,1,2020/11/1
じゃがいも,野菜,50,5,2020/11/5
バナナ,果物,300,2,2020/11/6
メロン,果物,1500,1,2020/11/8
1. csv-parse
csv-parse は、一番ダウンロードされているライブラリです。
1-1. インストール
$ npm install --save csv-parse
1-2. 同期処理で取得
まず、同期処理の例です。
シンプルな形式ですが、ファイルを一旦全部読み込むため、ファイルサイズ分だけメモリを使用することになります。
1-2-1. オプション指定なし
import * as fs from 'fs';
import { parse } from 'csv-parse/sync';
const data = fs.readFileSync('src/assets/csv/test.csv');
const records = parse(data);
for (const record of records) {
console.log(record);
}
<参考サイト>
・import * as fs from 'fs' の記述について
<出力結果>
オプションの指定をしない場合の取得結果は、以下のように配列形式となります。
[ '品名', '区分', '単価', '購入数', '購入日' ]
[ 'にんじん', '野菜', '70', '3', '2020/10/20' ]
[ 'りんご', '果物', '150', '2', '2020/10/22' ]
[ 'みかん', '果物', '40', '10', '2020/10/30' ]
[ 'キャベツ', '野菜', '180', '1', '2020/11/1' ]
[ 'じゃがいも', '野菜', '50', '5', '2020/11/5' ]
[ 'バナナ', '果物', '300', '2', '2020/11/6' ]
[ 'メロン', '果物', '1500', '1', '2020/11/8' ]
1-2-2. オプション指定
オプション指定は、parse() メソッドの引数に指定します。
以下は、CSV をオブジェクト形式で取得するようにして、区切り文字をタブ文字と指定する場合です。
const records = parse(data, { columns: true, delimiter: '\t' });
キー | 内容 |
---|---|
columns | 先頭行をカラム名としてオブジェクト表示 |
delimiter | 区切り文字を指定 |
※ 上記以外にもオプションはありますので、公式サイトなどを確認してみてください。
出力結果は、以下のようにオブジェクト形式となります。
{
'品名': 'にんじん',
'区分': '野菜',
'単価': '70',
'購入数': '3',
'購入日': '2020/10/20'
}
{
'品名': 'りんご',
'区分': '果物',
'単価': '150',
'購入数': '2',
'購入日': '2020/10/22'
}
# 以下略
コード全体を表示
<テキストファイル>
使用するファイルの区切り文字はタブ(\t
)にしています。
タブ区切りのため拡張子は .tsv としていますが、.csv でも何でも構いません。
品名 区分 単価 購入数 購入日
にんじん 野菜 70 3 2020/10/20
りんご 果物 150 2 2020/10/22
みかん 果物 40 10 2020/10/30
キャベツ 野菜 180 1 2020/11/1
じゃがいも 野菜 50 5 2020/11/5
バナナ 果物 300 2 2020/11/6
メロン 果物 1500 1 2020/11/8
<ソースコード>
import * as fs from 'fs';
import { parse } from 'csv-parse/sync';
const data = fs.readFileSync('src/assets/csv/testTab.tsv');
const records = parse(data, { columns: true, delimiter: '\t' });
for (const record of records) {
console.log(record);
}
<出力結果>
{
'品名': 'にんじん',
'区分': '野菜',
'単価': '70',
'購入数': '3',
'購入日': '2020/10/20'
}
{
'品名': 'りんご',
'区分': '果物',
'単価': '150',
'購入数': '2',
'購入日': '2020/10/22'
}
{
'品名': 'みかん',
'区分': '果物',
'単価': '40',
'購入数': '10',
'購入日': '2020/10/30'
}
{
'品名': 'キャベツ',
'区分': '野菜',
'単価': '180',
'購入数': '1',
'購入日': '2020/11/1'
}
{
'品名': 'じゃがいも',
'区分': '野菜',
'単価': '50',
'購入数': '5',
'購入日': '2020/11/5'
}
{
'品名': 'バナナ',
'区分': '果物',
'単価': '300',
'購入数': '2',
'購入日': '2020/11/6'
}
{
'品名': 'メロン',
'区分': '果物',
'単価': '1500',
'購入数': '1',
'購入日': '2020/11/8'
}
1-3. Stream で取得
Stream で取得する場合は次のようになります。
コードは少し長くなってしまいます。
1-3-1. オプション指定なし
import * as fs from 'fs';
import { parse } from 'csv-parse';
const records = [];
const parser = parse();
parser.on('readable', () => {
let record;
while ((record = parser.read()) !== null) {
records.push(record);
}
});
parser.on('error', (e) => console.log(e.message));
parser.on('end', () => console.log(records));
// parser を使用して実行
fs.createReadStream('src/assets/csv/test.csv').pipe(parser);
<出力結果>
[
[ '品名', '区分', '単価', '購入数', '購入日' ],
[ 'にんじん', '野菜', '70', '3', '2020/10/20' ],
[ 'りんご', '果物', '150', '2', '2020/10/22' ],
[ 'みかん', '果物', '40', '10', '2020/10/30' ],
[ 'キャベツ', '野菜', '180', '1', '2020/11/1' ],
[ 'じゃがいも', '野菜', '50', '5', '2020/11/5' ],
[ 'バナナ', '果物', '300', '2', '2020/11/6' ],
[ 'メロン', '果物', '1500', '1', '2020/11/8' ]
]
1-3-2. オプション指定
parse の引数にオプションを指定します。
指定できる内容は、同期処理の場合と同じです。
const parser = parse({ delimiter: '\t', columns: true });
キー | 内容 |
---|---|
columns | 先頭行をカラム名としてオブジェクト表示 |
delimiter | 区切り文字を指定 |
※ 上記以外にもオプションはありますので、公式サイトなどを確認してみてください。
コード全体を表示
<テキストファイル>
使用するファイルの区切り文字はタブ(\t
)にしています。
タブ区切りのため拡張子は .tsv としていますが、.csv でも何でも構いません。
品名 区分 単価 購入数 購入日
にんじん 野菜 70 3 2020/10/20
りんご 果物 150 2 2020/10/22
みかん 果物 40 10 2020/10/30
キャベツ 野菜 180 1 2020/11/1
じゃがいも 野菜 50 5 2020/11/5
バナナ 果物 300 2 2020/11/6
メロン 果物 1500 1 2020/11/8
<ソースコード>
import * as fs from 'fs';
import { parse } from 'csv-parse';
const records = [];
const parser = parse({ delimiter: '\t', columns: true });
parser.on('readable', () => {
let record;
while ((record = parser.read()) !== null) {
records.push(record);
}
});
parser.on('error', (e) => console.log(e.message));
parser.on('end', () => console.log(records));
// parser を使用して実行
fs.createReadStream('src/assets/csv/testTab.tsv').pipe(parser);
<出力結果>
[
{
'品名': 'にんじん',
'区分': '野菜',
'単価': '70',
'購入数': '3',
'購入日': '2020/10/20'
},
{
'品名': 'りんご',
'区分': '果物',
'単価': '150',
'購入数': '2',
'購入日': '2020/10/22'
},
{
'品名': 'みかん',
'区分': '果物',
'単価': '40',
'購入数': '10',
'購入日': '2020/10/30'
},
{
'品名': 'キャベツ',
'区分': '野菜',
'単価': '180',
'購入数': '1',
'購入日': '2020/11/1'
},
{
'品名': 'じゃがいも',
'区分': '野菜',
'単価': '50',
'購入数': '5',
'購入日': '2020/11/5'
},
{
'品名': 'バナナ',
'区分': '果物',
'単価': '300',
'購入数': '2',
'購入日': '2020/11/6'
},
{
'品名': 'メロン',
'区分': '果物',
'単価': '1500',
'購入数': '1',
'購入日': '2020/11/8'
}
]
2. csv-parser
次は、csv-parser です。
前述の csv-parse と1文字違いなので混同しそうですが、末尾に r
が付いており、異なるライブラリとなります。
2-1. インストール
$ npm install --save csv-parser
2-2. サンプルコード
Stream を使用してファイルを読み込みます。
2-2-1. オプション指定なし
import * as fs from 'fs';
import * as csv from 'csv-parser';
const results = [];
fs.createReadStream('src/assets/csv/test.csv')
.pipe(csv())
.on('data', (data) => results.push(data))
.on('end', () => console.log(results));
<出力結果>
オプションの指定をしない場合の取得結果は、以下のようにオブジェクト形式となります。
[
{
'品名': 'にんじん',
'区分': '野菜',
'単価': '70',
'購入数': '3',
'購入日': '2020/10/20'
},
{
'品名': 'りんご',
'区分': '果物',
'単価': '150',
'購入数': '2',
'購入日': '2020/10/22'
},
# 中略
]
2-2-2. オプション指定
オプション指定は、csv() メソッドの引数に指定します。
以下は、先頭行もレコードとして取得し(headers: false
)、区切り文字をタブ文字と指定する場合です(CSV の代わりにタブ区切りのファイルを読み込みます)。
import * as fs from 'fs';
import * as csv from 'csv-parser';
const results = [];
fs.createReadStream('src/assets/csv/testTab.tsv')
.pipe(csv({ separator: '\t', headers: false }))
.on('data', (data) => results.push(data))
.on('end', () => console.log(results));
キー | 内容 |
---|---|
headers | 先頭行をカラム名としてオブジェクト表示(デフォルト: true) |
separator | 区切り文字を指定 |
※ 上記以外にもオプションはありますので、公式サイトなどを確認してみてください。
出力結果は、以下のようになります。
[
{ '0': '品名', '1': '区分', '2': '単価', '3': '購入数', '4': '購入日' },
{ '0': 'にんじん', '1': '野菜', '2': '70', '3': '3', '4': '2020/10/20' },
{ '0': 'りんご', '1': '果物', '2': '150', '3': '2', '4': '2020/10/22' },
{ '0': 'みかん', '1': '果物', '2': '40', '3': '10', '4': '2020/10/30' },
{ '0': 'キャベツ', '1': '野菜', '2': '180', '3': '1', '4': '2020/11/1' },
{ '0': 'じゃがいも', '1': '野菜', '2': '50', '3': '5', '4': '2020/11/5' },
{ '0': 'バナナ', '1': '果物', '2': '300', '3': '2', '4': '2020/11/6' },
{ '0': 'メロン', '1': '果物', '2': '1500', '3': '1', '4': '2020/11/8' }
]
3. csv
次は、csv というライブラリです。
コードが一番シンプルになります。
3-1. インストール
$ npm install --save csv
3-2. サンプルコード
Stream を使用してファイルを読み込みます。
3-2-1. オプション指定なし
import * as fs from 'fs';
import * as csv from 'csv';
fs.createReadStream('src/assets/csv/test.csv').pipe(
csv.parse((err, data) => console.log(data)),
);
<出力結果>
オプションの指定をしない場合の取得結果は、以下のように配列形式となります。
[
[ '品名', '区分', '単価', '購入数', '購入日' ],
[ 'にんじん', '野菜', '70', '3', '2020/10/20' ],
[ 'りんご', '果物', '150', '2', '2020/10/22' ],
[ 'みかん', '果物', '40', '10', '2020/10/30' ],
[ 'キャベツ', '野菜', '180', '1', '2020/11/1' ],
[ 'じゃがいも', '野菜', '50', '5', '2020/11/5' ],
[ 'バナナ', '果物', '300', '2', '2020/11/6' ],
[ 'メロン', '果物', '1500', '1', '2020/11/8' ]
]
3-2-2. オプション指定
オプション指定は、次のように csv.parse() メソッドの引数に指定します。
以下は、CSV をオブジェクト形式で取得するようにして、区切り文字をタブ文字と指定する場合です(CSV の代わりにタブ区切りのファイルを読み込みます)。
import * as fs from 'fs';
import * as csv from 'csv';
fs.createReadStream('src/assets/csv/testTab.tsv').pipe(
csv.parse({ columns: true, delimiter: '\t' }, (err, data) =>
console.log(data),
),
);
キー | 内容 |
---|---|
columns | 先頭行をカラム名としてオブジェクト表示(デフォルト: false) |
delimiter | 区切り文字を指定 |
※ 上記以外にもオプションはありますので、公式サイトなどを確認してみてください。
出力結果は、以下のようになります。
[
{
'品名': 'にんじん',
'区分': '野菜',
'単価': '70',
'購入数': '3',
'購入日': '2020/10/20'
},
{
'品名': 'りんご',
'区分': '果物',
'単価': '150',
'購入数': '2',
'購入日': '2020/10/22'
},
# 中略
]
4. fast-csv
最後に fast-csv です。
4-1. インストール
$ npm install --save fast-csv
4-2. サンプルコード
Stream を使用してファイルを読み込みます。
4-2-1. オプション指定なし
import * as fs from 'fs';
import { parse } from '@fast-csv/parse';
const rows = [];
fs.createReadStream('src/assets/csv/test.csv')
.pipe(parse())
.on('error', (error) => console.error(error))
.on('data', (row) => rows.push(row))
.on('end', () => console.log(rows));
<出力結果>
オプションの指定をしない場合の取得結果は、以下のように配列形式となります。
[
[ '品名', '区分', '単価', '購入数', '購入日' ],
[ 'にんじん', '野菜', '70', '3', '2020/10/20' ],
[ 'りんご', '果物', '150', '2', '2020/10/22' ],
[ 'みかん', '果物', '40', '10', '2020/10/30' ],
[ 'キャベツ', '野菜', '180', '1', '2020/11/1' ],
[ 'じゃがいも', '野菜', '50', '5', '2020/11/5' ],
[ 'バナナ', '果物', '300', '2', '2020/11/6' ],
[ 'メロン', '果物', '1500', '1', '2020/11/8' ]
]
4-2-2. オプション指定
オプション指定は、次のように parse() メソッドの引数に指定します。
以下は、CSV をオブジェクト形式で取得するようにして、区切り文字をタブ文字と指定する場合です(CSV の代わりにタブ区切りのファイルを読み込みます)。
import * as fs from 'fs';
import { parse } from '@fast-csv/parse';
const rows = [];
fs.createReadStream('src/assets/csv/testTab.tsv')
.pipe(parse({ delimiter: '\t', headers: true }))
.on('error', (error) => console.error(error))
.on('data', (row) => rows.push(row))
.on('end', () => console.log(rows));
キー | 内容 |
---|---|
headers | 先頭行をカラム名としてオブジェクト表示(デフォルト: false) |
delimiter | 区切り文字を指定 |
※ 上記以外にもオプションはありますので、公式サイトなどを確認してみてください。
出力結果は、以下のようになります。
[
{
'品名': 'にんじん',
'区分': '野菜',
'単価': '70',
'購入数': '3',
'購入日': '2020/10/20'
},
{
'品名': 'りんご',
'区分': '果物',
'単価': '150',
'購入数': '2',
'購入日': '2020/10/22'
},
# 中略
]
さいごに
以上です。
私もどれを使うか迷いましたが、コードが一番簡略になる csv ライブラリを使おうかと思います。