d3-dsv
这个模块提供了一个针对分隔符文件/字符串的解析和格式化工具,大多数情况下是comma- (CSV) 或 tab
- 分割(TSV)。这种扁平的格式在表格类数据中很流行如 Excel
, 并且比 JSON
更节省空间。这个模块的实现基于 RFC 4180。
CSV
与 TSV
的转换以及格式化是内置的,比如解析:
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-request 的 d3.csv 和 d3.tsv 方法。
Installing
NPM
安装: npm install d3-dsv
。此外还可以下载 latest release。可以直接从 d3js.org 以 standalone 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>
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
};
});
注意: 使用 +
比 parseInt 或 parseFloat 通常刚快但也更严格. 例如, "30px"
使用 +
返回 NaN
, 而使用 parseInt
和 parseFloat
返回 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 类似于将 map 和 filter 应用到返回的结果中.
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 到位的话,请注意 Byte-Order Marks
DSV
文件有时候以 byte order mark (BOM) 开始;例如从 Microsoft Excel
保存的 UTF-8
编码的 CSV
包含 BOM
。在 web
上通常不是问题因为 UTF-8 解码算法 会自动删除 BOM
. 但是在 Nodejs
中使用 UTF-8
编码时 不会移除 BOM
。
如果 BOM
没有被移除则文本的第一个字符为一个零宽度的不间断空格。所以一个带有 BOM
的 CSV
由 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
csv2tsv [options…] [file]
等价于 dsv2json
dsv2json [options…] [file]
将输入的 DSV
file 转为 JSON
. 如果 file 没有定义则默认从输入流中读取. 例如将 CSV
转为 JSON
:
csv2json < example.csv > example.json
或者将 CSV
转为换行符分割的 JSON
流:
csv2json -n < example.csv > example.ndjson
csv2json [options…] [file]
等价于 json2dsv
json2dsv [options…] [file]
将输入的 JSON
file 转为 CSV
. 如果 file 没有定义则默认从输入流中读取. 例如将 JSON
转为 CSV
:
json2csv < example.json > example.csv
或者将换行符分割的 JSON
转为 CSV
:
json2csv -n < example.ndjson > example.csv
csv2json [options…] [file]
等价于 json2dsv.
tsv2json [options…] [file]
等价于 json2dsv, 但是 output delimiter 默认为 tab
(\t).