LoginSignup
21
28

More than 5 years have passed since last update.

Node.jsでShift_JISのCSVを読み書きする

Posted at

はじめに

以前 Vue.jsとVue RouterとVuexとVuexFireとElectronとFirebaseとFoundation Sitesでアプリを作った時に、CSVの読み書きをする機能を実装しました。Shift_JISで出力するのに苦労したのは、今となっては良い思い出です。

必要なパッケージ

  • node-csv
  • iconv-lite

前者はCSVをパースしたり生成したりするのに使います。csv-parsecsv-stringify などの複数のパッケージに分かれていて、今回はそれらパッケージを個別に呼び出しました。

後者は文字コードのエンコード・デコードをするのに使います。iconvというパッケージもありますが、ネイティヴモジュールを内部で使用しているパッケージで、Electronでアプリのパッケージする時に面倒だなあと思って使いませんでした。

Shift_JISのCSVを読み込む

例えばこんな感じのCSVデータがあるとします。

名前 年齢 身長 体重
山田太郎 25 170 60
山田二郎 24 171 55
山田三郎 22 172 60
山田四郎 21 168 58

これを以下のようなJavaScriptのオブジェクトに変換します。
数字が文字列になっているところは、必要に応じて変換してください。

const data = [
  { fullname: '山田太郎', age: '25', height: '170', weight: '60' },
  { fullname: '山田二郎', age: '24', height: '171', weight: '55' },
  { fullname: '山田三郎', age: '22', height: '172', weight: '60' },
  { fullname: '山田四郎', age: '21', height: '168', weight: '58' }
]

このように変換するコードは以下の通りです。
ストリームAPIを使って比較的きれいに記述できていると思います。
rxjsとか使うともっと良く書けるのかなと思います。

import parse = require('csv-parse')
import fs = require('fs')
import iconv = require('iconv-lite')

const headers = {
  '名前': 'fullname',
  '年齢': 'age',
  '身長': 'height',
  '体重': 'weight'
}

type Dictionary<T> = { [key: string]: T }

function parseColumns(line: Dictionary<keyof typeof headers>): string[] {
  const table: string[] = []

  for (let key in line) {
    table.push(columnTable[line[key]])
  }

  return table
}

interface Person {
  fullname: string
  age: string
  height: string
  weight: string
}

/**
 * @param path CSVファイルが保存されているパス
 */
function parseCSV(path: string): Promise<Person[]> {
  return new Promise(resolve => {
    const parser = parse({ columns: parseColumns as any })

    const rs: fs.ReadStream = fs.createReadStream(path)
    rs.pipe(iconv.decodeStream('SJIS'))
      .pipe(iconv.encodeStream('UTF-8'))
      .pipe(parser)

    const people: Person[] = []

    parser.on('readable', () => {
      let data: any
      while (data = parser.read()) {
        people.push(data)
      }
    })

    parser.on('end', () => {
      resolve(people)
    })
  })
}

Shift_JISのCSVを出力する

今度は、読み込みの逆をやりたいと思います。
あんまり綺麗に書けてないですけど十分間に合ってるコードだと思います。
CSV読み込みの時のように、headerの対応表を辞書型で定義して、
WriteStream を使うコードを書きたかったなあと思いました。

import stringify = require('csv-stringify')
import fs = require('fs')
import iconv = require('iconv-lite')

interface Person {
  fullname: string
  age: string
  height: string
  weight: string
}

function saveCSV(path: string, people: Person[]) {
  const header: string[] = ['名前', '年齢', '身長', '体重']

  const rows: string[][] = people.map(person => {
    return [
      person.fullname,
      person.age,
      person.height,
      person.weight
    ]
  })

  stringily([header, ...rows], (_err, output) => {
    fs.writeFile(path, iconv.encode(output, 'shift_jis'))
  })
}

参考にしたサイト

https://solutionware.jp/blog/2016/09/29/node-jsでcsvファイル操作/
このサイトを参考にさせていただきました。

21
28
0

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
28