跳至主要内容

[note] node-csv 筆記

node-csv @ github

Parse:用來解析 CSV 檔案

💡 使用 Callback 或 Sync API 的方法(而非下方的 stream)會相對簡單。

// 使用 Stream
const csv = require('csv');
const path = require('path');
const fs = require('fs');
const fsPromises = fs.promises;

readAndParseCSV();

async function readAndParseCSV() {
try {
// STEP 1: 讀取 CSV 檔
const inputFilePath = path.resolve(__dirname, './foobar.csv');
const input = await fsPromises.readFile(inputFilePath);

// STEP 2:建立讀出 CSV 用的陣列和 parser
const output = [];
const parser = csv.parse({
delimiter: ',',
});

// STEP 3-1:建立對應事件 - 讀取資料
parser.on('readable', function () {
let record;
while ((record = parser.read())) {
output.push(record);
}
});

// STEP 3-2:錯誤處理
parser.on('error', function (err) {
console.error(err.message);
});

// STEP 3-3:取得最後 output 的結果
parser.on('end', function () {
console.log('output', output);
});

// STEP 4:放入預備讀取的內容
parser.write(input);

// STEP 5:關閉 readable stream
parser.end();
} catch (error) {
console.log('error', error);
}
}

使用 callback 搭配 Promise:

// 使用 callback
const parse = require('csv-parse');
const fs = require('fs');
const fsPromises = fs.promises;
const path = require('path');

const inputFilePath = path.resolve(__dirname, './mask-data.csv');

main();

async function main() {
const inputFile = await fsPromises.readFile(inputFilePath);
const parsedResult = await parseCSV(inputFile, {
delimiter: ',',
columns: true,
});

console.log('parsedResult', parsedResult);
}

function parseCSV(input, options) {
return new Promise((resolve, reject) => {
parse(input, options, (error, output) => {
if (error) {
console.error('[ERROR] parseCSV: ', error.message);
reject('[ERROR] parseCSV: ', error.message);
}

resolve(output);
});
});
}

transform:將讀進來的檔案進行處理

const transform = require('csv');

// STEP 1:定義最終 output 儲存的陣列
const output = [];

// STEP 2:定義要如何轉換資料
const transformer = csv.transform(function (data) {
// data 會是讀到的資料
data.push(data.shift());
return data;
});

// STEP 3-1:定義對應的事件 - 讀取到資料時
transformer.on('readable', function () {
let row;
while ((row = transformer.read())) {
output.push(row);
}
});

// STEP 3-2:定義對應的事件 - 錯誤處理
transformer.on('error', function (err) {
console.error(err.message);
});

// STEP 3-3:定義對應的事件 - 讀取完資料時
transformer.on('finish', function () {
console.log('output', output);
});

// STEP 4:帶入希望進行轉換的資料(可以是透過 csv-parse 解析完到資料
transformer.write(['1', '2', '3', '4']);
transformer.write(['a', 'b', 'c', 'd']);

// STEP 5:關閉 readable stream
transformer.end();

Snippets

const csv = require('csv');
const path = require('path');
const fs = require('fs');
const fsPromises = fs.promises;

async function main({ start, end }) {
try {
const inputFilePath = path.resolve(__dirname, './../address.csv');
const inputFile = await fsPromises.readFile(inputFilePath);
const addresses = await parseCSV(inputFile);

// searchResult is in JSON
// const searchResult = await googleAddresses(addresses.slice(start, end));
if (!searchResult) return;
await saveCSV({
input: searchResult,
fileName: `result-${start}-${end}.csv`,
});
} catch (error) {
console.log('error', error);
}
}

function parseCSV(input) {
return new Promise((resolve, reject) => {
const transformer = csv.transform(function (data) {
return data.map(word2Number);
});

const output = [];
const parser = csv.parse({
delimiter: ',',
skip_empty_lines: true,
trim: true,
});

parser.on('readable', function () {
let record;
while ((record = parser.read())) {
transformer.write(record);
// output.push(record);
}
});

parser.on('error', function (err) {
console.error(err.message);
reject(err.message);
});

transformer.on('readable', function () {
let row;
while ((row = transformer.read())) {
output.push(row);
}
});

transformer.on('error', function (err) {
console.error(err.message);
reject(err.message);
});

transformer.on('finish', function () {
// console.log('output', output);
resolve(output);
});

parser.write(input);

parser.end();
transformer.end();
});
}

function saveCSV({ input, fileName }) {
return new Promise((resolve, reject) => {
csv.stringify(
input,
{
cast: {
string: number2Word,
},
columns: ['address', 'isNotFound', 'results'],
},
async function (err, data) {
if (err) {
console.error('[saveCSV]' + err);
reject(err);
}

await fsPromises.writeFile(`./data/${fileName}`, data);
resolve(data);
},
);
});
}

function word2Number(word) {
const hasToTransform = //.test(word);

if (!hasToTransform) return word;

let newValue;
newValue = word.replace(/十一樓/, '11樓');
newValue = newValue.replace(/十二樓/, '12樓');
newValue = newValue.replace(/十三樓/, '13樓');
newValue = newValue.replace(/十四樓/, '14樓');
newValue = newValue.replace(/十五樓/, '15樓');
newValue = newValue.replace(/十六樓/, '16樓');
newValue = newValue.replace(/一樓/, '1樓');
newValue = newValue.replace(/二樓/, '2樓');
newValue = newValue.replace(/三樓/, '3樓');
newValue = newValue.replace(/四樓/, '4樓');
newValue = newValue.replace(/五樓/, '5樓');
newValue = newValue.replace(/六樓/, '6樓');
newValue = newValue.replace(/七樓/, '7樓');
newValue = newValue.replace(/八樓/, '8樓');
newValue = newValue.replace(/九樓/, '9樓');
newValue = newValue.replace(/十樓/, '10樓');
return newValue;
}