d3-dsv

这个模块提供了一个针对分隔符文件/字符串的解析和格式化工具,大多数情况下是comma- (CSV) 或 tab - 分割(TSV)。这种扁平的格式在表格类数据中很流行如 Excel, 并且比 JSON 更节省空间。这个模块的实现基于 RFC 4180

CSVTSV 的转换以及格式化是内置的,比如解析:

d3.csvParse("foo,bar\n1,2"); // [{foo: "1", bar: "2"}, columns: ["foo", "bar"]]
d3.tsvParse("foo\tbar\n1\t2"); // [{foo: "1", bar: "2"}, columns: ["foo", "bar"]]

或者格式化:

d3.csvFormat([{foo: "1", bar: "2"}]); // "foo,bar\n1,2"
d3.tsvFormat([{foo: "1", bar: "2"}]); // "foo\tbar\n1\t2"

使用不同的分隔符,比如 "|" 来作为分割字符,则使用 d3.dsvFormat:

var psv = d3.dsvFormat("|");

console.log(psv.parse("foo|bar\n1|2")); // [{foo: "1", bar: "2"}, columns: ["foo", "bar"]]

为了在浏览器中方便的加载 CSV 文件,参考 d3-requestd3.csvd3.tsv 方法。

Installing

NPM 安装: npm install d3-dsv。此外还可以下载 latest release。可以直接从 d3js.orgstandalone library 单独的标准库或者作为 D3 4.0 的一部分引入。支持 AMD, CommonJS 以及基础的标签引入形式,如果使用标签引入则会暴露 d3 全局变量:

<script src="https://d3js.org/d3-dsv.v1.min.js"></script>
<script>

var data = d3.csvParse(string);

</script>

在浏览器中测试 d3-dsv.

API Reference

# d3.csvParse(string[, row]) <>

等价于 dsvFormat(",").parse.

# d3.csvParseRows(string[, row]) <>

等价于 dsvFormat(",").parseRows.

# d3.csvFormat(rows[, columns]) <>

等价于 dsvFormat(",").format.

# d3.csvFormatRows(rows) <>

等价于 dsvFormat(",").formatRows.

# d3.tsvParse(string[, row]) <>

等价于 dsvFormat("\t").parse.

# d3.tsvParseRows(string[, row]) <>

等价于 dsvFormat("\t").parseRows.

# d3.tsvFormat(rows[, columns]) <>

等价于 dsvFormat("\t").format.

# d3.tsvFormatRows(rows) <>

等价于 dsvFormat("\t").formatRows.

# d3.dsvFormat(delimiter) <>

根据指定的 delimiter 构造一个新的 DSV 解析以及格式化。delimiter 必须是一个单字符(i.e., 一个单一的 16位的代码单元); 所以 ASCII 可以作为分隔符,而 emoji 不可以。

# dsv.parse(string[, row]) <>

解析指定的 string, 返回解析后的行对象数组。

dsv.parseRows 不同, 这个方法要求 DSV 内容的第一行包含一组列名,这些列名将会被作为最终返回的数组的属性名。例如解析如下的 CSV 文件:

Year,Make,Model,Length
1997,Ford,E350,2.34
2000,Mercury,Cougar,2.38

返回的 JavaScript 数组为:

[
  {"Year": "1997", "Make": "Ford", "Model": "E350", "Length": "2.34"},
  {"Year": "2000", "Make": "Mercury", "Model": "Cougar", "Length": "2.38"}
]

返回的数组会包含一个 columns 属性表示输入数据的原始列名次序(因为解析之后成为对象类型,对象类型的属性迭代次序是不可靠的)。例如:

data.columns; // ["Year", "Make", "Model", "Length"]

如果没有指定 row 转换函数,则字段值将会是字符串。为了安全起见,没有将其自动转换为数值、日期或者其他的形式。在某些场景中,JavScript 会自动强制将字符串转为数值类型(例如使用 + 操作符), 但是更好的方式是使用 row 转换函数。

如果指定了 row 转换函数则会为每一行调用指定的函数,函数的参数依次为: 每一列的数据(d, 对象形式), 当前列的索引 i 以及列名数组。如果返回值为 null 或者 undefined 则当前行会被跳过并且最终不会出现在 dsv.parse 的解析结果中,返回值会作为当前行对象。例如:

var data = d3.csvParse(string, function(d) {
  return {
    year: new Date(+d.Year, 0, 1), // lowercase and convert "Year" to Date
    make: d.Make, // lowercase
    model: d.Model, // lowercase
    length: +d.Length // lowercase and convert "Length" to number
  };
});

