LoginSignup
21
18

More than 1 year has passed since last update.

Node.js (NestJS) で csv ファイルを読み込む(4つのライブラリ比較)

Last updated at Posted at 2022-08-21

はじめに

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 ファイル
以下のファイルを共通して使用します(フォルダは自由に指定してください)。

src/assets/csv/test.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. オプション指定なし

sample.ts
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 をオブジェクト形式で取得するようにして、区切り文字をタブ文字と指定する場合です。

sample.ts
const records = parse(data, { columns: true, delimiter: '\t' });
キー 内容
columns 先頭行をカラム名としてオブジェクト表示
delimiter 区切り文字を指定

※ 上記以外にもオプションはありますので、公式サイトなどを確認してみてください。

出力結果は、以下のようにオブジェクト形式となります。

出力結果
{
  '品名': 'にんじん',
  '区分': '野菜',
  '単価': '70',
  '購入数': '3',
  '購入日': '2020/10/20'
}
{
  '品名': 'りんご',
  '区分': '果物',
  '単価': '150',
  '購入数': '2',
  '購入日': '2020/10/22'
}
# 以下略
コード全体を表示

テキストファイル
使用するファイルの区切り文字はタブ(\t)にしています。
タブ区切りのため拡張子は .tsv としていますが、.csv でも何でも構いません。

src/assets/csv/testTab.tsv
品名	区分	単価	購入数	購入日
にんじん	野菜	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

ソースコード

sample.ts
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. オプション指定なし

sample.ts
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 の引数にオプションを指定します。
指定できる内容は、同期処理の場合と同じです。

sample.ts
const parser = parse({ delimiter: '\t', columns: true });
キー 内容
columns 先頭行をカラム名としてオブジェクト表示
delimiter 区切り文字を指定

※ 上記以外にもオプションはありますので、公式サイトなどを確認してみてください。

コード全体を表示

テキストファイル
使用するファイルの区切り文字はタブ(\t)にしています。
タブ区切りのため拡張子は .tsv としていますが、.csv でも何でも構いません。

src/assets/csv/testTab.tsv
品名	区分	単価	購入数	購入日
にんじん	野菜	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

ソースコード

sample.ts
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. オプション指定なし

sample.ts
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 の代わりにタブ区切りのファイルを読み込みます)。

sample.ts
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. オプション指定なし

sample.ts
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 の代わりにタブ区切りのファイルを読み込みます)。

sample.ts
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. オプション指定なし

sample.ts
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 の代わりにタブ区切りのファイルを読み込みます)。

sample.ts
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 ライブラリを使おうかと思います。

21
18
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
18