跳至内容

d3-dsv

此模块提供用于分隔符分隔值的解析器和格式化程序,最常见的是 逗号分隔值 (CSV) 或制表符分隔值 (TSV)。这些表格格式在 Microsoft Excel 等电子表格程序中很受欢迎,并且通常比 JSON 更节省空间。此实现基于 RFC 4180

例如,要解析

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

要格式化

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

要使用不同的分隔符,例如对于管道分隔值使用“|”,请使用 d3.dsvFormat

js
d3.dsvFormat("|").parse("foo|bar\n1|2")) // [{foo: "1", bar: "2"}, columns: ["foo", "bar"]]

为了在浏览器中轻松加载 DSV 文件,请参阅 d3-fetchd3.csvd3.tsvd3.dsv 方法。

dsvFormat(delimiter)

js
const csv = d3.dsvFormat(",");

源代码 · 为指定的分隔符delimiter构造一个新的 DSV 解析器和格式化程序。分隔符必须是单个字符(即单个 16 位代码单元);因此,ASCII 分隔符是可以的,但表情符号分隔符则不行。

dsv.parse(string, row)

注意

此方法需要不安全的 eval 内容安全策略

js
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 数组是

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

返回的数组还公开了一个 columns 属性,该属性包含输入顺序的列名(与 Object.keys 相反,Object.keys 的迭代顺序是任意的)。例如

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

如果列名不唯一,则只返回每个名称的最后一个值;要访问所有值,请改用 dsv.parseRows(参见 示例)。

如果没有指定行转换函数row,则字段值为字符串。为了安全起见,不会自动转换为数字、日期或其他类型。在某些情况下,JavaScript 可能会自动为您将字符串强制转换为数字(例如,使用 + 运算符),但最好指定行转换函数row。有关可推断和强制转换常见类型(如数字和字符串)的便捷行转换函数row,请参见 d3.autoType

如果指定了行转换函数row,则将为每行调用指定函数,并将表示当前行的对象(d)、从第一个非标题行开始的索引(i)以及列名数组作为参数传递。如果返回的值为 null 或 undefined,则跳过该行,并将从 dsv.parse 返回的数组中省略该行;否则,返回的值将定义相应的行对象。例如

js
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 而不是 parseIntparseFloat 通常更快,但更严格。例如,"30px" 使用 + 强制转换时返回 NaN,而 parseInt 和 parseFloat 返回 30

dsv.parseRows(string, row)

js
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 数组是

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

如果没有指定行转换函数row,则字段值为字符串。为了安全起见,不会自动转换为数字、日期或其他类型。在某些情况下,JavaScript 可能会自动为您将字符串强制转换为数字(例如,使用 + 运算符),但最好指定行转换函数row。有关可推断和强制转换常见类型(如数字和字符串)的便捷行转换函数row,请参见 d3.autoType

如果指定了行转换函数row,则将为每行调用指定函数,并将表示当前行的数组(d)、从第一个行开始的索引(i)以及列名数组作为参数传递。如果返回的值为 null 或 undefined,则跳过该行,并将从 dsv.parse 返回的数组中省略该行;否则,返回的值将定义相应的行对象。例如

js
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 类似于对返回的行应用 mapfilter 运算符。

dsv.format(rows, columns)

js
d3.csvFormat([{foo: "1", bar: "2"}]) // "foo,bar\n1,2"
js
d3.csvFormat([{foo: "1", bar: "2"}], ["foo"]) // "foo\n1"

源代码 · 将指定的对象rows 数组格式化为分隔符分隔的值,返回一个字符串。此操作是 dsv.parse 的逆操作。每行将由换行符(\n)分隔,每行内的每个列将由分隔符(例如逗号,,)分隔。包含分隔符、双引号(")或换行符的值将使用双引号进行转义。

如果未指定columns,则通过rows 中所有对象的所有属性的并集确定形成标题行的列名列表;列的顺序是非确定性的。如果指定了columns,则它是一个表示列名的字符串数组。例如

js
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)

