[英]csv-parse error handling in pipe
作為我正在構建的應用程序的一部分,我正在使用csv-parse讀取和操作大型(大約 5.5GB,800 萬行)csv 文件。 我的過程運行得相對順利,但被困在一個項目上 - 捕獲由不一致的列數引發的錯誤。
我正在使用管道函數,因為它與應用程序的其余部分配合得很好,但我的問題是,如何將解析器拋出的錯誤重定向到日志並允許進程繼續?
我認識到我可以使用relax_column_count
選項來跳過列數不一致的記錄,而該選項幾乎就足夠了。 問題在於,出於數據質量評估的目的,我需要記錄這些記錄,以便我可以返回並查看導致列數不正確的原因(該過程是一個具有許多潛在故障點的提要)。
作為旁注,我知道解決此問題的最簡單方法是清理此過程上游的數據,但不幸的是我無法控制數據源。
例如,在示例集中,我收到以下錯誤:
事件.js:141
扔er; // 未處理的“錯誤”事件
錯誤:行(行號)上的列數與標題不匹配
示例數據(實際上不是我的數據,而是展示了同樣的問題):
year, month, value1, value2
2012, 10, A, B
2012, 11, B, C,
2012, 11, C, D,
2013, 11, D, E,
2013, 11, E, F,
2013, 11, F,
2013, 11, G, G,
2013, 1, H, H,
2013, 11, I, I,
2013, 12, J, J,
2014, 11, K, K,
2014, 4, L, L,
2014, 11, M, M,
2014, 5, N,
2014, 11, O, N,
2014, 6, P, O,
2015, 11, Q, P,
2015, 11, R, Q,
2015, 11, S, R,
2015, 11, T, S,
代碼:
const fs = require('fs');
const parse = require('csv-parse');
const stringify = require('csv-stringify');
const transform = require('stream-transform');
const paths = {
input: './sample.csv',
output: './output.csv',
error: './errors.csv',
}
var input = fs.createReadStream(paths.input);
var output = fs.createWriteStream(paths.output);
var error = fs.createWriteStream(paths.error);
var stringifier = stringify({
header: true,
quotedString: true,
});
var parser = parse({
relax: true,
delimiter: ',',
columns: true,
//relax_column_count: true,
})
var transformer = transform((record, callback) => {
callback(null, record);
}, {parallel: 10});
input.pipe(parser).pipe(transformer).pipe(stringifier).pipe(output);
想法?
我開發了一個解決這個問題的方法。 它不使用管道 API ,而是使用 CSV 包的回調 API 。 它不像我希望的那樣優雅,但它是功能性的,並且具有顯式錯誤處理的好處,這不會導致過程在不一致的列數上停止。
該過程逐行讀取文件,根據settings
對象 ( settings.mapping
) 中的預期字段列表解析該行,然后轉換、字符串化並將結果輸出行寫入新的 csv。
我將其設置為記錄由於列數與文件頭不一致以及一些額外數據(執行日期時間、行號和整行作為診斷信息的文本)而導致的錯誤。我沒有設置其他錯誤類型的日志記錄,因為它們都位於 csv 結構錯誤的下游,但您也可以修改代碼以編寫這些錯誤。(您也可以將它們寫入 JSON 或 MySQL 數據庫,但有一件事一次)。
好消息是,與直接方法相比,使用這種方法並沒有對性能造成巨大影響。 我沒有做過任何正式的性能測試,但在 60MB 的文件上,兩種方法的性能大致相同(假設文件沒有不一致的行)。 明確的下一步是研究將寫入捆綁到磁盤以減少 I/O。
我仍然很想知道是否有更好的方法來做到這一點,所以如果您有想法,請務必發布答案! 與此同時,我想我會發布這個有效的答案,以防它對其他人在使用相同類型的格式不一致的源時苦苦掙扎時有用。
信用到期的信用,特別是兩個問題/答案:
示例代碼:
'use strict'
// Dependencies
const es = require('event-stream');
const fs = require('fs');
const parse = require('csv-parse');
const stringify = require('csv-stringify');
const transform = require('stream-transform');
// Reference objects
const paths = {
input: 'path to input.csv',
output: 'path to output.csv',
error: 'path to error output.csv',
}
const settings = {
mapping: {
// Each field is an object with the field name as the key
// and can have additional properties for use in the transform
// component of this process
// Example
'year' : {
import: true,
}
}
}
const metadata = {
records: 0,
error: 0
}
// Set up streams
var input = fs.createReadStream(paths.input);
var errors = fs.createWriteStream(paths.error, {flags: 'ax'});
var output = fs.createWriteStream(paths.output, {flags: 'ax'});
// Begin process (can be refactored into function, but simplified here)
input
.pipe(es.split()) // split based on row, assumes \n row endings
.pipe(es.mapSync(line => { // synchronously process each line
// Remove headers, specified through settings
if (metadata.records === 0) return metadata.records++;
var id = metadata.records;
// Parse csv by row
parse(line, {
relax: true,
delimiter: ',',
columns: Object.keys(settings.mapping),
}, (error, record) => {
// Write inconsistent column error
if (error) {
metadata.error++;
errors.write(
new Date() + ', Inconsistent Columns, ' +
id + ', `' +
line + '`\n'
);
}
// Apply transform / reduce
transform(record, (record) => {
// Do stuff to record
return record;
}, (error, record) => {
// Throw tranform errors
if (error) {
throw error;
}
// Stringify results and write to new csv
stringify(record, {
header: false,
quotedString: true,
}, (error, record) => {
// Throw stringify errors
if (error) {
console.log(error);
}
// Write record to new csv file
output.write(record);
});
});
})
// Increment record count
metadata.records++;
}))
.on('end', () => {
metadata.records--;
console.log(metadata)
})
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.