Node.jsでストリームを使用してファイルを操作する方法
著者は、 Write for DOnations プログラムの一環として、 Girls WhoCodeを選択して寄付を受け取りました。
序章
コンピューティングにおけるストリームの概念は、通常、安定した連続的なフローでのデータの配信を表します。 ソースからの読み取りまたはソースへの書き込みにストリームを継続的に使用できるため、すべてのデータを一度にメモリに収める必要がなくなります。
ストリームを使用すると、2つの大きな利点があります。 1つは、処理を開始する前にすべてのデータをメモリにロードする必要がないため、メモリを効率的に使用できることです。 もう1つの利点は、ストリームの使用が時間効率に優れていることです。 ペイロード全体を待つのではなく、ほぼすぐにデータの処理を開始できます。 これらの利点により、ストリームはI/O操作での大規模なデータ転送に適したツールになります。 ファイルは、いくつかのデータを含むバイトのコレクションです。 ファイルはNode.jsの一般的なデータソースであるため、ストリームはNode.jsのファイルを効率的に操作する方法を提供します。
Node.jsは、ストリームを操作するためのコアNode.jsモジュールであるstream
モジュールでストリーミングAPIを提供します。 すべてのNode.jsストリームは、EventEmitter
クラスのインスタンスです(詳細については、 Node.js でのイベントエミッターの使用を参照してください)。 これらは、データ送信プロセス中にさまざまな間隔でリッスンできるさまざまなイベントを発行します。 ネイティブstream
モジュールは、データの読み取りと書き込み、送信ライフサイクルの管理、および送信エラーの処理に使用できるイベントをリッスンするためのさまざまな機能で構成されるインターフェイスを提供します。
Node.jsには4種類のストリームがあります。 彼らです:
- 読み取り可能なストリーム:データを読み取ることができるストリーム。
- 書き込み可能なストリーム:データを書き込むことができるストリーム。
- デュプレックスストリーム:読み取りと書き込みが可能なストリーム(通常は同時に)。
- 変換ストリーム:出力(または書き込み可能なストリーム)が入力(または読み取り可能なストリーム)の変更に依存しているデュプレックスストリーム。
ファイルシステムモジュール(fs
)は、ファイルを操作し、一般的にローカルファイルシステムをナビゲートするためのネイティブNode.jsモジュールです。 これを行うためのいくつかの方法を提供します。 これらのメソッドのうちの2つは、ストリーミングAPIを実装します。 これらは、ストリームを使用してファイルを読み書きするためのインターフェイスを提供します。 これらの2つの方法を使用して、読み取りおよび書き込み可能なファイルストリームを作成できます。
この記事では、fs.createReadStream
およびfs.createWriteStream
関数を使用してファイルの読み取りと書き込みを行います。 また、あるストリームの出力を別のストリームの入力として使用し、カスタム変換スチームを実装します。 これらのアクションを実行することで、ストリームを使用してNode.jsのファイルを操作する方法を学びます。 これらの概念を示すために、Linuxベースのシステムにあるcat
機能を複製し、端末からファイルに入力を書き込み、ファイルをコピーし、コンテンツを変換するコマンドを使用してコマンドラインプログラムを作成します。ファイル。
前提条件
このチュートリアルを完了するには、次のものが必要です。
- 開発マシンにインストールされているNode.js。 このチュートリアルでは、バージョン14.10.0を使用します。 さまざまなプラットフォームへのインストールについては、チュートリアルシリーズNode.jsをインストールしてローカル開発環境を作成する方法を参照してください。
- 私たちのシリーズNode.jsでコーディングする方法で見つけることができるNode.jsの基本的な知識。
- Node.js
fs
モジュールの基本的な知識。これは、Node.jsのfsモジュールを使用したファイルの操作方法にあります。
ブラウザの端末を使用してこのチュートリアルのコマンドを試してみたい場合は、次のインタラクティブ端末の起動!ボタンをクリックして開始してください。
インタラクティブターミナルを起動します!
ステップ1—ファイル処理コマンドラインプログラムの設定
このステップでは、基本的なコマンドを使用してコマンドラインプログラムを作成します。 このコマンドラインプログラムは、チュートリアルの後半で学習する概念を示します。ここでは、ファイルを操作するために作成する関数でこれらのコマンドを使用します。
まず、このプログラムのすべてのファイルを含むフォルダーを作成します。 ターミナルで、node-file-streams
という名前のフォルダーを作成します。
mkdir node-file-streams
cd
コマンドを使用して、作業ディレクトリを新しいフォルダに変更します。
cd node-file-streams
次に、お気に入りのテキストエディタでmycliprogram
というファイルを作成して開きます。 このチュートリアルでは、ターミナルテキストエディタであるGNUnano
を使用します。 nanoを使用してファイルを作成して開くには、次のコマンドを入力します。
nano mycliprogram
テキストエディタで、次のコードを追加してshebangを指定し、Node.jsプロセスからのコマンドライン引数の配列を格納し、アプリケーションに必要なコマンドのリストを格納します。
node-file-streams / mycliprogram
#!/usr/bin/env node const args = process.argv; const commands = ['read', 'write', 'copy', 'reverse'];
最初の行には、プログラムインタープリターへのパスであるシバンが含まれています。 この行を追加すると、プログラムローダーはNode.jsを使用してこのプログラムを解析するようになります。
コマンドラインでNode.jsスクリプトを実行すると、Node.jsプロセスの実行時にいくつかのコマンドライン引数が渡されます。 これらの引数には、argv
プロパティまたはNode.jsprocess
を使用してアクセスできます。 argv
プロパティは、Node.jsスクリプトに渡されるコマンドライン引数を含む配列です。 2行目では、そのプロパティをargs
という変数に割り当てます。
次に、getHelpText
関数を作成して、プログラムの使用方法のマニュアルを表示します。 以下のコードをmycliprogram
ファイルに追加します。
node-file-streams / mycliprogram
... const getHelpText = function() { const helpText = ` simplecli is a simple cli program to demonstrate how to handle files using streams. usage: mycliprogram <command> <path_to_file> <command> can be: read: Print a file's contents to the terminal write: Write a message from the terminal to a file copy: Create a copy of a file in the current directory reverse: Reverse the content of a file and save its output to another file. <path_to_file> is the path to the file you want to work with. `; console.log(helpText); }
getHelpText
関数は、プログラムのヘルプテキストとして作成した複数行の文字列を出力します。 ヘルプテキストには、プログラムが期待するコマンドライン引数またはパラメータが表示されます。
次に、制御ロジックを追加してargs
の長さを確認し、適切な応答を提供します。
node-file-streams / mycliprogram
... let command = ''; if(args.length < 3) { getHelpText(); return; } else if(args.length > 4) { console.log('More arguments provided than expected'); getHelpText(); return; } else { command = args[2] if(!args[3]) { console.log('This tool requires at least one path to a file'); getHelpText(); return; } }
上記のコードスニペットでは、端末から受信したコマンドを格納するための空の文字列command
を作成しました。 最初のif
ブロックは、args
配列の長さが3未満かどうかをチェックします。 3未満の場合は、プログラムの実行時に他の追加の引数が渡されなかったことを意味します。 この場合、ヘルプテキストを端末に出力して終了します。
else if
ブロックは、args
配列の長さが4より大きいかどうかを確認します。 そうである場合、プログラムは必要以上の引数を受け取りました。 プログラムは、ヘルプテキストとともにこの趣旨のメッセージを出力して終了します。
最後に、else
ブロックで、3番目の要素または[X106X]変数のargs
配列の2番目のインデックスにある要素を格納します。 このコードは、args
配列に4番目の要素またはインデックス=3の要素があるかどうかもチェックします。 アイテムが存在しない場合は、続行するにはファイルパスが必要であることを示すメッセージが端末に出力されます。
ファイルを保存します。 次に、アプリケーションを実行します。
./mycliprogram
以下の出力のようなpermission denied
エラーが発生する可能性があります。
Output-bash: ./mycliprogram: Permission denied
このエラーを修正するには、ファイルに実行権限を付与する必要があります。これは、次のコマンドで実行できます。
chmod +x mycliprogram
ファイルを再実行してください。 出力は次のようになります。
Outputsimplecli is a simple cli program to demonstrate how to handle files using streams. usage: mycliprogram <command> <path_to_file> read: Print a file's contents to the terminal write: Write a message from the terminal to a file copy: Create a copy of a file in the current directory reverse: Reverse the content of a file and save it output to another file.
最後に、前に作成したcommands
配列にコマンドを部分的に実装します。 mycliprogram
ファイルを開き、以下のコードを追加します。
node-file-streams / mycliprogram
... switch(commands.indexOf(command)) { case 0: console.log('command is read'); break; case 1: console.log('command is write'); break; case 2: console.log('command is copy'); break; case 3: console.log('command is reverse'); break; default: console.log('You entered a wrong command. See help text below for supported functions'); getHelpText(); return; }
switchステートメントで見つかったコマンドを入力するたびに、プログラムはそのコマンドに適切なcaseブロックを実行します。 この部分的な実装では、コマンドの名前を端末に出力します。 文字列が上記で作成したコマンドのリストにない場合、プログラムはヘルプテキストとともにその旨のメッセージを出力します。 その後、プログラムは終了します。
ファイルを保存し、read
コマンドと任意のファイル名を使用してプログラムを再実行します。
./mycliprogram read test.txt
出力は次のようになります。
Outputcommand is read
これで、コマンドラインプログラムが正常に作成されました。 次のセクションでは、createReadStream()
を使用して、アプリケーションでcat
機能をread
コマンドとして複製します。
ステップ2—createReadStream()
でファイルを読み取る
コマンドラインアプリケーションのread
コマンドは、ファイルシステムからファイルを読み取り、Linuxベースの端末のcat
コマンドと同様に端末に出力します。 このセクションでは、fs
モジュールのcreateReadStream()
を使用してその機能を実装します。
createReadStream
関数は、EventsEmitter
クラスから継承するため、リッスンできるイベントを発行する読み取り可能なストリームを作成します。 data
イベントは、これらのイベントの1つです。 読み取り可能なストリームがデータを読み取るたびに、data
イベントを発行し、データを解放します。 コールバック関数とともに使用すると、そのデータまたはchunk
を使用してコールバックが呼び出され、そのコールバック関数内でそのデータを処理できます。 この場合、そのチャンクをターミナルに表示します。
まず、簡単にアクセスできるように、作業ディレクトリにテキストファイルを追加します。 このセクションとそれに続くいくつかのセクションでは、lorem-ipsum.txt
というファイルを使用します。 これは、 Lorem Ipsum Generator を使用して生成された約1200行のloremipsumテキストを含むテキストファイルであり、GitHubでホストされます。 ターミナルで、次のコマンドを入力して、ファイルを作業ディレクトリにダウンロードします。
wget https://raw.githubusercontent.com/do-community/node-file-streams/999e66a11cd04bc59843a9c129da759c1c515faf/lorem-ipsum.txt
コマンドラインアプリケーションでcat
機能を複製するには、必要なcreateReadStream
関数が含まれているため、fs
モジュールをインポートする必要があります。 これを行うには、mycliprogram
ファイルを開き、shebangの直後に次の行を追加します。
node-file-streams / mycliprogram
#!/usr/bin/env node const fs = require('fs');
次に、switch
ステートメントの下にread()
という関数を作成します。この関数は、読み取りたいファイルのファイルパスという1つのパラメーターを使用して作成します。 この関数は、そのファイルから読み取り可能なストリームを作成し、そのストリームでdata
イベントをリッスンします。
node-file-streams / mycliprogram
... function read(filePath) { const readableStream = fs.createReadStream(filePath); readableStream.on('error', function (error) { console.log(`error: ${error.message}`); }) readableStream.on('data', (chunk) => { console.log(chunk); }) }
このコードは、error
イベントをリッスンすることによってエラーもチェックします。 エラーが発生すると、エラーメッセージが端末に出力されます。
最後に、以下のコードブロックに示すように、最初のケースブロックcase 0
で、console.log()
をread()
関数に置き換える必要があります。
node-file-streams / mycliprogram
... switch (command){ case 0: read(args[3]); break; ... }
ファイルを保存して新しい変更を保持し、プログラムを実行します。
./mycliprogram read lorem-ipsum.txt
出力は次のようになります。
Output<Buffer 0a 0a 4c 6f 72 65 6d 20 69 70 73 75 6d 20 64 6f 6c 6f 72 20 73 69 74 20 61 6d 65 74 2c 20 63 6f 6e 73 65 63 74 65 74 75 72 20 61 64 69 70 69 73 63 69 ... > ... <Buffer 76 69 74 61 65 20 61 6e 74 65 20 66 61 63 69 6c 69 73 69 73 20 6d 61 78 69 6d 75 73 20 75 74 20 69 64 20 73 61 70 69 65 6e 2e 20 50 65 6c 6c 65 6e 74 ... >
上記の出力に基づいて、データがチャンクまたはピースで読み取られ、これらのデータがBuffer
タイプであることがわかります。 簡潔にするために、上記の端末出力には2つのチャンクのみが表示され、省略記号は、ここに表示されているチャンクの間にいくつかのバッファーがあることを示しています。 ファイルが大きいほど、バッファーまたはチャンクの数が多くなります。
人間が読める形式でデータを返すには、createReadStream()
関数に2番目の引数として必要なエンコードタイプの文字列値を渡して、データのエンコードタイプを設定します。 createReadStream()
関数の2番目の引数に、次の強調表示されたコードを追加して、エンコードタイプをutf8
に設定します。
node-file-streams / mycliprogram
... const readableStream = fs.createReadStream(filePath, 'utf8') ...
プログラムを再実行すると、ターミナルにファイルの内容が表示されます。 プログラムは、lorem-ipsum.txt
ファイルのloremipsumテキストを、ファイルに表示されているとおりに1行ずつ出力します。
OutputLorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean est tortor, eleifend et enim vitae, mattis condimentum elit. In dictum ex turpis, ac rutrum libero tempus sed... ... ...Quisque nisi diam, viverra vel aliquam nec, aliquet ut nisi. Nullam convallis dictum nisi quis hendrerit. Maecenas venenatis lorem id faucibus venenatis. Suspendisse sodales, tortor ut condimentum fringilla, turpis erat venenatis justo, lobortis egestas massa massa sed magna. Phasellus in enim vel ante viverra ultricies.
上記の出力は、端末に印刷されたファイルの内容のごく一部を示しています。 端末の出力をlorem-ipsum.txt
ファイルと比較すると、cat
コマンドの場合と同様に、内容がファイルと同じで、同じ形式であることがわかります。
このセクションでは、コマンドラインプログラムにcat
機能を実装して、ファイルの内容を読み取り、createReadStream
関数を使用して端末に出力しました。 次のステップでは、createWriteStream()
を使用して端末からの入力に基づいてファイルを作成します。
ステップ3—createWriteStream()
を使用してファイルに書き込む
このセクションでは、createWriteStream()
を使用して端末からファイルに入力を書き込みます。 createWriteStream
関数は、データを書き込むことができる書き込み可能なファイルストリームを返します。 前の手順の読み取り可能なストリームと同様に、この書き込み可能なストリームは、error
、finish
、pipe
などの一連のイベントを発行します。 さらに、データをチャンクまたはビットでストリームに書き込むためのwrite
関数を提供します。 write
関数は、chunk
を取り込みます。これは、文字列、Buffer
、<Uint8Array>
、またはその他のJavaScript値です。 チャンクが文字列の場合は、エンコーディングタイプを指定することもできます。
端末からファイルに入力を書き込むには、コマンドラインプログラムでwrite
という関数を作成します。 この関数では、端末から入力を受け取り(ユーザーが入力を終了するまで)、データをファイルに書き込むプロンプトを作成します。
まず、mycliprogram
ファイルの先頭にあるreadline
モジュールをインポートする必要があります。 readline
モジュールはネイティブのNode.jsモジュールであり、標準入力(stdin
)や端末などの読み取り可能なストリームから一度に1行ずつデータを受信するために使用できます。 mycliprogram
ファイルを開き、強調表示された行を追加します。
node-file-streams / mycliprogram
#!/usr/bin/env node const fs = require('fs'); const readline = require('readline');
次に、read()
関数の下に次のコードを追加します。
node-file-streams / mycliprogram
... function write(filePath) { const writableStream = fs.createWriteStream(filePath); writableStream.on('error', (error) => { console.log(`An error occured while writing to the file. Error: ${error.message}`); }); }
ここでは、filePath
パラメーターを使用して書き込み可能なストリームを作成しています。 このファイルパスは、write
ワードの後のコマンドライン引数になります。 また、問題が発生した場合(たとえば、存在しないfilePath
を指定した場合)にエラーイベントをリッスンします。
次に、端末からメッセージを受信するプロンプトを作成し、前にインポートしたreadline
モジュールを使用して、指定されたfilePath
にメッセージを書き込みます。 リードラインインターフェイスとプロンプトを作成し、line
イベントをリッスンするには、ブロックに示すようにwrite
関数を更新します。
node-file-streams / mycliprogram
... function write(filePath) { const writableStream = fs.createWriteStream(filePath); writableStream.on('error', (error) => { console.log(`An error occured while writing to the file. Error: ${error.message}`); }); const rl = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: 'Enter a sentence: ' }); rl.prompt(); rl.on('line', (line) => { switch (line.trim()) { case 'exit': rl.close(); break; default: sentence = line + '\n' writableStream.write(sentence); rl.prompt(); break; } }).on('close', () => { writableStream.end(); writableStream.on('finish', () => { console.log(`All your sentences have been written to ${filePath}`); }) setTimeout(() => { process.exit(0); }, 100); }); }
プログラムが端末から標準入力(stdin
)を行ごとに読み取り、指定されたprompt
を書き込むことができるreadlineインターフェース(rl
)を作成しました。 ]文字列から標準出力(stdout
)。 また、prompt()
関数を呼び出して、構成されたprompt
メッセージを新しい行に書き込み、ユーザーが追加の入力を提供できるようにしました。
次に、rl
インターフェイスで2つのイベントリスナーをチェーンしました。 1つ目は、入力ストリームが行末入力を受信するたびに発行されるline
イベントをリッスンします。 この入力は、改行文字(\n
)、復帰文字(\r
)、または両方の文字を合わせたもの(\r\n
)であり、通常はを押したときに発生します。コンピュータのENTER
またはリターン↵
キー。 したがって、端末に入力しているときにこれらのキーのいずれかを押すと、line
イベントが発生します。 コールバック関数は、入力line
の1行を含む文字列を受け取ります。
行をトリミングして、それがexit
という単語であるかどうかを確認しました。 そうでない場合、プログラムはline
に改行文字を追加し、.write()
関数を使用してsentence
をfilePath
に書き込みます。 次に、プロンプト関数を呼び出して、ユーザーに別のテキスト行を入力するように促しました。 line
がexit
の場合、プログラムはrl
インターフェースでclose
関数を呼び出します。 close
関数は、rl
インスタンスを閉じ、標準の入力(stdin
)および出力(stdout
)ストリームを解放します。
この関数は、rl
インスタンスでリッスンした2番目のイベントclose
イベントに移動します。 このイベントは、rl.close()
を呼び出すと発生します。 ストリームにデータを書き込んだ後、ストリームでend
関数を呼び出して、書き込み可能なストリームにデータを書き込まないようにプログラムに指示する必要があります。 これを行うと、データが出力ファイルに完全にフラッシュされます。 したがって、exit
という単語を入力すると、rl
インスタンスを閉じ、end
関数を呼び出して書き込み可能なストリームを停止します。
プログラムが端末から指定されたfilePath
にすべてのテキストを正常に書き込んだことをユーザーにフィードバックするために、writableStream
でfinish
イベントをリッスンしました。 コールバック関数では、書き込みが完了したときにユーザーに通知するメッセージを端末に記録しました。 最後に、finish
イベントがフィードバックを提供するのに十分な時間を提供するために、100ミリ秒後にプロセスを終了しました。
最後に、mycliprogram
でこの関数を呼び出すには、switch
ステートメントのcase 1
ブロックのconsole.log
ステートメントを新しいwrite
に置き換えます。 ]関数、ここに示すように:
node-file-streams / mycliprogram
... switch (command){ ... case 1: write(args[3]); break; ... }
新しい変更を含むファイルを保存します。 次に、write
コマンドを使用して、ターミナルでコマンドラインアプリケーションを実行します。
./mycliprogram write output.txt
Enter a sentence
プロンプトで、必要な入力を追加します。 いくつか入力した後、exit
と入力します。
出力は次のようになります(強調表示された行の代わりに入力が表示されます)。
OutputEnter a sentence: Twinkle, twinkle, little star Enter a sentence: How I wonder what you are Enter a sentence: Up above the hills so high Enter a sentence: Like a diamond in the sky Enter a sentence: exit All your sentences have been written to output.txt
output.txt
をチェックして、前に作成したread
コマンドを使用してファイルの内容を確認します。
./mycliprogram read output.txt
ターミナル出力には、exit
を除くコマンドに入力したすべてのテキストが含まれている必要があります。 上記の入力に基づいて、output.txt
ファイルには次の内容が含まれています。
OutputTwinkle, twinkle, little star How I wonder what you are Up above the hills so high Like a diamond in the sky
このステップでは、ストリームを使用してファイルに書き込みました。 次に、コマンドラインプログラムでファイルをコピーする関数を実装します。
ステップ4—pipe()
を使用してファイルをコピーする
このステップでは、pipe
関数を使用して、ストリームを使用してファイルのコピーを作成します。 ストリームを使用してファイルをコピーする方法は他にもありますが、データフローを管理する必要がないため、pipe
を使用することをお勧めします。
たとえば、ストリームを使用してファイルをコピーする1つの方法は、ファイルの読み取り可能なストリームを作成し、data
イベントでストリームをリッスンし、ストリームイベントから各chunk
をに書き込むことです。ファイルコピーの書き込み可能なストリーム。 以下のスニペットは例を示しています。
example.js
const fs = require('fs'); const readableStream = fs.createReadStream('lorem-ipsum.txt', 'utf8'); const writableStream = fs.createWriteStream('lorem-ipsum-copy.txt'); readableStream.on('data', () => { writableStream.write(chunk); }); writableStream.end();
この方法の欠点は、読み取り可能ストリームと書き込み可能ストリームの両方でイベントを管理する必要があることです。
ストリームを使用してファイルをコピーするための推奨される方法は、pipe
を使用することです。 配管パイプは、水タンク(出力)などの水源から蛇口または蛇口(入力)に水を渡します。 同様に、pipe
を使用して、データを出力ストリームから入力ストリームに転送します。 (Linuxベースのbashシェルに精通している場合は、pipe |
コマンドがデータを1つのストリームから別のストリームに転送します。)
Node.jsのパイピングは、最初の方法を使用する場合のようにデータフローを管理することなく、ソースからデータを読み取り、別の場所に書き込む機能を提供します。 前のアプローチとは異なり、読み取り可能ストリームと書き込み可能ストリームの両方でイベントを管理する必要はありません。 このため、ストリームを使用するコマンドラインアプリケーションにコピーコマンドを実装するための推奨されるアプローチです。
mycliprogram
ファイルに、ユーザーがcopy
コマンドライン引数を使用してプログラムを実行したときに呼び出される新しい関数を追加します。 copyメソッドは、pipe()
を使用して、入力ファイルからファイルの宛先コピーにコピーします。 以下に示すように、write
関数の後にcopy
関数を作成します。
node-file-streams / mycliprogram
... function copy(filePath) { const inputStream = fs.createReadStream(filePath) const fileCopyPath = filePath.split('.')[0] + '-copy.' + filePath.split('.')[1] const outputStream = fs.createWriteStream(fileCopyPath) inputStream.pipe(outputStream) outputStream.on('finish', () => { console.log(`You have successfully created a ${filePath} copy. The new file name is ${fileCopyPath}.`); }) }
コピー機能では、fs.createReadStream()
を使用して入力または読み取り可能なストリームを作成しました。 また、宛先の新しい名前を生成し、ファイルのコピーを出力し、fs.createWriteStream()
を使用して出力または書き込み可能なストリームを作成しました。 次に、.pipe()
を使用して、inputStream
からoutputStream
にデータをパイプ処理しました。 最後に、finish
イベントをリッスンし、ファイルのコピーが成功したときにメッセージを出力しました。
書き込み可能なストリームを閉じるには、ストリームでend()
関数を呼び出す必要があることを思い出してください。 ストリームをパイピングする場合、読み取り可能なストリーム(inputStream
)がend
イベントを発行すると、書き込み可能なストリーム(outputStream
)でend()
関数が呼び出されます。 書き込み可能ストリームのend()
関数は、finish
イベントを発行し、このイベントをリッスンして、ファイルのコピーが終了したことを示します。
この関数の動作を確認するには、mycliprogram
ファイルを開き、switch
ステートメントのcase 2
ブロックを次のように更新します。
node-file-streams / mycliprogram
... switch (command){ ... case 2: copy(args[3]); break; ... }
switch
ステートメントのcase 2
ブロックでcopy
関数を呼び出すと、mycliprogram
プログラムをcopy
コマンドと必要なファイルパスがあれば、copy
関数が実行されます。
mycliprogram
を実行します:
./mycliprogram copy lorem-ipsum.txt
出力は次のようになります。
OutputYou have successfully created a lorem-ipsum-copy.txt copy. The new file name is lorem-ipsum-copy.txt.
node-file-streams
フォルダー内に、lorem-ipsum-copy.txt
という名前の新しく追加されたファイルが表示されます。
pipe
を使用して、コマンドラインプログラムにコピー機能を正常に追加しました。 次のステップでは、ストリームを使用してファイルのコンテンツを変更します。
ステップ5—Transform()
を使用してファイルの内容を元に戻す
このチュートリアルの前の3つのステップでは、fs
モジュールを使用してストリームを操作しました。 このセクションでは、変換ストリームを提供するネイティブstream
モジュールのTransform()
クラスを使用してファイルストリームを変更します。 変換ストリームを使用して、データの読み取り、データの操作、および出力としての新しいデータの提供を行うことができます。 したがって、出力は入力データの「変換」です。 変換ストリームを使用するNode.jsモジュールには、暗号化用のcrypto
モジュールと、ファイルの圧縮および解凍用のgzip
を備えたzlib
モジュールが含まれます。
Transform()
抽象クラスを使用してカスタム変換ストリームを実装します。 作成する変換ストリームは、ファイルの内容を1行ずつ反転します。これは、変換ストリームを使用してファイルの内容を必要に応じて変更する方法を示しています。
mycliprogram
ファイルに、ユーザーがreverse
コマンドライン引数を渡したときにプログラムが呼び出すreverse
関数を追加します。
まず、ファイルの先頭にあるTransform()
クラスを他のインポートの下にインポートする必要があります。 以下に示すように、強調表示された行を追加します。
mycliprogram
#!/usr/bin/env node ... const stream = require('stream'); const Transform = stream.Transform || require('readable-stream').Transform;
v0.10
より前のNode.jsバージョンでは、Transform
抽象クラスがありません。 したがって、上記のコードブロックにはreadable-streams
ポリフィルが含まれているため、このプログラムは以前のバージョンのNode.jsで動作します。 Node.jsのバージョンが> 0.10
の場合、プログラムは抽象クラスを使用し、そうでない場合はポリフィルを使用します。
注:Node.jsバージョン< 0.10
を使用している場合は、npm init -y
を実行してpackage.json
ファイルを作成し、 npm install readable-stream
を、ポリフィルを適用するための作業ディレクトリに移動します。
次に、copy
関数のすぐ下にreverse
関数を作成します。 この関数では、filePath
パラメーターを使用して読み取り可能なストリームを作成し、反転ファイルの名前を生成し、その名前を使用して書き込み可能なストリームを作成します。 次に、Transform()
クラスのインスタンスであるreverseStream
を作成します。 Transform()
クラスを呼び出すときは、1つの関数を含むオブジェクトを渡します。 この重要な機能はtransform
機能です。
copy
関数の下に、以下のコードブロックを追加して、reverse
関数を追加します。
node-file-streams / mycliprogram
... function reverse(filePath) { const readStream = fs.createReadStream(filePath); const reversedDataFilePath = filePath.split('.')[0] + '-reversed.'+ filePath.split('.')[1]; const writeStream = fs.createWriteStream(reversedDataFilePath); const reverseStream = new Transform({ transform (data, encoding, callback) { const reversedData = data.toString().split("").reverse().join(""); this.push(reversedData); callback(); } }); readStream.pipe(reverseStream).pipe(writeStream).on('finish', () => { console.log(`Finished reversing the contents of ${filePath} and saving the output to ${reversedDataFilePath}.`); }); }
transform
関数は、data
、encoding
タイプ、およびcallback
関数の3つのパラメーターを受け取ります。 この関数内で、データを文字列に変換し、文字列を分割し、結果の配列の内容を逆にして、それらを結合し直しました。 このプロセスは、データを順方向ではなく逆方向に書き換えます。
次に、2つのpipe()
関数を使用して、readStream
をreverseStream
に接続し、最後にwriteStream
に接続しました。 最後に、finish
イベントをリッスンして、ファイルの内容が完全に逆になったことをユーザーに警告しました。
上記のコードは、finish
イベントをリッスンするために別の構文を使用していることに気付くでしょう。 新しい行でwriteStream
のfinish
イベントをリッスンする代わりに、on
関数を2番目のpipe
関数にチェーンしました。 一部のイベントリスナーをストリームにチェーンできます。 この場合、これを行うと、writeStream
でon('finish')
関数を呼び出すのと同じ効果があります。
まとめると、switch
ステートメントのcase 3
ブロックのconsole.log
ステートメントをreverse()
に置き換えます。
node-file-streams / mycliprogram
... switch (command){ ... case 3: reverse(args[3]); break; ... }
この機能をテストするには、国の名前をアルファベット順に含む別のファイル( countrys.csv )を使用します。 以下のコマンドを実行して、作業ディレクトリにダウンロードできます。
wget https://raw.githubusercontent.com/do-community/node-file-streams/999e66a11cd04bc59843a9c129da759c1c515faf/countries.csv
その後、mycliprogram
を実行できます。
./mycliprogram reverse countries.csv
出力は次のようになります。
OutputFinished reversing the contents of countries.csv and saving the output to countries-reversed.csv.
countries-reversed.csv
とcountries.csv
の内容を比較して、変換を確認します。 それぞれの名前が逆に書かれ、名前の順序も逆になっています(「アフガニスタン」は「natsinahgfA」と書かれて最後に表示され、「ジンバブエ」は「ewbabmiZ」と書かれて最初に表示されます)。
これで、カスタム変換ストリームが正常に作成されました。 また、ファイル処理にストリームを使用する関数を使用してコマンドラインプログラムを作成しました。
結論
ストリームは、ネイティブNode.jsモジュール、およびデータを効率的に処理する方法を提供するため、入出力操作を実行するさまざまなyarn
およびnpm
パッケージで使用されます。 この記事では、さまざまなストリームベースの関数を使用してNode.jsのファイルを操作しました。 read
、write
、copy
、およびreverse
コマンドを使用してコマンドラインプログラムを作成しました。 次に、これらの各コマンドを、それに応じて名前が付けられた関数に実装しました。 関数を実装するには、fs
モジュールのcreateReadStream
、createWriteStream
、pipe
、createInterface
関数などの関数を使用しました。 X145X] モジュール、そして最後に抽象Transform()
クラス。 最後に、これらの関数を小さなコマンドラインプログラムにまとめました。
次のステップとして、作成したコマンドラインプログラムを拡張して、ローカルで使用する可能性のある他のファイルシステム機能を含めることができます。 良い例は、データを.tsv
ストリームソースから.csv
に変換するパーソナルツールを作成することや、この記事で使用したwget
コマンドを複製してGitHubからファイルをダウンロードすることです。 。
作成したコマンドラインプログラムは、コマンドライン引数自体を処理し、単純なプロンプトを使用してユーザー入力を取得します。 Node.jsスクリプトでコマンドライン引数を処理する方法およびInquirer.jsを使用してインタラクティブなコマンドラインプロンプトを作成する方法を参照すると、より堅牢で保守しやすいコマンドラインアプリケーションの構築について詳しく知ることができます。 。
さらに、Node.jsは、ユースケースに必要となる可能性のあるさまざまな Node.jsストリームモジュールクラス、メソッド、およびイベントに関する広範なドキュメントを提供します。