js
d3.csvFormatBody([{foo: "1", bar: "2"}]) // "1,2"
js
d3.csvFormatBody([{foo: "1", bar: "2"}], ["foo"]) // "1"

源代码 · 等同于 dsv.format,但省略标题行。例如,在将行追加到现有文件时,这很有用。

dsv.formatRows(rows)

js
d3.csvFormatRows([["foo", "bar"], ["1", "2"]]) // "foo,bar\n1,2"

源代码 · 将指定的字符串数组数组rows 格式化为分隔符分隔的值,返回一个字符串。此操作是 dsv.parseRows 的逆操作。每行将由换行符(\n)分隔,每行内的每个列将由分隔符(例如逗号,,)分隔。包含分隔符、双引号(")或换行符的值将使用双引号进行转义。

要将对象数组转换为数组数组,同时明确指定列,请使用 array.map。例如

js
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 起来,以生成第一行

js
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)

js
d3.csvFormatRow(["foo", "bar"]) // "foo,bar"

源代码 · 将单个字符串数组row 格式化为分隔符分隔的值,返回一个字符串。行内的每个列将由分隔符(例如逗号,,)分隔。包含分隔符、双引号(")或换行符的值将使用双引号进行转义。

dsv.formatValue(value)

js
d3.csvFormatValue("foo") // "foo"

来源 · 将单个或字符串格式化为分隔符分隔的值,返回一个字符串。包含分隔符、双引号(")或换行符的值将使用双引号进行转义。

csvParse(string, row)

等效于 d3.dsvFormat(",").parse.

csvParseRows(string, row)

等效于 d3.dsvFormat(",").parseRows.

csvFormat(rows, columns)

等效于 d3.dsvFormat(",").format.

csvFormatBody(rows, columns)

等效于 d3.dsvFormat(",").formatBody.

csvFormatRows(rows)

等效于 d3.dsvFormat(",").formatRows.

csvFormatRow(row)

等效于 d3.dsvFormat(",").formatRow.

csvFormatValue(value)

等效于 d3.dsvFormat(",").formatValue.

tsvParse(string, row)

等效于 d3.dsvFormat("\t").parse.

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.parsedsv.parseRows 结合使用,用作访问器函数。例如,考虑以下 CSV 文件

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

d3.csvParse 一起使用时,

js
d3.csvParse(string, d3.autoType)

结果的 JavaScript 数组为

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

类型推断的工作原理如下。对于给定对象中的每个,计算修剪后的值;然后,该值将按以下方式重新分配

  1. 如果为空,则为 null
  2. 如果完全是 "true",则为 true
  3. 如果完全是 "false",则为 false
  4. 如果完全是 "NaN",则为 NaN
  5. 否则,如果可以转换为数字,则为数字。
  6. 否则,如果是一个仅日期或日期时间字符串,则为 Date。
  7. 否则,为字符串(原始未修剪的值)。

带有前导零的值可能会被强制转换为数字;例如 "08904" 强制转换为 8904。但是,额外的字符(例如逗号或单位(例如"$1.00""(123)""1,234""32px")将阻止数字强制转换,导致字符串。

日期字符串必须采用 ECMAScript 的ISO 8601 格式的子集。当指定仅日期字符串(例如 YYYY-MM-DD)时,推断的时间为午夜 UTC;但是,如果指定日期时间字符串(例如 YYYY-MM-DDTHH:MM)但没有时区,则假定为本地时间。

自动类型推断主要旨在为 dsv.formatdsv.formatRows 提供安全、可预测的行为,以用于常见的 JavaScript 类型。如果您需要不同的行为,则应实现自己的行访问器函数。

有关更多信息,请参阅d3.autoType 笔记本.

内容安全策略

如果启用了内容安全策略,请注意 dsv.parsescript-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.