d3-dsv
此模块提供用于分隔符分隔值的解析器和格式化程序,最常见的是 逗号分隔值 (CSV) 或制表符分隔值 (TSV)。这些表格格式在 Microsoft Excel 等电子表格程序中很受欢迎,并且通常比 JSON 更节省空间。此实现基于 RFC 4180。
例如,要解析
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
d3.dsvFormat("|").parse("foo|bar\n1|2")) // [{foo: "1", bar: "2"}, columns: ["foo", "bar"]]
为了在浏览器中轻松加载 DSV 文件,请参阅 d3-fetch 的 d3.csv、d3.tsv 和 d3.dsv 方法。
dsvFormat(delimiter)
const csv = d3.dsvFormat(",");
源代码 · 为指定的分隔符delimiter构造一个新的 DSV 解析器和格式化程序。分隔符必须是单个字符(即单个 16 位代码单元);因此,ASCII 分隔符是可以的,但表情符号分隔符则不行。
dsv.parse(string, row)
注意
此方法需要不安全的 eval 内容安全策略。
d3.csvParse("foo,bar\n1,2") // [{foo: "1", bar: "2"}, columns: ["foo", "bar"]]
源代码 · 解析指定字符串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
属性,该属性包含输入顺序的列名(与 Object.keys 相反,Object.keys 的迭代顺序是任意的)。例如
data.columns // ["Year", "Make", "Model", "Length"]
如果列名不唯一,则只返回每个名称的最后一个值;要访问所有值,请改用 dsv.parseRows(参见 示例)。
如果没有指定行转换函数row,则字段值为字符串。为了安全起见,不会自动转换为数字、日期或其他类型。在某些情况下,JavaScript 可能会自动为您将字符串强制转换为数字(例如,使用 + 运算符),但最好指定行转换函数row。有关可推断和强制转换常见类型(如数字和字符串)的便捷行转换函数row,请参见 d3.autoType。
如果指定了行转换函数row,则将为每行调用指定函数,并将表示当前行的对象(d
)、从第一个非标题行开始的索引(i
)以及列名数组作为参数传递。如果返回的值为 null 或 undefined,则跳过该行,并将从 dsv.parse 返回的数组中省略该行;否则,返回的值将定义相应的行对象。例如
const data = d3.csvParse(string, (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
};
});
注意:使用 + 或 Number 而不是 parseInt 或 parseFloat 通常更快,但更严格。例如,"30px"
使用 + 强制转换时返回 NaN
,而 parseInt 和 parseFloat 返回 30
。
dsv.parseRows(string, row)
d3.csvParseRows("foo,bar\n1,2") // [["foo", "bar"], ["1", "2"]]
源代码 · 解析指定字符串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,则字段值为字符串。为了安全起见,不会自动转换为数字、日期或其他类型。在某些情况下,JavaScript 可能会自动为您将字符串强制转换为数字(例如,使用 + 运算符),但最好指定行转换函数row。有关可推断和强制转换常见类型(如数字和字符串)的便捷行转换函数row,请参见 d3.autoType。
如果指定了行转换函数row,则将为每行调用指定函数,并将表示当前行的数组(d
)、从第一个行开始的索引(i
)以及列名数组作为参数传递。如果返回的值为 null 或 undefined,则跳过该行,并将从 dsv.parse 返回的数组中省略该行;否则,返回的值将定义相应的行对象。例如
const data = d3.csvParseRows(string, (d, i) => {
return {
year: new Date(+d[0], 0, 1), // convert first column to Date
make: d[1],
model: d[2],
length: +d[3] // convert fourth column to number
};
});
实际上,row 类似于对返回的行应用 map 和 filter 运算符。
dsv.format(rows, columns)
d3.csvFormat([{foo: "1", bar: "2"}]) // "foo,bar\n1,2"
d3.csvFormat([{foo: "1", bar: "2"}], ["foo"]) // "foo\n1"
源代码 · 将指定的对象rows 数组格式化为分隔符分隔的值,返回一个字符串。此操作是 dsv.parse 的逆操作。每行将由换行符(\n
)分隔,每行内的每个列将由分隔符(例如逗号,,
)分隔。包含分隔符、双引号("
)或换行符的值将使用双引号进行转义。
如果未指定columns,则通过rows 中所有对象的所有属性的并集确定形成标题行的列名列表;列的顺序是非确定性的。如果指定了columns,则它是一个表示列名的字符串数组。例如
const string = d3.csvFormat(data, ["year", "make", "model", "length"]);
每行对象上的所有字段都将强制转换为字符串。如果字段值为 null 或 undefined,则使用空字符串。如果字段值是 Date,则使用 ECMAScript 日期时间字符串格式(ISO 8601 的子集):例如,UTC 午夜的日期将格式化为 YYYY-MM-DD
。要更好地控制哪些字段以及如何格式化字段,请先将rows 映射到字符串数组数组,然后使用 dsv.formatRows。
dsv.formatBody(rows, columns)
d3.csvFormatBody([{foo: "1", bar: "2"}]) // "1,2"
d3.csvFormatBody([{foo: "1", bar: "2"}], ["foo"]) // "1"
源代码 · 等同于 dsv.format,但省略标题行。例如,在将行追加到现有文件时,这很有用。
dsv.formatRows(rows)
d3.csvFormatRows([["foo", "bar"], ["1", "2"]]) // "foo,bar\n1,2"
源代码 · 将指定的字符串数组数组rows 格式化为分隔符分隔的值,返回一个字符串。此操作是 dsv.parseRows 的逆操作。每行将由换行符(\n
)分隔,每行内的每个列将由分隔符(例如逗号,,
)分隔。包含分隔符、双引号(")或换行符的值将使用双引号进行转义。
要将对象数组转换为数组数组,同时明确指定列,请使用 array.map。例如
const string = d3.csvFormatRows(data.map((d, i) => {
return [
d.year.getUTCFullYear(), // Assuming d.year is a Date object.
d.make,
d.model,
d.length
];
}));
如果您愿意,还可以将此结果与列名数组 array.concat 起来,以生成第一行
const string = d3.csvFormatRows([[
"year",
"make",
"model",
"length"
]].concat(data.map((d, i) => {
return [
d.year.getUTCFullYear(), // Assuming d.year is a Date object.
d.make,
d.model,
d.length
];
})));
dsv.formatRow(row)
d3.csvFormatRow(["foo", "bar"]) // "foo,bar"
源代码 · 将单个字符串数组row 格式化为分隔符分隔的值,返回一个字符串。行内的每个列将由分隔符(例如逗号,,
)分隔。包含分隔符、双引号(")或换行符的值将使用双引号进行转义。
dsv.formatValue(value)
d3.csvFormatValue("foo") // "foo"
来源 · 将单个值或字符串格式化为分隔符分隔的值,返回一个字符串。包含分隔符、双引号(")或换行符的值将使用双引号进行转义。
csvParse(string, row)
csvParseRows(string, row)
等效于 d3.dsvFormat(",").parseRows
.
csvFormat(rows, columns)
csvFormatBody(rows, columns)
等效于 d3.dsvFormat(",").formatBody
.
csvFormatRows(rows)
等效于 d3.dsvFormat(",").formatRows
.
csvFormatRow(row)
等效于 d3.dsvFormat(",").formatRow
.
csvFormatValue(value)
等效于 d3.dsvFormat(",").formatValue
.
tsvParse(string, row)
tsvParseRows(string, row)
等效于 d3.dsvFormat("\t").parseRows
.
tsvFormat(rows, columns)
等效于 d3.dsvFormat("\t").format
.
tsvFormatBody(rows, columns)
等效于 d3.dsvFormat("\t").formatBody
.
tsvFormatRows(rows)
等效于 d3.dsvFormat("\t").formatRows
.
tsvFormatRow(row)
等效于 d3.dsvFormat("\t").formatRow
.
tsvFormatValue(value)
等效于 d3.dsvFormat("\t").formatValue
.
autoType(object)
来源 · 给定一个表示解析后的行的对象(或数组),推断对象上值的类型并相应地强制转换它们,返回被修改的对象。此函数旨在与 dsv.parse 和 dsv.parseRows 结合使用,用作行访问器函数。例如,考虑以下 CSV 文件
Year,Make,Model,Length
1997,Ford,E350,2.34
2000,Mercury,Cougar,2.38
与 d3.csvParse 一起使用时,
d3.csvParse(string, d3.autoType)
结果的 JavaScript 数组为
[
{"Year": 1997, "Make": "Ford", "Model": "E350", "Length": 2.34},
{"Year": 2000, "Make": "Mercury", "Model": "Cougar", "Length": 2.38}
]
类型推断的工作原理如下。对于给定对象中的每个值,计算修剪后的值;然后,该值将按以下方式重新分配
- 如果为空,则为
null
。 - 如果完全是
"true"
,则为true
。 - 如果完全是
"false"
,则为false
。 - 如果完全是
"NaN"
,则为NaN
。 - 否则,如果可以转换为数字,则为数字。
- 否则,如果是一个仅日期或日期时间字符串,则为 Date。
- 否则,为字符串(原始未修剪的值)。
带有前导零的值可能会被强制转换为数字;例如 "08904"
强制转换为 8904
。但是,额外的字符(例如逗号或单位(例如,"$1.00"
、"(123)"
、"1,234"
或 "32px"
)将阻止数字强制转换,导致字符串。
日期字符串必须采用 ECMAScript 的ISO 8601 格式的子集。当指定仅日期字符串(例如 YYYY-MM-DD)时,推断的时间为午夜 UTC;但是,如果指定日期时间字符串(例如 YYYY-MM-DDTHH:MM)但没有时区,则假定为本地时间。
自动类型推断主要旨在为 dsv.format 和 dsv.formatRows 提供安全、可预测的行为,以用于常见的 JavaScript 类型。如果您需要不同的行为,则应实现自己的行访问器函数。
有关更多信息,请参阅d3.autoType 笔记本.
内容安全策略
如果启用了内容安全策略,请注意 dsv.parse 在 script-src
指令中需要 unsafe-eval
,因为它是为了快速解析而(安全)使用了动态代码生成。(请参阅来源。)或者,使用 dsv.parseRows.
字节顺序标记
DSV 文件有时以字节顺序标记 (BOM)开头;例如,从 Microsoft Excel 中以 CSV UTF-8 格式保存电子表格将包含一个 BOM。在网络上,这通常不是问题,因为 UTF-8 解码算法(在编码标准中指定)会删除 BOM。另一方面,Node.js 不会在解码 UTF-8 时删除 BOM。
如果 BOM 未被删除,则文本的第一个字符为零宽度不间断空格。因此,如果 CSV 文件带 BOM 被 d3.csvParse 解析,则第一列的名称将以零宽度不间断空格开头。这很难发现,因为此字符在打印时通常不可见。
要在解析之前删除 BOM,请考虑使用 strip-bom.