注意: 使用 +parseIntparseFloat 通常刚快但也更严格. 例如, "30px" 使用 + 返回 NaN, 而使用 parseIntparseFloat 返回 30.

# dsv.parseRows(string[, row]) <>

解析指定的 string, 返回一个数组的数组,每行数据以数组的形式表示.

dsv.parse 不同, 这个方法将第一行识别为数据而非列名,因此 DSV 内容中不应该再在第一行给出列名. 每一行会被解析为数组而不是对象. 并且数组的长度是可变的(取决于当前行可以被拆分为多少份). 例如解析如下没有列名行的 CSV 文件:

1997,Ford,E350,2.34
2000,Mercury,Cougar,2.38

返回的 JavaScript 数组为:

[
  ["1997", "Ford", "E350", "2.34"],
  ["2000", "Mercury", "Cougar", "2.38"]
]

如果没有指定 row 转换函数,则字段值将会是字符串。为了安全起见,没有将其自动转换为数值、日期或者其他的形式。在某些场景中,JavScript 会自动强制将字符串转为数值类型(例如使用 + 操作符), 但是更好的方式是使用 row 转换函数。

如果指定了 row 转换函数则会为每一行调用指定的函数,函数的参数依次为: 每一列的数据(d, 数组形式), 当前列的索引 i 以及列名数组。如果返回值为 null 或者 undefined 则当前行会被跳过并且最终不会出现在 dsv.parseRows 的解析结果中,返回值会作为当前行对象。例如:

var data = d3.csvParseRows(string, function(d, i) {
  return {
    year: new Date(+d[0], 0, 1), // convert first colum column to Date
    make: d[1],
    model: d[2],
    length: +d[3] // convert fourth column to number
  };
});

事实上,row 类似于将 mapfilter 应用到返回的结果中.

# dsv.format(rows[, columns]) <>

将指定的 rows 对象数组格式化为 DSV 字符串。这个操作是 dsv.parse 的逆操作。行与行之间使用 \n 分开并且行内列之间使用指定的分隔符分开(比如 CSV 使用 ,)。如果行内某些数值已经包含分隔符或者换行符则使用双引号 " 来表示转义.

如果 columns 没有指定,则构成头行的列名的列表是由各行中所有对象的所有属性决定的,并且列名的次序是不确定的。如果指定了 columns,则其应该是由一组表示列名的字符串数组,并且列次序也会被确定。例如:

var string = d3.csvFormat(data, ["year", "make", "model", "length"]);

每一个行对象上的所有字段都会被强制转换为字符串。为了能对格式化操作进行更多的控制比如字段如何被格式化,首先遍历行对象数组,然后使用 dsv.formatRows

# dsv.formatRows(rows) <>

将指定行数组的数组格式化为字符串。这个操作是 dsv.parseRows 的逆操作。行与行之间使用 \n 分割,行内列与列之间使用指定的分隔符分割(比如 CSV 使用 ,)。如果值中已经包含分隔符或者换行符则使用双引号表示转义。

在显式的指定列名称时可以先使用 array.map 将对象数组转为数组的数组。例如:

var string = d3.csvFormatRows(data.map(function(d, i) {
  return [
    d.year.getFullYear(), // Assuming d.year is a Date object.
    d.make,
    d.model,
    d.length
  ];
}));

如果你乐意也可以使用 array.concat 来生成一个带有列名的数组,这样最终格式化字符串的第一行会包含列名:

var string = d3.csvFormatRows([[
    "year",
    "make",
    "model",
    "length"
  ]].concat(data.map(function(d, i) {
  return [
    d.year.getFullYear(), // Assuming d.year is a Date object.
    d.make,
    d.model,
    d.length
  ];
})));

Content Security Policy

如果 content security policy 到位的话,请注意 dsv.parse 可能会导致安全问题,因为其实现使用了 不安全的 - eval(这种方式解析更快),可以参考 源码,在不能保证安全时可以使用 dsv.parseRows 替代。

Byte-Order Marks

DSV 文件有时候以 byte order mark (BOM) 开始;例如从 Microsoft Excel 保存的 UTF-8 编码的 CSV 包含 BOM。在 web 上通常不是问题因为 UTF-8 解码算法 会自动删除 BOM. 但是在 Nodejs 中使用 UTF-8 编码时 不会移除 BOM

