Skip to content

Instantly share code, notes, and snippets.

@vace
Last active March 15, 2020 15:06
Show Gist options
  • Save vace/ff71809794e83693af1ea56263a677f9 to your computer and use it in GitHub Desktop.
Save vace/ff71809794e83693af1ea56263a677f9 to your computer and use it in GitHub Desktop.
csv text parse object
/**
* [csvTextParser 简单的csv文本解析器]
* @param {string} text [csv文本]
* @param {boolean} columns [读取第一行做为表头]
* @param {array} columns [列配置]
* @param {string} columns[i].field 字段名
* @param {int} columns[i].index? 对应列索引,默认为i
* @param {int} columns[i].trim? 是否首尾去空
* @param {string|Function} columns[i].type 字段类型(int,float,string,boolean,function)
* @param {object} options [解析配置]
* @param {string} options.charRow [行分隔符]
* @param {string} options.charColon [字段包裹符号]
* @param {string} options.charDivide [列分隔符]
* @return {[type]} [description]
*/
function csvTextParser (text, columns, options = {}) {
// 配置
const {charRow = "\n", charColon = '"', charDivide = ',', trim} = options
const textCount = text.length
let isInColon = false
let tempChars = ''
let array = []
let row
const pushColumn = (column) => {
row.push(tempChars)
tempChars = ''
}
const pushRow = () => {
row = []
array.push(row)
}
for (var i = 0; i <= textCount; i++) {
let char = text[i]
if (isInColon) { // 进入包裹区域
if (char === charColon) {
i += 1
let nextChar = text[i]
if (nextChar === charDivide || nextChar == null) {
isInColon = false
pushColumn()
continue
} else if (nextChar === charRow) {
isInColon = false
pushColumn()
pushRow()
continue
}
}
} else {
if (char === charDivide) { // 分列
pushColumn()
continue
} else if (char === charRow) {
pushColumn()
pushRow()
continue
} else if (char === charColon) {
let prevChar = text[i - 1]
if (prevChar === charDivide || prevChar === charRow || prevChar == null) {
isInColon = true
continue
}
}
}
if (i === 0) {
pushRow()
} else if (i === textCount) {
pushColumn()
}
tempChars += char
}
let renderType = (value, row, type, col) => {
if (!type) return value
if (typeof type === 'function') {
return type(value, row, col)
}
switch (type) {
case 'int': return parseInt(value, 10)
case 'float': return parseFloat(value)
case 'string': return String(value)
case 'boolean': return !!value
case 'json': return JSON.parse(value)
default:
console.warn('unknow type:', type)
return value
}
}
if (!columns) {
return array
}
if (columns === true) {
columns = array.shift()
}
return array.map(cols => {
var object = {}
columns.forEach((col, _index) => {
if (typeof col === 'string') {
object[col] = cols[_index]
} else {
const { field = _index, index = _index, type, trim } = col
const value = renderType(cols[index], cols, type, col)
object[field] = trim && typeof value === 'string' ? value.trim() : value
}
})
return object
})
}
// parse array test
console.log(csvTextParser(`1,小明,早上好
2,小张,下午好
3,未知,"很多冒号""道,”””ss’’’""服务中心"
4,"换行
测试","冒号测试""呵
呵🙄"`))
// parse simple object
console.log(csvTextParser(`1,小明,98.6
2,小张,99.99999`, ['id', 'name', 'score']))
// parse object
console.log(csvTextParser(`1,小明,98.6
2,小张,99.99999`, [
{field: 'id', type: 'int'},
{field: 'name', type: 'string'},
{field: 'score', type: 'float'}
]))
// parse mixed object
console.log(csvTextParser(`1,小明,80.5,B
2,小张,90.5,A`, [
{field: 'id', type: 'int'},
'name',
{field: 'score', type: 'float'},
{field: 'surplus', index: 2, type: value => {
return 100 - value
}},
{field: 'level', index: 3}
]))
// parse header
console.log(csvTextParser(`ID,name,score,level
1,小明,80.5,B
2,小张,90.5,A`, true))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment