Papa Parse v5 で CSV をブラウザでパース・生成する
Papa Parse v5 系で CSV を header 自動検出 + dynamicTyping + ストリーム処理で扱うパターン。Excel の Shift_JIS / BOM、worker:true で main thread を逃がす設計、step ストリーミング時に results.data が空になる罠まで実装メモ。
検証日: 2026-05-26
使用バージョン:
papaparse@5.5.3(2026-05 時点でmain)対象: ブラウザでユーザがアップロードする CSV を扱う、Node スクリプトで CSV ↔ JSON 変換したい人
Papa Parse v5 で CSV をブラウザでパース / 生成 するパターン。header 自動検出 + dynamicTyping + Web Worker によるストリーム処理(数 GB 級でも UI freeze なし)、エスケープ周りの落とし穴を動く demo で確認します。
触って試す
| name | age | city |
|---|---|---|
| Alice | 30 | Tokyo |
| Bob | 25 | Osaka |
| Charlie | 35 | Kyoto |
| Dana, the Great | 28 | New York |
CSV を編集すると右側で header 自動検出 + クォート処理が即時表示される。
セットアップ(npm 1 行 / ESM + UMD / Node どちらも)
npm i papaparse
# TypeScript の型を別途
npm i -D @types/papaparse
| 項目 | 値 |
|---|---|
| 公開バージョン | 5.5.3 |
| Node 必要バージョン | 6+(現実には 18 LTS で動かす) |
| TypeScript 型 | @types/papaparse(DefinitelyTyped) |
| ブラウザ対応 | ESM / UMD 同梱、Worker / FileReader を使う(ブラウザ環境のみ) |
| ライセンス | MIT |
ブラウザだけで使うなら import Papa from "papaparse"、Node でも同じ import で OK。Worker を使う場合(後述)は bundle 配布が必要(papaparse.min.js を public に置く)。
最小サンプル(PapaParse 5.x、3 行で CSV パース)
Papa.parse(csv, options) で文字列を 1 回でパース。header: true で 1 行目をキーとして解釈:
import Papa from "papaparse";
const csv = `name,age
Alice,30
Bob,25`;
const result = Papa.parse(csv, {
header: true, // 1 行目をキーとして使う
skipEmptyLines: true,
dynamicTyping: true, // "30" → 30(数値変換)
});
console.log(result.data);
// [{ name: "Alice", age: 30 }, { name: "Bob", age: 25 }]
ファイルから読む(ブラウザ)
<input type="file"> の File オブジェクトを Papa.parse に直接渡す。worker: true で別 thread、step で 1 行ずつ stream 処理:
const file = inputElement.files![0];
Papa.parse<Record<string, string>>(file, {
header: true,
worker: true, // 大きいファイルは worker で処理
step: (row) => {
// 1 行ずつストリーム処理(数 GB の CSV でも OK)
console.log(row.data);
},
complete: () => console.log("done"),
});
worker: true で別 thread で処理。UI を block しない。
ポイント:
- worker:true にすると
stepcallback は main thread で実行される(worker→main の postMessage 経由)。DOM 操作 / state 更新が安全 stepを入れたらdataは空配列:全部メモリに溜め込まないストリーム動作になる(complete(results)のresults.dataも空)- chunk size はブラウザ内部で自動調整、明示制御は基本不要
JSON → CSV(unparse)
逆方向(オブジェクト配列 → CSV 文字列)は Papa.unparse。quotes / delimiter 等を渡して書式を制御:
const csv = Papa.unparse([
{ name: "Alice", age: 30 },
{ name: "Bob", age: 25 },
], {
delimiter: ",",
quotes: true, // すべてのフィールドを quote
header: true,
});
文字コード判定 — Excel が吐く Shift_JIS / CP932 への対処
日本語 Windows の Excel から「CSV (カンマ区切り)」 で保存すると Shift_JIS / CP932 で出力される。UTF-8 前提で Papa.parse(file) に投げると、日本語が全部文字化けする(� の連鎖)。
実用的な対処は 3 通り:
import Papa from "papaparse";
// (1) Papa の encoding オプションを使う(brower の TextDecoder 経由で読み込み)
Papa.parse(file, { header: true, encoding: "Shift-JIS" });
// (2) 自前で TextDecoder を通す(BOM の有無で UTF-8 / SJIS を切り分けたい時)
const buf = await file.arrayBuffer();
const head = new Uint8Array(buf, 0, 3);
const hasBom = head[0] === 0xef && head[1] === 0xbb && head[2] === 0xbf;
const encoding = hasBom ? "utf-8" : "shift-jis";
const text = new TextDecoder(encoding).decode(buf);
const result = Papa.parse(text, { header: true });
// (3) ユーザに「UTF-8 で保存して」と注意する(技術者向け社内ツールはこれが楽)
TextDecoder の shift-jis ラベルは Web 標準で Windows-31J / CP932 を実装するので、実質 Excel の CSV を読める。Node 単体だと shift-jis decoder は標準では入っていないので、iconv-lite か @kayahr/text-encoding を別途入れる必要がある。
5 つの落とし穴と直し方
| 症状 | 原因 | 直し方 |
|---|---|---|
step 設定したのに complete(results) の results.data が空配列 | step を渡すと メモリに溜め込まない動作に切り替わる | step の callback 内で自前に配列に push、もしくは IndexedDB 等にストリーム書き出し |
"01"(郵便番号)が 1 になる | dynamicTyping: true で 数値変換される | dynamicTyping: { age: true, code: false } のように列単位で制御、または全部 false |
| 改行を含むセルが分裂する | 改行入りセルが quote されていない | 出力側で quotes: true を Papa.unparse に渡すか、CSV 仕様に従い "line1\nline2" で囲む |
| 巨大 CSV で UI が固まる | main thread でパースしている | worker: true を付ける(別 thread で実行、step が main thread に届く) |
TS で Papa.parse<T>(...) の戻り値型が緩い | header の型推論まではしてくれない | Papa.parse<{ name: string; age: number }>(csv, { header: true }) のように 明示で generic を当てる |
ストリーム + worker の組み合わせは「数百 MB の CSV を即座に行ごとに描画する」ような UX に向いている(Excel 互換の見た目を web で実装する時の定番)。
評価
評価
| 観点 | 評価 | コメント |
|---|---|---|
| 学習コスト | ◎ | API は parse / unparse の 2 つ |
| 性能 | ◎ | worker + step でブラウザで GB 級も処理可 |
| RFC 4180 互換 | ◎ | quote / 改行 / カンマすべて対応 |
| 型 | ○ | 結果型は generic で絞れる |
| バンドルサイズ | ○ | 50KB 前後(min+gzip) |
向く / 向かないケース
- 向く: ブラウザでユーザアップロード CSV 処理、Node スクリプト、ETL 前処理
- 向かない: TSV や独自区切りの軽量パース(
split('\n').map(s => s.split(','))で十分なケース) - 向かない: 厳密な型バリデーションが必要 → papaparse でパース後 zod で検証 が現実解
関連 Topic / 関連書籍
この記事と関係する tech-book.net の Topic と、それぞれの Topic に紐づく書籍:
JavaScriptによるはじめてのアルゴリズム入門 | tech-book.net
関連テーマ・学習ロードマップから次の一冊へ。
Python と JavaScriptではじめるデータビジュアライゼーション | tech-book.net
関連テーマ・学習ロードマップから次の一冊へ。
React Native+Expoではじめるスマホアプリ開発 : JavaScriptによるアプリ構築の実際 | tech-book.net
関連テーマ・学習ロードマップから次の一冊へ。
はじめてのWebデザイン&プログラミング : HTML、CSS、JavaScript、PHPの基本 | tech-book.net
関連テーマ・学習ロードマップから次の一冊へ。
フロントエンドの知識地図ーー 一冊でHTML/CSS/JavaScriptの開発技術が学べる本 | tech-book.net
関連テーマ・学習ロードマップから次の一冊へ。