Consider this small delimited file:
id,name;label
1,A;B
With a comma delimiter, it has two columns:
[
["id", "name;label"],
["1", "A;B"]
]
With a semicolon delimiter, it also has two columns:
[
["id,name", "label"],
["1,A", "B"]
]
Both interpretations have consistent row widths. Choosing whichever delimiter appears first in a candidate list hides an unresolved decision.
Separate parsing from selection
Parse each candidate using a parser configured for that delimiter. A candidate is usable only if it passes the syntax and table-shape checks your importer requires.
Those checks should include quoted fields, escaped quotes, embedded newlines, and a consistent number of fields per record. Counting punctuation or splitting the first line cannot handle all of them.
RFC 4180 describes the comma-separated format and its quoting rules. Semicolon-separated files are a separate dialect that an importer may deliberately support; the RFC does not define automatic delimiter detection.
The selection step can then work with small summaries. Here is one explicit heuristic: prefer the unique valid candidate with the most columns, and otherwise ask for a choice.
function chooseDelimiter(candidates) {
const usable = candidates.filter(c => c.valid && c.columns > 1);
if (usable.length === 0) {
return { status: "needs-choice", reason: "no-multicolumn-candidate" };
}
const width = Math.max(...usable.map(c => c.columns));
const leaders = usable.filter(c => c.columns === width);
if (leaders.length !== 1) {
return {
status: "needs-choice",
reason: "ambiguous",
delimiters: leaders.map(c => c.delimiter)
};
}
return { status: "suggested", delimiter: leaders[0].delimiter };
}
console.log(chooseDelimiter([
{ delimiter: ",", valid: true, columns: 2 },
{ delimiter: ";", valid: true, columns: 2 },
{ delimiter: "\t", valid: true, columns: 1 }
]));
// { status: "needs-choice", reason: "ambiguous", delimiters: [",", ";"] }
This function selects among parser results. It does not parse CSV, and valid must come from the parser and validation step, not from a delimiter count.
A suggestion still needs a preview
The widest candidate is a heuristic, not proof. Punctuation inside unquoted values can make the wrong dialect look plausible. A known export format or an expected header schema is stronger evidence.
Also, one-column CSV is valid. The columns > 1 filter means that this heuristic cannot infer a useful delimiter from it; it does not mean the file should be rejected.
Offer comma, semicolon, and tab as explicit choices when those are the dialects your importer supports. Reparse the original text after a choice, show the resulting headers and sample rows, and keep existing imported data until confirmation.
Representing ambiguity as a normal result gives the interface something useful to do before a mistaken guess changes stored data.
Follow this DEV profile for more practical examples of reliable JavaScript import workflows.
Top comments (0)