Skip to content

Commit 99d98a5

Browse files
committed
feat(parse): delimiter auto
1 parent 889cf1f commit 99d98a5

5 files changed

Lines changed: 111 additions & 2 deletions

File tree

csv

Submodule csv updated 160 files

src/md/parse/errors.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ Finally, each type of error identified by its code property may include addition
8989
- code: `CSV_INVALID_OPTION_DELIMITER`
9090
message: `Invalid option delimiter: delimiter must be a non empty string or buffer, got {value}`
9191
Thrown when the delimiter option is incorrect.
92+
- code: `CSV_INVALID_OPTION_DELIMITER_AUTO`
93+
message: `delimiter_auto must be a boolean or a configuration object, got {value}`
94+
since: 6.3.0
95+
Thrown when the record_delimiter option is incorrect.
9296
- code: `CSV_INVALID_OPTION_RECORD_DELIMITER`
9397
message: ``Invalid option `record_delimiter`: value must be a non empty string or buffer or array of string|buffer, got {value}``
9498
since: 5.0.0

src/md/parse/options.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ All options are optional. The options from the [Node.js Stream Writable](https:/
3838
- [`delimiter`](/parse/options/delimiter/) (string|Buffer|[string|Buffer])
3939
_Since version 0.0.1_
4040
Set one or several field delimiters containing one or several characters. It defaults to `,` (comma).
41+
- [`delimiter_auto`](/parse/options/delimiter_auto/) (boolean|object)
42+
_Since version 6.3.0_
43+
Automatically discovers the character used to separate the fields inside a record.
4144
- [`encoding`](/parse/options/encoding/) (string|Buffer)
4245
_Since version 4.13.0_
4346
Set the input and output encodings. Using `null` or `false` output the raw buffer instead of a string and it defaults to `utf8`.

src/md/parse/options/delimiter.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Defines the character(s) used to delimitate the fields inside a record. A single
2121
- Optional
2222
- Default: `","` (a one character comma)
2323
- Since: 0.0.1
24-
- Related: [`record_delimiter`](/parse/options/record_delimiter/), [`quote`](/parse/options/quote/), [`escape`](/parse/options/escape/) — see [Available Options](/parse/options/#available-options)
24+
- Related: [`delimiter_auto`](/parse/options/delimiter_auto/), [`record_delimiter`](/parse/options/record_delimiter/), [`quote`](/parse/options/quote/), [`escape`](/parse/options/escape/) — see [Available Options](/parse/options/#available-options)
2525

2626
It is not possible to escape a delimiter. A field must be quoted if it contains a delimiter which should not be interpreted as such.
2727

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
---
2+
title: Option delimiter_auto
3+
navtitle: delimiter_auto
4+
description: Option "delimiter_auto" automatically discovers the character used to separate the fields inside a record.
5+
keywords:
6+
- csv
7+
- parse
8+
- options
9+
- delimiter
10+
- delimiter_auto
11+
- discovery
12+
- separator
13+
- tsv
14+
- fields
15+
- records
16+
---
17+
18+
# Option `delimiter_auto`
19+
20+
Automatically discover the character used to delimit the fields inside a record. Only a single delimiter is discovered. The selection is based on a scoring function.
21+
22+
- Type: `boolean|object`
23+
- Optional
24+
- Default: `false`
25+
- Since: 6.3.0
26+
- Related: [`delimiter`](/parse/options/delimiter/), [`record_delimiter`](/parse/options/record_delimiter/) — see [Available Options](/parse/options/#available-options)
27+
28+
When enabled, the [`delimiter`](/parse/options/delimiter/) option is initialized to an empty array and the delimiter is resolved from the data instead. Discovery happens only once: as soon as a delimiter is selected, it is reused for the remaining records.
29+
30+
## Usage
31+
32+
Pass `true` to enable discovery with the default configuration:
33+
34+
`embed:packages/csv-parse/samples/option.delimiter_auto.ts`
35+
36+
In this example the colon (`:`) is selected over the comma (`,`) even though the comma carries a higher preference weight: the dot appears more often and just as consistently across both lines, so its score is higher.
37+
38+
## Configuration
39+
40+
The option is configured as a boolean or a configuration object.
41+
42+
The boolean value `true` is converted to a configuration object filled with the default values.
43+
44+
The configuration object accepts the following properties:
45+
46+
- `preferred (object)`
47+
Map where each key is a character code and each value is the weight applied to that character's score. Defaults to:
48+
49+
| Character | Weight |
50+
| --------------- | ------ |
51+
| `,` (comma) | 1.8 |
52+
| `\t` (tab) | 1.8 |
53+
| `;` (semicolon) | 1.6 |
54+
| ` ` (space) | 1.6 |
55+
| `:` (colon) | 1.5 |
56+
| `.` (period) | 1.4 |
57+
| `/` (slash) | 1.4 |
58+
59+
Any character not listed defaults to a weight of `1`.
60+
61+
- `score (function)`
62+
User function computing the score of a character. See [Scoring function](#scoring-function) below.
63+
- `size (number)`
64+
Number of bytes buffered from the beginning of the input before discovery runs. Defaults to `2048`. In streaming mode, discovery is deferred until at least `size` bytes have been received, or until the input ends, whichever comes first. A larger value samples more data and improves accuracy at the cost of buffering more before the first record is emitted.
65+
66+
## How discovery works
67+
68+
1. The first chunk of data, up to `size` bytes, is parsed into records.
69+
2. For every ASCII character code (`0` to `126`), the number of occurrences is counted on each line.
70+
3. A score is computed per character from its total count, its standard deviation across lines, and its preference weight.
71+
4. The character with the highest score is selected as the delimiter.
72+
73+
Because only character codes below `127` are inspected, multi-byte and non-ASCII delimiters are not considered.
74+
75+
## Scoring function
76+
77+
The scoring function returns the score used to extract the dominant character. It receives the `info` and `options` arguments.
78+
79+
- `info (object)`
80+
- `char_code (number)`
81+
Character code being scored.
82+
- `lines (array)`
83+
Array indexed by line number whose values are the number of occurrences of the character on that line.
84+
- `std (number)`
85+
Standard deviation of the occurrences across the lines. A low value means the character appears with a consistent frequency on every line.
86+
- `total (number)`
87+
Total number of occurrences of the character.
88+
- `preferred (boolean)`
89+
`true` when the character is present in the `preferred` map.
90+
- `options (object)`
91+
The normalized `delimiter_auto` configuration object, exposing `preferred`, `score`, and `size`.
92+
93+
The default implementation rewards characters that are both frequent and evenly distributed across lines (low standard deviation), weighted by the preference map:
94+
95+
```js
96+
const score = (info, options) => {
97+
return (info.total - info.std) * (options.preferred[info.char_code] || 1)
98+
}
99+
```
100+
101+
You can supply your own `score` function to change how the delimiter is selected, for example to consider only preferred characters or to penalize rare ones more aggressively.
102+
`

0 commit comments

Comments
 (0)