ファイルのコピー
NodeJS では基本的なファイル操作 API は提供されていますが、ファイルコピーなどの高度な機能は提供されていないため、まずはファイルコピープログラムで練習してみましょう。 copy コマンドと同様に、プログラムはソース ファイル パスとターゲット ファイル パスという 2 つのパラメータを受け入れることができる必要があります。
小さいファイルのコピー
NodeJS の組み込み fs モジュールを使用して、次のようにこのプログラムを簡単に実装します。
var fs = require('fs'); function copy(src, dst) { fs.writeFileSync(dst, fs.readFileSync(src)); } function main(argv) { copy(argv[0], argv[1]); } main(process.argv.slice(2));
上記のプログラムは、fs.readFileSync を使用してソース パスからファイルの内容を読み取り、fs.writeFileSync を使用してファイルの内容をターゲット パスに書き込みます。
Bean の知識: process はグローバル変数であり、コマンド ライン パラメーターは process.argv を通じて取得できます。 argv[0] は NodeJS 実行可能プログラムの絶対パスに固定的に等しく、argv[1] はメイン モジュールの絶対パスに固定的に等しいため、最初のコマンド ライン パラメーターは argv[2] の位置から始まります。
大きなファイルのコピー
上記のプログラムは、いくつかの小さなファイルを問題なくコピーできますが、すべてのファイルの内容を一度にメモリに読み込んでから一度にディスクに書き込むこの方法は、大きなファイルのコピーには適しておらず、メモリが疲れ果てる。大きなファイルの場合、コピーが完了するまでは少しの読み取りと少しの書き込みしかできません。したがって、上記のプログラムを次のように修正する必要があります。
var fs = require('fs'); function copy(src, dst) { fs.createReadStream(src).pipe(fs.createWriteStream(dst)); } function main(argv) { copy(argv[0], argv[1]); } main(process.argv.slice(2));
上記のプログラムは、fs.createReadStream を使用してソース ファイルの読み取り専用データ ストリームを作成し、fs.createWriteStream を使用してターゲット ファイルの書き込み専用データ ストリームを作成し、パイプ メソッドを使用して 2 つを接続します。データストリーム。接続が行われた後に何が起こるかというと、より抽象的に言えば、水がパイプに沿ってあるバケツから別のバケツに流れるということです。
ディレクトリを横断する
ファイルを操作する場合、ディレクトリの移動は一般的な要件です。たとえば、指定されたディレクトリ内のすべての JS ファイルを検索して処理する必要があるプログラムを作成する場合、ディレクトリ全体を走査する必要があります。
再帰的アルゴリズム
再帰アルゴリズムは通常、ディレクトリを走査するときに使用されます。そうでない場合は、簡潔なコードを書くのが困難になります。再帰的アルゴリズムは、サイズを継続的に縮小することで問題を解決するという点で数学的帰納法に似ています。次の例は、このアプローチを示しています。
function factorial(n) { if (n === 1) { return 1; } else { return n * factorial(n - 1); } }
上記の関数は、N (N!) の階乗を計算するために使用されます。ご覧のとおり、N が 1 より大きい場合、問題は N と N-1 の階乗の計算に帰着します。 N が 1 に等しい場合、問題は最小サイズに達し、それ以上の単純化は必要ないため、1 が直接返されます。
トラップ: 再帰アルゴリズムを使用して記述されたコードは単純ですが、再帰ごとに関数呼び出しが生成されるため、パフォーマンスを優先する必要がある場合は、再帰アルゴリズムをループ アルゴリズムに変換して関数呼び出しの数を減らす必要があります。
トラバーサルアルゴリズム
ディレクトリはツリー構造であり、走査時には通常、深さ優先 + 事前順序走査アルゴリズムが使用されます。深さ優先とは、ノードに到達した後、隣接するノードではなく、最初に子ノードをたどることを意味します。事前順序トラバーサルとは、最後にノードに戻ったときではなく、初めてノードに到達したときにトラバーサルが完了することを意味します。したがって、この走査方法を使用する場合、以下のツリーの走査順序は A > B > D > C になります。
A / \ B C / \ \ D E F
同期トラバーサル
必要なアルゴリズムを理解したら、次のディレクトリ トラバーサル関数を簡単に実装できます。
function travel(dir, callback) { fs.readdirSync(dir).forEach(function (file) { var pathname = path.join(dir, file); if (fs.statSync(pathname).isDirectory()) { travel(pathname, callback); } else { callback(pathname); } }); }
ご覧のとおり、この関数はトラバーサルの開始点としてディレクトリを使用します。サブディレクトリが見つかると、最初にそのサブディレクトリが走査されます。ファイルが見つかると、そのファイルへの絶対パスがコールバック関数に渡されます。コールバック関数はファイルパスを取得した後、さまざまな判断や処理を行うことができます。したがって、次のディレクトリがあるとします:
- /home/user/ - foo/ x.js - bar/ y.js z.css
次のコードを使用してディレクトリをトラバースすると、取得される入力は次のようになります。
travel('/home/user', function (pathname) { console.log(pathname); });
/home/user/foo/x.js /home/user/bar/y.js /home/user/z.css
非同期トラバーサル
ディレクトリの読み取りやファイルのステータスの読み取りに非同期 API を使用する場合、ディレクトリ トラバーサル機能の実装は少し複雑になりますが、原理はまったく同じです。トラベル関数の非同期バージョンは次のとおりです。
function travel(dir, callback, finish) { fs.readdir(dir, function (err, files) { (function next(i) { if (i < files.length) { var pathname = path.join(dir, files[i]); fs.stat(pathname, function (err, stats) { if (stats.isDirectory()) { travel(pathname, callback, function () { next(i + 1); }); } else { callback(pathname, function () { next(i + 1); }); } }); } else { finish && finish(); } }(0)); }); }
非同期トラバーサル関数の作成スキルについては、ここでは詳しく説明しません。これについては、後続の章で詳しく説明します。つまり、非同期プログラミングは非常に複雑であることがわかります。