跳至內容

d3-dsv

此模組提供分隔符號值的剖析器和格式化程式,最常見的是 逗號分隔值 (CSV) 或 tab 分隔值 (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(",");

原始碼 · 建立新的 DSV 解析器和格式化器,用於指定的分隔符分隔符必須是單一字元(,單一 16 位元碼元);因此,ASCII 分隔符是可以的,但表情符號分隔符不行。

dsv.parse(字串, )

注意

此方法需要不安全的 eval 內容安全性政策

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

原始碼 · 解析指定的字串,該字串必須採用分隔符分隔值格式,並使用適當的分隔符,傳回一個陣列的物件,代表已解析的列。

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 不同,後者的迭代順序是任意的)。例如

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

如果欄位名稱不唯一,只會傳回每個名稱的最後一個值;若要存取所有值,請改用 dsv.parseRows(請參閱 範例)。

如果未指定轉換函式,欄位值會是字串。為了安全,不會自動轉換為數字、日期或其他類型。在某些情況下,JavaScript 可能自動將字串強制轉換為數字(例如,使用 + 算子),但最好指定一個轉換函式。請參閱 d3.autoType,取得一個方便的轉換函式,用於推論和強制轉換常見類型,例如數字和字串。

如果指定了轉換函式,則會針對每一列呼叫指定的函式,並傳遞一個代表目前列的物件(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(字串, )

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

原始碼 · 分析指定的 字串,該字串必須採用分隔符值格式,並使用適當的分隔符,傳回一個陣列,其中包含代表已分析列的陣列。

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"]
]

如果未指定轉換函式,欄位值會是字串。為了安全,不會自動轉換為數字、日期或其他類型。在某些情況下,JavaScript 可能自動將字串強制轉換為數字(例如,使用 + 算子),但最好指定一個轉換函式。請參閱 d3.autoType,取得一個方便的轉換函式,用於推論和強制轉換常見類型,例如數字和字串。

如果指定 轉換函式,則會針對每一列呼叫指定的函式,並傳入代表目前列的陣列 (d)、從第一列開始的索引 (i) 和欄位名稱陣列。如果傳回值為 null 或未定義,則會略過該列,且不會包含在 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
  };
});

實際上, 類似於將 mapfilter 算子套用至傳回的列。

dsv.format(, 欄位)

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

原始碼 · 將指定的物件 陣列格式化為分隔符值,傳回字串。此操作是 dsv.parse 的反向操作。每一列會以換行符號 (\n) 分隔,且每一列中的每一欄位會以分隔符 (例如逗號 ,) 分隔。包含分隔符、雙引號 (") 或換行符號的值會使用雙引號進行跳脫。

如果未指定 欄位,則會透過判斷 中所有物件的所有屬性的聯集來決定形成標題列的欄位名稱清單;欄位的順序是不確定的。如果指定 欄位,則它是一個代表欄位名稱的字串陣列。例如

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

每一列物件中的所有欄位都會強制轉換為字串。如果欄位值為 null 或未定義,則會使用空字串。如果欄位值為日期,則會使用 ECMAScript 日期時間字串格式 (ISO 8601 的子集):例如,UTC 子夜的日期會格式化為 YYYY-MM-DD。若要進一步控制格式化哪些欄位以及如何格式化,請先將 對應到字串陣列陣列,然後使用 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"

來源 · 將單一 value 或字串格式化為分隔符號值,並傳回字串。包含分隔符號、雙引號 (") 或換行符號的值會使用雙引號進行跳脫。

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. 否則,如果為僅日期或日期時間字串,則為日期。
  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.parse需要在script-src指令中設定unsafe-eval,因為為了快速解析而(安全地)使用動態程式碼產生。(請參閱原始碼。)或者,使用dsv.parseRows

位元組順序標記

DSV 檔案有時會以 位元組順序標記 (BOM) 開頭;例如,從 Microsoft Excel 以 CSV UTF-8 格式儲存試算表會包含 BOM。在網路上這通常不是問題,因為編碼標準中指定的 UTF-8 解碼演算法 會移除 BOM。另一方面,Node.js 在解碼 UTF-8 時 不會移除 BOM

如果未移除 BOM,文字的第一個字元會是零寬度不換行空白。因此,如果 d3.csvParse 分析帶有 BOM 的 CSV 檔案,第一欄的名稱會以零寬度不換行空白開頭。這可能很難發現,因為這個字元在列印時通常是看不見的。

若要在分析前移除 BOM,請考慮使用 strip-bom