JavaScriptだけでCSVファイルを読み込む方法

2023年4月14日金曜日

javascript

t f B! P L

例えば、ブラウザで選択したCSVを読み込んで、その内容を画面の一覧に表示するWebアプリを作ろうと思った場合、従来のWebアプリでは一旦サーバー側にファイルを送信して、PHPなどのバックエンド言語で読み込み、再度ブラウザ側で一覧する流れが一般的でした。

今では、HTML5をはじめとするJS仕様の進化、サードパーティ製ライブラリの充実などにより、JavaScirptだけで「CSVファイルの読み込み」や「文字コード変換」が可能になった。つまり、ファイルをサーバー側に一切送信せず、上記のようなWebアプリが作成可能になりました。

では実際に、JavaScirptでCSVファイルを読み込む方法を見ていきましょう。

スポンサーリンク

ローカルで選択されたCSVファイルを読む

HTML 側にはファイル選択を置いておき、idは file としておく。

<input id="file" type="file" accept=".csv"/>

JavaScript でCSVファイルを読み込む処理を実装する。

document.getElementById("file").addEventListener("change", e => {
    const files = e.target.files

    //FileReaderの準備
    const reader = new FileReader();
    reader.addEventListener("load", () => {
      //ファイルから読み取った文字列を変数resultに格納
      const { result } = reader;

      //改行コードで区切って行の配列を作成
      const tmp = result.split("\n")

      //行ごとにカンマで区切り、2次元配列を作成
      const lines = tmp.map(line => line.split(','))
      console.log(lines)
    });

    //選択されたCSVを読み込み
    reader.readAsText(files[0])
})

上のコードを少し補足する。

まず、ファイルの読み込みには FIle APIの FileReader を使っており reader.readAsText(files[0]) の部分で選択されたファイルの内容を文字列として読み込んでいる。
ファイルの読み込みが完了すると addEventListener で登録したコールバック関数が呼び出される。その中で、ファイルの文字列を改行コードとカンマで区切った2次元配列を作成している。

このコードに少し問題があり、ダブルクォーテーションで各項目の値が囲まれたCSVの場合、ダブルクォーテーションが2次元配列のデータの中に入り込んでしまう。

ダブルクォーテーションで各項目の値が囲まれたCSVの場合

そもそも、CSVの一般的なルールでは、文字列はダブルクォーテーションで「囲む」、数値はダブルクォーテーションで「囲まない」なので、基本的にはデータがダブルクォーテーションで囲まれていることを前提に処理を実装する必要がある。

ダブルクォーテーションで囲まれたCSVにも対応

前述のコードを改良して、ダブルクォーテーションを取り除いて読み込むようにしたのが次のコードである。

document.getElementById("file").addEventListener("change", e => {
    const files = e.target.files

    //FileReaderの準備
    const reader = new FileReader();
    reader.addEventListener("load", () => {
      //ファイルから読み取った文字列を変数resultに格納
      const { result } = reader;

      //改行コードで区切って行の配列を作成
      const tmp = result.split("\n")

      //行ごとにカンマで区切り2次元配列を作成
      const lines = tmp
        .map(line => line.split(',')              //カンマで区切る
          .map(x => x.replace(/^"(.*)"$/, '$1'))  //前後のダブルクォーテーションを取る
        )  
      console.log(lines)
    });

    //選択されたCSVを読み込み
    reader.readAsText(files[0])
})

上のコードのポイントは以下である。

//行ごとにカンマで区切り2次元配列を作成
const lines = tmp
  .map(line => line.split(',')              //カンマで区切る
    .map(x => x.replace(/^"(.*)"$/, '$1'))  //前後のダブルクォーテーションを取る
  )  

まず、line.split(',') の部分で行をカンマで区切ったのち、次の行の x.replace(/^"(.*)"$/, '$1')) で前後のダブルクォーテーションを取り除いている。

実行すると、CSVから取得したデータからダブルクォーテーションが取り除かれているのが分かる。
ダブルクォーテーションが取り除かれた

スポンサーリンク

文字コードを変換する

日本では、CSVファイルは Shift-JIS の文字コードで保存されていることが割と多い。
ただ、JavaScript の文字列は内部的に UTF-16 として符号化されるため、Shift-JIS で保存されたCSVファイルを読み込むと、次のように文字化する。

文字化けしたCSV

JavaScriptで文字コード変換をする方法はいくつかあるが「encoding.js」というライブラリを使うのが便利である。「encoding.js」の使い方は以下で詳しく紹介しているので、こちらもどうぞ。

■関連記事
JavaScirptでファイルの文字コード変換ができる「encoding.js」

「encoding.js」のインストール

インストールはお馴染みの npm で。

npm install --save encoding-japanese

TypeScriptの場合は型定義もインストール。

npm install --save-dev @types/encoding-japanese

CSVファイルの文字コードを変換する

読み取ったCSVの文字コードを Unicode に変換してから読み込むようにしたのが以下のコードである。

