序章
CSVは、表形式のデータを保存するためのプレーンテキストファイル形式です。 CSVファイルは、コンマ区切り文字を使用してテーブルセルの値を区切り、新しい行で行の開始位置と終了位置を示します。 ほとんどのスプレッドシートプログラムとデータベースは、CSVファイルをエクスポートおよびインポートできます。 CSVはプレーンテキストファイルであるため、どのプログラミング言語でもCSVファイルを解析して書き込むことができます。 Node.js には、 node-csv 、 fast-csv 、 papaparse など、CSVファイルを処理できる多くのモジュールがあります。
このチュートリアルでは、 node-csv Node.jsストリームを使用してCSVファイルを読み取るモジュール。これにより、大量のメモリを消費することなく大きなデータセットを読み取ることができます。 プログラムを変更して、解析されたデータをCSVファイルからSQLiteデータベースに移動します。 また、データベースからデータを取得し、それを解析します node-csv、Node.jsストリームを使用して、CSVファイルにチャンクで書き込みます。
前提条件
このチュートリアルに従うには、次のものが必要です。
-
ローカル環境またはサーバー環境にインストールされたNode.js。 Node.jsのインストール方法とローカル開発環境の作成に従って、Node.jsをインストールします。
-
ローカル環境またはサーバー環境にインストールされたSQLite。Ubuntu20.04にSQLiteをインストールして使用する方法の手順1に従ってインストールできます。 SQLiteの使用方法に関する知識は役に立ち、インストールガイドのステップ2〜7で学ぶことができます。
-
Node.jsプログラムの作成に精通していること。 Node.jsで最初のプログラムを作成して実行する方法を参照してください。
-
Node.jsストリームに精通していること。 Node.jsでストリームを使用してファイルを操作する方法を参照してください。
ステップ1—プロジェクトディレクトリの設定
このセクションでは、プロジェクトディレクトリを作成し、アプリケーションのパッケージをダウンロードします。 また、 Stats NZ からCSVデータセットをダウンロードします。これには、ニュージーランドの国際的な移行データが含まれています。
開始するには、というディレクトリを作成します csv_demo ディレクトリに移動します。
- mkdir csv_demo
- cd csv_demo
次に、ディレクトリをnpmプロジェクトとして初期化します。 npm init 指図:
- npm init -y
The -y オプションは通知します npm init すべてのプロンプトに「はい」と言います。 このコマンドは、 package.json いつでも変更できるデフォルト値を使用します。
ディレクトリがnpmプロジェクトとして初期化されたので、必要な依存関係をインストールできます。 node-csv と node-sqlite3.
次のコマンドを入力してインストールします node-csv:
- npm install csv
The node-csv moduleは、データを解析してCSVファイルに書き込むことができるモジュールのコレクションです。 このコマンドは、の一部である4つのモジュールすべてをインストールします。 node-csv パッケージ: csv-generate, csv-parse, csv-stringify、 と stream-transform. を使用します csv-parse CSVファイルを解析するモジュールと csv-stringify CSVファイルにデータを書き込むモジュール。
次に、 node-sqlite3 モジュール:
- npm install sqlite3
The node-sqlite3 モジュールを使用すると、アプリがSQLiteデータベースと対話できるようになります。
プロジェクトにパッケージをインストールした後、ニュージーランド移行CSVファイルをダウンロードします。 wget 指図:
- wget https://www.stats.govt.nz/assets/Uploads/International-migration/International-migration-September-2021-Infoshare-tables/Download-data/international-migration-September-2021-estimated-migration-by-age-and-sex-csv.csv
ダウンロードしたCSVファイルには長い名前が付いています。 作業を簡単にするために、ファイル名の名前を、 mv 指図:
- mv international-migration-September-2021-estimated-migration-by-age-and-sex-csv.csv migration_data.csv
新しいCSVファイル名 migration_data.csv、より短く、操作が簡単です。
使用する nano、またはお気に入りのテキストエディタで、ファイルを開きます。
- nano migration_data.csv
開くと、次のようなコンテンツが表示されます。
year_month,month_of_release,passenger_type,direction,sex,age,estimate,standard_error,status
2001-01,2020-09,Long-term migrant,Arrivals,Female,0-4 years,344,0,Final
2001-01,2020-09,Long-term migrant,Arrivals,Male,0-4 years,341,0,Final
...
最初の行には列名が含まれ、後続のすべての行には各列に対応するデータが含まれています。 各データはコンマで区切ります。 この文字は、フィールドを区切るため、区切り文字と呼ばれます。 カンマの使用に限定されません。 他の人気のある区切り文字にはコロンが含まれます(:)、セミコロン(;)、およびtabs(\td). ほとんどのモジュールではファイルの解析に区切り文字が必要なため、ファイルで使用されている区切り文字を知る必要があります。
ファイルを確認して区切り文字を特定したら、 migration_data.csv を使用してファイル CTRL+X.
これで、プロジェクトに必要な依存関係がインストールされました。 次のセクションでは、CSVファイルを読みます。
ステップ2—CSVファイルの読み取り
このセクションでは、 node-csv CSVファイルを読み取り、その内容をコンソールに記録します。 を使用します fs モジュールの createReadStream() CSVファイルからデータを読み取り、読み取り可能なストリームを作成する方法。 次に、ストリームを、で初期化された別のストリームにパイプします。 csv-parse データのチャンクを解析するモジュール。 データのチャンクが解析されたら、コンソールに記録できます。
を作成して開きます readCSV.js お好みのエディタでファイル:
- nano readCSV.js
あなたの中で readCSV.js ファイル、インポート fs と csv-parse 次の行を追加してモジュールを作成します。
const fs = require("fs");
const { parse } = require("csv-parse");
最初の行で、 fs 変数を割り当て、 fs Node.jsが require() モジュールをインポートすると、メソッドが戻ります。
2行目では、 parse によって返されたオブジェクトからのメソッド require() メソッドに parse 破壊構文を使用する変数。
次の行を追加して、CSVファイルを読み取ります。
...
fs.createReadStream("./migration_data.csv")
.pipe(parse({ delimiter: ",", from_line: 2 }))
.on("data", function (row) {
console.log(row);
})
The createReadStream() からの方法 fs モジュールは、読み取りたいファイル名の引数を受け入れます。 migration_data.csv ここ。 次に、読み取り可能な stream を作成します。これにより、大きなファイルが取得され、小さなチャンクに分割されます。 読み取り可能なストリームを使用すると、ストリームからのデータの読み取りのみが可能になり、書き込みはできなくなります。
読み取り可能なストリームを作成した後、ノードの pipe() メソッドは、データのチャンクを読み取り可能なストリームから別のストリームに転送します。 2番目のストリームは、 csv-parse モジュールの parse() メソッドは内部で呼び出されます pipe() 方法。 The csv-parse モジュールは、変換ストリーム(読み取りおよび書き込み可能なストリーム)を実装し、データチャンクを取得して別の形式に変換します。 たとえば、次のようなチャンクを受け取った場合 2001-01,2020-09,Long-term migrant,Arrivals,Female,0-4 years,344、 parse() メソッドはそれを配列に変換します。
The parse() メソッドは、プロパティを受け入れるオブジェクトを受け取ります。 次に、オブジェクトは、メソッドが解析するデータに関する詳細情報を構成および提供します。 オブジェクトは次のプロパティを取ります。
-
delimiter行の各フィールドを区切る文字を定義します。 値,パーサーに、コンマでフィールドを区切るように指示します。 -
from_lineパーサーが行の解析を開始する行を定義します。 値で2、パーサーは1行目をスキップし、2行目から開始します。 後でデータベースにデータを挿入するため、このプロパティは、データベースの最初の行に列名を挿入しないようにするのに役立ちます。
次に、Node.jsを使用してストリーミングイベントを添付します on() 方法。 ストリーミングイベントを使用すると、特定のイベントが発生した場合にメソッドがデータのチャンクを消費できます。 The data データがから変換されたときにイベントがトリガーされます parse() メソッドを使用する準備ができました。 データにアクセスするには、コールバックを on() メソッド、という名前のパラメータを取ります row. The row パラメータは、配列に変換されたデータチャンクです。 コールバック内で、を使用してコンソールにデータを記録します console.log() 方法。
ファイルを実行する前に、ストリームイベントをさらに追加します。 これらのストリームイベントはエラーを処理し、CSVファイル内のすべてのデータが消費されたときにコンソールに成功メッセージを書き込みます。
まだあなたの中に readCSV.js ファイル、強調表示されたコードを追加します。
...
fs.createReadStream("./migration_data.csv")
.pipe(parse({ delimiter: ",", from_line: 2 }))
.on("data", function (row) {
console.log(row);
})
.on("end", function () {
console.log("finished");
})
.on("error", function (error) {
console.log(error.message);
});
The end イベントは、CSVファイル内のすべてのデータが読み取られたときに発行されます。 これが発生すると、コールバックが呼び出され、終了したことを示すメッセージがログに記録されます。
CSVデータの読み取りと解析中にエラーが発生した場合、 error イベントが発行され、コールバックが呼び出され、コンソールにエラーメッセージが記録されます。
これで、完全なファイルは次のようになります。
const fs = require("fs");
const { parse } = require("csv-parse");
fs.createReadStream("./migration_data.csv")
.pipe(parse({ delimiter: ",", from_line: 2 }))
.on("data", function (row) {
console.log(row);
})
.on("end", function () {
console.log("finished");
})
.on("error", function (error) {
console.log(error.message);
});
保存して終了します readCSV.js を使用してファイル CTRL+X.
次に、を使用してファイルを実行します node 指図:
- node readCSV.js
出力は次のようになります(簡潔にするために編集)。
Output[
'2001-01',
'2020-09',
'Long-term migrant',
'Arrivals',
'Female',
'0-4 years',
'344',
'0',
'Final'
]
...
[
'2021-09',
...
'70',
'Provisional'
]
finished
CSVファイルのすべての行は、 csv-parse ストリームを変換します。 ストリームからチャンクを受信するたびにロギングが行われるため、データは一度に表示されるのではなく、ダウンロードされているように見えます。
このステップでは、CSVファイルのデータを読み取り、それを配列に変換します。 次に、CSVファイルからデータベースにデータを挿入します。
ステップ3—データベースへのデータの挿入
Node.jsを使用してCSVファイルからデータベースにデータを挿入すると、データベースに挿入する前にデータを処理、クリーンアップ、または拡張するために使用できるモジュールの膨大なライブラリにアクセスできます。
このセクションでは、を使用してSQLiteデータベースとの接続を確立します node-sqlite3 モジュール。 次に、データベースにテーブルを作成し、 readCSV.js ファイルを変更し、CSVファイルから読み取ったすべてのデータをデータベースに挿入するように変更します。
を作成して開きます db.js エディター内のファイル:
- nano db.js
あなたの中で db.js ファイルに次の行を追加して、 fs と node-sqlite3 モジュール:
const fs = require("fs");
const sqlite3 = require("sqlite3").verbose();
const filepath = "./population.db";
...
3行目では、SQLiteデータベースのパスを定義し、それを変数に格納します filepath. データベースファイルはまだ存在していませんが、 node-sqlite3 データベースとの接続を確立します。
同じファイルに、次の行を追加してNode.jsをSQLiteデータベースに接続します。
...
function connectToDatabase() {
if (fs.existsSync(filepath)) {
return new sqlite3.Database(filepath);
} else {
const db = new sqlite3.Database(filepath, (error) => {
if (error) {
return console.error(error.message);
}
console.log("Connected to the database successfully");
});
return db;
}
}
ここでは、という名前の関数を定義します connectToDatabase() データベースへの接続を確立します。 関数内で、 fs モジュールの existsSync() のメソッド if ステートメント。データベースファイルがプロジェクトディレクトリに存在するかどうかを確認します。 の場合 if 状態は次のように評価されます true、SQLiteをインスタンス化します Database() のクラス node-sqlite3 データベースファイルパスを持つモジュール。 接続が確立されると、関数は接続オブジェクトを返し、終了します。
ただし、 if ステートメントは次のように評価されます false (データベースファイルが存在しない場合)、実行はスキップされます else ブロック。 の中に else ブロック、インスタンス化 Database() データベースファイルパスとコールバックの2つの引数を持つクラス。
最初の引数はSQLiteデータベースファイルのパスです。 ./population.db. 2番目の引数は、データベースとの接続が正常に確立されたとき、またはエラーが発生したときに自動的に呼び出されるコールバックです。 コールバックは error パラメータとしてのオブジェクト。 null 接続が成功した場合。 コールバック内で、 if ステートメントは、 error オブジェクトが設定されます。 それが評価する場合 true、コールバックはエラーメッセージをログに記録して戻ります。 それが評価する場合 false、接続が確立されたことを確認する成功メッセージをログに記録します。
現在、 if と else ブロックは接続オブジェクトを確立します。 を呼び出すときにコールバックを渡します Database のクラス else ブロックしてデータベースにテーブルを作成しますが、データベースファイルが存在しない場合に限ります。 データベースファイルがすでに存在する場合、関数は if ブロックし、データベースに接続して、接続オブジェクトを返します。
データベースファイルが存在しない場合にテーブルを作成するには、強調表示されたコードを追加します。
const fs = require("fs");
const sqlite3 = require("sqlite3").verbose();
const filepath = "./population.db";
function connectToDatabase() {
if (fs.existsSync(filepath)) {
return new sqlite3.Database(filepath);
} else {
const db = new sqlite3.Database(filepath, (error) => {
if (error) {
return console.error(error.message);
}
createTable(db);
console.log("Connected to the database successfully");
});
return db;
}
}
function createTable(db) {
db.exec(`
CREATE TABLE migration
(
year_month VARCHAR(10),
month_of_release VARCHAR(10),
passenger_type VARCHAR(50),
direction VARCHAR(20),
sex VARCHAR(10),
age VARCHAR(50),
estimate INT
)
`);
}
module.exports = connectToDatabase();
今、 connectToDatabase() を呼び出す createTable() 関数は、に格納されている接続オブジェクトを受け入れます db 引数としての変数。
外 connectToDatabase() 関数、あなたは定義します createTable() 接続オブジェクトを受け入れる関数 db パラメータとして。 あなたは exec() 上のメソッド db SQLステートメントを引数として取る接続オブジェクト。 SQLステートメントは、という名前のテーブルを作成します migration 7列あり。 列名は、の見出しと一致します migration_data.csv ファイル。
最後に、 connectToDatabase() 関数を作成し、関数によって返された接続オブジェクトをエクスポートして、他のファイルで再利用できるようにします。
保存して終了します db.js ファイル。
データベース接続が確立されたら、次にコピーして変更します。 readCSV.js その行を挿入するファイル csv-parse データベースに解析されたモジュール。
ファイルをコピーして名前を変更します insertData.js 次のコマンドを使用します。
- cp readCSV.js insertData.js
を開きます insertData.js エディター内のファイル:
- nano insertData.js
強調表示されたコードを追加します。
const fs = require("fs");
const { parse } = require("csv-parse");
const db = require("./db");
fs.createReadStream("./migration_data.csv")
.pipe(parse({ delimiter: ",", from_line: 2 }))
.on("data", function (row) {
db.serialize(function () {
db.run(
`INSERT INTO migration VALUES (?, ?, ? , ?, ?, ?, ?)`,
[row[0], row[1], row[2], row[3], row[4], row[5], row[6]],
function (error) {
if (error) {
return console.log(error.message);
}
console.log(`Inserted a row with the id: ${this.lastID}`);
}
);
});
});
3行目では、接続オブジェクトをからインポートします。 db.js ファイルを作成して変数に保存します db.
内部 data に添付されたイベントコールバック fs モジュールストリーム、あなたは serialize() 接続オブジェクトのメソッド。 このメソッドは、SQLステートメントが実行を終了してから別のステートメントが実行を開始することを保証します。これにより、システムが競合する操作を同時に実行するデータベースの競合状態を防ぐことができます。
The serialize() メソッドはコールバックを取ります。 コールバック内で、 run 上のメソッド db 接続オブジェクト。 このメソッドは、次の3つの引数を受け入れます。
-
最初の引数は、SQLiteデータベースで渡されて実行されるSQLステートメントです。 The
run()メソッドは、結果を返さないSQLステートメントのみを受け入れます。 TheINSERT INTO migration VALUES (?, ..., ?ステートメントはテーブルに行を挿入しますmigration、 そしてその?後で値に置き換えられるプレースホルダーですrun()メソッドの2番目の引数。 -
2番目の引数は配列です
[row[0], ... row[5], row[6]]. 前のセクションでは、parse()メソッドは、読み取り可能なストリームからデータのチャンクを受け取り、それを配列に変換します。 データは配列として受信されるため、各フィールド値を取得するには、配列インデックスを使用して次のようにアクセスする必要があります。[row[1], ..., row[6]]、など。 -
3番目の引数は、データが挿入されたとき、またはエラーが発生したときに実行されるコールバックです。 コールバックはエラーが発生したかどうかをチェックし、エラーメッセージをログに記録します。 エラーがない場合、関数はコンソールに成功メッセージをログに記録します。
console.log()メソッド、IDとともに行が挿入されたことを通知します。
最後に、 end と error ファイルからのイベント。 の非同期性により、 node-sqlite3 メソッド、 end と error イベントは、データがデータベースに挿入される前に実行されるため、不要になりました。
ファイルを保存して終了します。
を実行します insertData.js を使用してファイル node:
- node insertData.js
システムによっては時間がかかる場合がありますが、 node 以下の出力を返す必要があります。
OutputConnected to the database successfully
Inserted a row with the id: 1
Inserted a row with the id: 2
...
Inserted a row with the id: 44308
Inserted a row with the id: 44309
Inserted a row with the id: 44310
メッセージ、特にIDは、CSVファイルの行がデータベースに保存されたことを示します。
これで、CSVファイルを読み取り、そのコンテンツをデータベースに挿入できます。 次に、CSVファイルを作成します。
ステップ4—CSVファイルの書き込み
このセクションでは、データベースからデータを取得し、ストリームを使用してCSVファイルに書き込みます。
作成して開く writeCSV.js エディターで:
- nano writeCSV.js
あなたの中で writeCSV.js ファイルに次の行を追加して、 fs と csv-stringify モジュールとデータベース接続オブジェクト db.js:
const fs = require("fs");
const { stringify } = require("csv-stringify");
const db = require("./db");
The csv-stringify モジュールは、オブジェクトまたは配列からのデータをCSVテキスト形式に変換します。
次に、次の行を追加して、データを書き込むCSVファイルの名前とデータを書き込む書き込み可能なストリームを含む変数を定義します。
...
const filename = "saved_from_db.csv";
const writableStream = fs.createWriteStream(filename);
const columns = [
"year_month",
"month_of_release",
"passenger_type",
"direction",
"sex",
"age",
"estimate",
];
The createWriteStream メソッドは、データのストリームを書き込むファイル名の引数を取ります。これは、 saved_from_db.csv に保存されているファイル名 filename 変数。
4行目では、 columns CSVデータのヘッダーの名前を含む配列を格納する変数。 これらのヘッダーは、ファイルへのデータの書き込みを開始すると、CSVファイルの最初の行に書き込まれます。
まだあなたの中に writeCSV.js ファイルに次の行を追加して、データベースからデータを取得し、CSVファイルに各行を書き込みます。
...
const stringifier = stringify({ header: true, columns: columns });
db.each(`select * from migration`, (error, row) => {
if (error) {
return console.log(error.message);
}
stringifier.write(row);
});
stringifier.pipe(writableStream);
console.log("Finished writing data");
まず、 stringify 引数としてオブジェクトを使用するメソッド。これにより、変換ストリームが作成されます。 変換ストリームは、オブジェクトからのデータをCSVテキストに変換します。 に渡されたオブジェクト stringify() メソッドには2つのプロパティがあります。
headerブール値を受け入れ、ブール値がに設定されている場合はヘッダーを生成しますtrue.columnsCSVファイルの最初の行に書き込まれる列の名前を含む配列を取ります。headerオプションがに設定されているtrue.
次に、 each() からの方法 db 2つの引数を持つ接続オブジェクト。 最初の引数はSQLステートメントです select * from migration データベース内の行を1つずつ取得します。 2番目の引数は、データベースから行が取得されるたびに呼び出されるコールバックです。 コールバックは2つのパラメータを取ります: error オブジェクトと row データベース内の単一の行から取得されたデータを含むオブジェクト。 コールバック内で、 error オブジェクトはに設定されています if 声明。 条件が次のように評価された場合 true、エラーメッセージは、を使用してコンソールに記録されます console.log() 方法。 エラーがない場合は、 write() 上の方法 stringifier、データをに書き込みます stringifier ストリームを変換します。
いつ each() メソッドは反復を終了し、 pipe() 上のメソッド stringifier ストリームはデータをチャンクで送信し、書き込みを開始します writableStream. 書き込み可能なストリームは、データの各チャンクを saved_from_db.csv ファイル。 すべてのデータがファイルに書き込まれると、 console.log() 成功メッセージをログに記録します。
完全なファイルは次のようになります。
const fs = require("fs");
const { stringify } = require("csv-stringify");
const db = require("./db");
const filename = "saved_from_db.csv";
const writableStream = fs.createWriteStream(filename);
const columns = [
"year_month",
"month_of_release",
"passenger_type",
"direction",
"sex",
"age",
"estimate",
];
const stringifier = stringify({ header: true, columns: columns });
db.each(`select * from migration`, (error, row) => {
if (error) {
return console.log(error.message);
}
stringifier.write(row);
});
stringifier.pipe(writableStream);
console.log("Finished writing data");
ファイルを保存して閉じてから、 writeCSV.js ターミナルのファイル:
- node writeCSV.js
次の出力が表示されます。
OutputFinished writing data
データが書き込まれたことを確認するには、を使用してファイルの内容を調べます。 cat 指図:
- cat saved_from_db.csv
cat ファイルに書き込まれたすべての行を返します(簡潔にするために編集):
Outputyear_month,month_of_release,passenger_type,direction,sex,age,estimate
2001-01,2020-09,Long-term migrant,Arrivals,Female,0-4 years,344
2001-01,2020-09,Long-term migrant,Arrivals,Male,0-4 years,341
2001-01,2020-09,Long-term migrant,Arrivals,Female,10-14 years,
...
これで、データベースからデータを取得し、ストリームを使用して各行をCSVファイルに書き込むことができます。
結論
この記事では、CSVファイルを読み取り、そのデータをデータベースに挿入します。 node-csv と node-sqlite3 モジュール。 次に、データベースからデータを取得し、それを別のCSVファイルに書き込みました。
これで、CSVファイルの読み取りと書き込みができます。 次のステップとして、メモリ効率の高いストリームで同じ実装を使用して大規模なCSVデータセットを操作できるようになりました。または、ストリームの操作をはるかに簡単にするevent-streamのようなパッケージを調べることもできます。
詳細については node-csv、ドキュメント CSVProject-Node.jsCSVパッケージにアクセスしてください。 詳細については node-sqlite3、Githubドキュメントにアクセスしてください。 Node.jsスキルを継続的に向上させるには、Node.jsシリーズのコーディング方法を参照してください。