如果 BOM 没有被移除则文本的第一个字符为一个零宽度的不间断空格。所以一个带有 BOMCSVd3.csvParse 解析时第一列的名称会以一个零宽度的不间断空格开头,这个很难被发现因为打印时不会被看见。

在解析之前移除 BOM 考虑使用 strip-bom.

Command Line Reference

dsv2dsv

# dsv2dsv [options…] [file]

将指定的输入 DSV file 转换为另一种不同分隔符(或不同编码)的 DSV。如果没有指定 file 则默认读取输入流。例如将 CSV 转为 TSV

csv2tsv < example.csv > example.tsv

windows-1252 编码的 CSV 转为 utf-8 编码的 CSV:

dsv2dsv --input-encoding windows-1252 < latin1.csv > utf8.csv

# dsv2dsv -h
# dsv2dsv --help

输出帮助信息.

# dsv2dsv -V
# dsv2dsv --version

输出版本号.

# dsv2dsv -o file
# dsv2dsv --out file

指定输出文件名。 Defaults to “-” for stdout.

# dsv2dsv -r delimiter
# dsv2dsv --input-delimiter delimiter

指定输入文件的分隔符,默认为 "," 表示读取 CSV。 (You can enter a tab on the command line by typing ⌃V.)

# dsv2dsv --input-encoding encoding

指定输出字符的编码,默认为 utf-8.

# dsv2dsv -w delimiter
# dsv2dsv --output-delimiter delimiter

指定输出文件的分隔符,默认为 "," 表示读取 CSV。(You can enter a tab on the command line by typing ⌃V.)

# dsv2dsv --output-encoding encoding

指定输出字符的编码,默认为 utf-8.

# csv2tsv [options…] [file]

等价于 dsv2dsv, 但是 output delimiter 默认为 tab (\t).

# tsv2csv [options…] [file]

等价于 dsv2dsv, 但是 input delimiter 默认为 tab (\t).

dsv2json

# dsv2json [options…] [file]

将输入的 DSVfile 转为 JSON. 如果 file 没有定义则默认从输入流中读取. 例如将 CSV 转为 JSON:

csv2json < example.csv > example.json

或者将 CSV 转为换行符分割的 JSON 流:

csv2json -n < example.csv > example.ndjson

# dsv2json -h
# dsv2json --help

输出帮助信息.

# dsv2json -V
# dsv2json --version

输出版本号.

# dsv2json -o file
# dsv2json --out file

指定输出文件名. Defaults to “-” for stdout.

# dsv2json -r delimiter
# dsv2json --input-delimiter delimiter

指定输入文件的分隔符,默认为 "," 表示读取 CSV。 (You can enter a tab on the command line by typing ⌃V.)

# dsv2json --input-encoding encoding

指定输入字符的编码,默认为 utf-8.

# dsv2json -r encoding
# dsv2json --output-encoding encoding

指定输出字符的编码,默认为 utf-8.

# dsv2json -n
# dsv2json --newline-delimited

输出 newline-delimited JSON 而不是单一的 JSON 数组.

# csv2json [options…] [file]

等价于 dsv2json.

# tsv2json [options…] [file]

等价于 dsv2json, 但是 input delimiter 默认为 tab (\t).

json2dsv

# json2dsv [options…] [file]

将输入的 JSONfile 转为 CSV. 如果 file 没有定义则默认从输入流中读取. 例如将 JSON 转为 CSV:

json2csv < example.json > example.csv

或者将换行符分割的 JSON 转为 CSV:

json2csv -n < example.ndjson > example.csv

# json2dsv -h
# json2dsv --help

输出帮助信息.

# json2dsv -V
# json2dsv --version

输出版本号.

# json2dsv -o file
# json2dsv --out file

指定输出文件名. Defaults to “-” for stdout.

# json2dsv --input-encoding encoding

指定输入字符的编码,默认为 utf-8.

# json2dsv -w delimiter
# json2dsv --output-delimiter delimiter

指定输出文件的分隔符,默认为 "," 表示写为 CSV。 (You can enter a tab on the command line by typing ⌃V.)

# json2dsv --output-encoding encoding

指定输出字符的编码,默认为 utf-8.

# json2dsv -n
# json2dsv --newline-delimited

读取 newline-delimited JSON 而不是单个的 JSON 数组.

# csv2json [options…] [file]

等价于 json2dsv.

# tsv2json [options…] [file]

等价于 json2dsv, 但是 output delimiter 默认为 tab (\t).

最后更新: 2019-5-18 18:11:02
本站功能逐步完善中,如果您对本站有好的建议或者意见,欢迎留言。 取 消 确 定