document.getElementById("file").addEventListener("change", e => {
    const files = e.target.files

    //FileReaderの準備
    const reader = new FileReader()
    reader.addEventListener("load", () => {
      //ファイルから読み取った文字列を変数resultに格納
      const { result } = reader

      //文字コードをunicodeへ変換
      const conv = Encoding.convert(
        new Uint8Array(result as ArrayBuffer),
        {
          to: "UNICODE",
          from: 'AUTO',
        }
      )
      const unicodeStr = Encoding.codeToString(conv)

      //改行コードで区切って行の配列を作成
      const tmp = unicodeStr.split("\n")

      //行ごとにカンマで区切り2次元配列を作成
      const lines = tmp
        .map(line => line.split(',')              //カンマで区切る
          .map(x => x.replace(/^"(.*)"$/, '$1'))  //前後のダブルクォーテーションを取る
        )  
      console.log(lines)
    });

    //選択されたCSVを読み込み
    reader.readAsArrayBuffer(files[0])
})

文字コード変換しないコードに比べ、下記2点が異なる。

1つ目は、ファイルの内容をバイト配列として読み込むため FileReader の使用関数をreadAsArrayBufferにしている。

reader.readAsArrayBuffer(files[0])

2つ目は、「encoding.js」のEncoding.convet で文字コード変換をしている部分である。ここでは、ファイルの内容のバイト配列から文字コードを自動認識し、Unicodeで文字コード変換をしている。

//文字コードをunicodeへ変換
const conv = Encoding.convert(
  new Uint8Array(result),
  {
    to: "UNICODE",
    from: 'AUTO',
  }
)
const unicodeStr = Encoding.codeToString(conv)

Papa Parse ライブラリを使う

OSSライブラリを使う方法もあります。その一つに「Papa Parse」は JavaScirpt用のCSVパースライブラリがあり、主な特徴は以下です。(Chat GPT調べ)

  1. 大きなCSVファイルも高速にパースすることができます。
  2. UIの凍結を回避しながらバックグラウンドでパースを行うこともできます。
  3. CSVファイルの構造が複雑でも、様々なオプションを使用して簡単にパースすることができます。例えば、カラム数が不定の場合や、ヘッダーがない場合でも、適切な設定でパースすることができます。
  4. CSV以外にも、JSONやTSVなどのデータ形式にも対応しています。
  5. エラーハンドリングが容易:パース中に発生したエラーについて、わかりやすいエラーメッセージを提供してくれます。
  6. "ダブル""クォーテーション" のようにしてダブルクォーテーションのエスケープも可能

Papa Parseのインストール

まずはインストール

npm install papaparse

TypeScriptの人は型定義も

npm install --save @types/papaparse

CDN経由で読み込ませる場合は以下のタグをHTMLに挿入

<script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.4.1/papaparse.min.js" integrity="sha512-dfX5uYVXzyU8+KHqj8bjo7UkOdg18PaOtpa48djpNbZHwExddghZ+ZmzWT06R5v6NSk3ZUfsH6FNEDepLx9hPQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

CSVの読み込み

では、Papa Parseを使って <input type="file"> で選択されたCSVファイルを読み込むコードを書いてみる。

このように画面で選択されたファイルを Papa Parseの関数に突っ込むだけなので、ファイルを読み込むための非同期処理などが無くなりコードがシンプルになる。

document.getElementById("file").addEventListener("change", e => {
    const files = e.target.files

    Papa.parse(files[0], {
      header: true, // ヘッダー行がある場合はtrue
      complete: function(results) {
        // パースが完了したら、結果を表示する
        console.log(results)
      }
    })
})

Shift-JISなどのUnicode以外の文字コードには非対応

残念ながら Papa Parse は Shift-JISなどのUnicode 以外の文字コードには対応していない。なので、Shift-JIS の CSV が多い日本の事情においては、Papa Parseを使う前に前述した「encoding.js」のライブラリを使ってUnicodeへ文字コード変換しておく必要があるだろう。

document.getElementById("file").addEventListener("change", e => {
    const files = e.target.files

    //FileReaderの準備
    const reader = new FileReader();
    reader.addEventListener("load", () => {
      const { result } = reader;

      //文字コードをunicodeへ変換
      const conv = Encoding.convert(
        new Uint8Array(result),
        {
          to: "UNICODE",
          from: 'AUTO',
        }
      )
      const unicodeStr = Encoding.codeToString(conv)

      //ParaParseを使ってUnicode文字列のCSVをパース
      Papa.parse(unicodeStr, {
        header: true, // ヘッダー行がある場合はtrue
        complete: function(results) {
          // パースが完了したら、結果を表示する
          console.log(results);
        }
      })
    })

    reader.readAsArrayBuffer(files[0])
})

うーむ、結局コードが長くなっているような…

まとめ

JavaScriptだけでCSVファイルを読み込む方法を紹介してきました。File APIと「encoding.js」といった文字コード変換ライブラリを使用すれば、CSVファイルの読み込みをクライアントサイドだけで実現できます。特に、CSVファイルを読み込む処理では、カラム数のチェックや必須入力・属性チェックが必要となりますが、これをクライアントサイドに委ねることができると、サーバーリソースを節約することができるため、メリットがあります。

スポンサーリンク
スポンサーリンク

このブログを検索

Profile

自分の写真
Webアプリエンジニア。 日々新しい技術を追い求めてブログでアウトプットしています。
プロフィール画像は、猫村ゆゆこ様に書いてもらいました。

仕事募集もしていたり、していなかったり。

QooQ