Node.jsでファイルの読み書きします。
File System | Node.js v10.11.0 Documentation
fsモジュール
fsモジュールを使って読み書きします。非同期コールバック、同期、非同期プロミスバージョンという感じで、同じ処理に3つの関数がある場合が多いっぽい。基本非同期を使うことが推奨されている。
非同期関数たち
readもwirteも、同じような種類の関数がある。readはファイルの一部を任意の長さで取得するようなことができる。readFileはファイルを全部取得する。readStreamは、つなぎっぱにして頭から順番に読み込める。何かの処理を連続して行ったり、ファイルサイズが大きい場合などは、readStreamがいい。下記は非同期コールバック関数たちですが、下記に対応した、Promiseバージョンもある。
読み込み関数
書き込み関数
使ってみる
fs.readFile
ファイルをまるっと読み込むものなので、使い方は単純です。ファイル名の次の引数に文字エンコードをつけると、文字列が取得できます。
fs.readFile('message.txt', (err, data) => { if (err) throw err console.log(data) }) fs.readFile('message.txt', 'utf8', (err, data) => { if (err) throw err console.log(data) })
結果
<Buffer 61 62 63 64 0a 31 32 33 34 0a e3 81 82 e3 81 84 e3 81 86 e3 81 88 0a> abcd 1234 あいうえ
fs.read
特定の位置だけ取得したいといったときに使うのかな?最初の500バイトだけ取得したいとか。
const hoge = Buffer.alloc(100) fs.open('wp.xml', 'r', (err, fd) => { if (err) throw err fs.read(fd, hoge, 0, 50, 0, (err, bytesRead, buffer) => { if (err) throw err console.log(buffer.toString('utf8', 0, 100)) }) fs.close(fd, err => { if (err) throw err }) })
結果
<?xml version="1.0" encoding="UTF-8" ?> <!-- This
fs.ReadStream
順番にちょっとずつ取得するやつ。メモリに優しい。
const stream = fs.createReadStream('wp.xml') let count = 0 let totalSize = 0 stream.on('data', chunk => { console.log(chunk.toString('utf8', 0, 100)) count++ totalSize += chunk.length }) stream.on('end', () => { console.log(count) console.log(totalSize) })
結果
(.... 沢山の文字列...) 123 8004493
あとは、pipeというのも使える。
const r = fs.createReadStream('wp.xml') const z = zlib.createGzip() const w = fs.createWriteStream('wp.gz') r.pipe(z).pipe(w)
結果は、wp.gzが作成されます。
fs.writeFile
ファイルに書きます。まるっと書きます。
const data = 'こんにちは' fs.writeFile('hello.txt', data, err => { if (err) throw err }) fs.readFile('hello.txt', 'utf8', (err, data) => { if (err) throw err console.log(data) })
結果
こんにちは
fs.wirte
一部だけ書き換えるみたいなときに使うのかな?効率よく、「こんにチは」に変えるみたいな。
fs.open('hello.txt', 'r+', (err, fd) => { if (err) throw err fs.write(fd, 'チ', 9, 'utf8', (err, written, str) => { if (err) throw err }) })
おーできた。こんにチはになった。でも、r+とかのモードによって、全然挙動が違うし、OSによっても全然違うらしいから、注意が必要そう。。しかも、これ上書きされるけど、純粋に1文字追加したい場合はどうしたらいいんだろう?
fs.open('hello.txt', 'r+', (err, fd) => { if (err) throw err const chi = Buffer.from('あいチえお') console.log(chi) fs.write(fd, chi, 6, 3, 9, (err, bytesWritten, buffer) => { if (err) throw err }) })
ちなみに、これでもできた。ちょっとだけfs.writeの引数が違う。でも、やっぱり純粋1文字挿入はできない。挿入というのは、挿入する箇所以降の文字列をコピーして、場所を移動させた上で、上記を実行するみたいなことが必要なのかもしれん。
fs.WriteStream
ファイルを完全上書きでよければ、こんな感じで使える。
const stream = fs.createWriteStream('hello.txt') const arr = [1, 2, 3, 4, 5] arr.forEach(num => stream.write(num + "\n")) stream.end()
下記のようにモードの設定ができる
const arr = [6, 7] const options = { 'flags': 'r+', 'encoding': 'utf8' } const stream = fs.createWriteStream('hello.txt', options) arr.forEach(num => stream.write(num + "\n")) stream.end()
pipe
pipeは便利そうなので、もうちょい試してみる。
const read = fs.createReadStream('wp.xml') const write = fs.createWriteStream('new.xml') read.pipe(write)
これだと、単純にwp.xmlの内容が、new.xmlに書き込まれる。readとwriteの間に何かを挟むことで便利になります。例えば「全部大文字に変換してから書き込む」というのをやってみたいと思います。
const reader = fs.createReadStream('hello.txt') const writer = fs.createWriteStream('new.txt') const { Transform } = require('stream') class UpperCaseStream extends Transform { write (data) { data = data.toString().toUpperCase() this.emit('data', data) } } const upper = new UpperCaseStream() reader.pipe(upper).pipe(writer)
これでできた。