Turn a TSV into a CSV, a CSV into a TSV, or a semicolon file from a German copy of Excel into a comma file your import script accepts. The tool parses every field before writing it out, so a comma inside a quoted name stays in the name instead of starting a new column, which a find and replace on tabs cannot guarantee.
id name city note
1 Lovelace, Ada London wrote the "first" program
2 Hopper, Grace New York found an actual bug
3 Turing, Alan Wilmslow id,name,city,note
1,"Lovelace, Ada",London,"wrote the ""first"" program"
2,"Hopper, Grace",New York,found an actual bug
3,"Turing, Alan",Wilmslow, The names contain commas, so the tool quotes them. The note with quotes in it is quoted too, and its inner quotes are doubled. Every other field is left unquoted.
A comma triggers quoting in CSV, and a tab triggers it in TSV. The rule depends on the target, so a value can be unquoted in the TSV and quoted in the CSV.
The field is wrapped in quotes and each inner quote is doubled, so say "hi" becomes "say ""hi""". This is the RFC 4180 rule, and Excel follows it.
Quoted line breaks stay inside the field. The line numbers on the left count physical lines instead of records, so they can run ahead of the row count.
Pick Always in the Quotes menu when an importer requires every field to be quoted. Empty fields then come out as "".
The IANA registration for text/tab-separated-values
forbids tabs inside fields and has no quoting at all. However,
most TSV files from Excel, Google Sheets and database exports
quote the CSV way anyway, so this tool reads quotes when they are
there. If your TSV has a " at the start of a value
that is meant as a literal character, the parser treats it as an opening
quote and the columns shift. The status line warns you when a
quote is never closed.
Set From by hand when the stats show one column where you expected several. Auto-detect looks at the first 20 lines and picks whichever of comma, tab, semicolon or pipe appears the same number of times on each line, so it can guess wrong on a one-column file or a single-line file. The output keeps the input's line endings (CRLF or LF) and drops a leading BOM.
function parse(text, d) {
const rows = [[]];
let f = "", q = false;
for (let i = 0; i < text.length; i++) {
const c = text[i];
if (q) {
if (c === '"' && text[i + 1] === '"') { f += '"'; i++; }
else if (c === '"') q = false;
else f += c;
} else if (c === '"' && f === "") q = true;
else if (c === d) { rows.at(-1).push(f); f = ""; }
else if (c === "\n" || c === "\r") {
if (c === "\r" && text[i + 1] === "\n") i++;
rows.at(-1).push(f); f = ""; rows.push([]);
} else f += c;
}
rows.at(-1).push(f);
return rows.filter((r) => r.length > 1 || r[0] !== "");
}
const quote = (v, d) =>
/["\r\n]/.test(v) || v.includes(d) ? '"' + v.replace(/"/g, '""') + '"' : v;
const tsvToCsv = (tsv, to = ",") =>
parse(tsv, "\t").map((r) => r.map((v) => quote(v, to)).join(to)).join("\n");
console.log(tsvToCsv('id\tname\tnote\n1\tLovelace, Ada\twrote the "first" program\n')); A quote at the start of a field opens a quoted field, while a quote in the middle of a value is kept as a literal character. Most TSV exports behave the same way.
<?php
$in = fopen('data.tsv', 'r');
$out = fopen('data.csv', 'w');
while (($row = fgetcsv($in, null, "\t", '"', '')) !== false) {
if ($row === [null]) continue; // blank line
fputcsv($out, $row, ',', '"', '', "\n");
}
fclose($in);
fclose($out); Passing an empty escape character (PHP 7.4+) makes fgetcsv and fputcsv follow RFC 4180 instead of treating backslash as special. The eol argument needs PHP 8.1.
import csv, io
tsv = 'id\tname\tnote\n1\tLovelace, Ada\twrote the "first" program\n'
rows = csv.reader(io.StringIO(tsv), delimiter="\t")
out = io.StringIO()
csv.writer(out, delimiter=",", lineterminator="\n").writerows(rows)
print(out.getvalue(), end="")
# Files: open(..., newline="", encoding="utf-8") on both sides,
# and quoting=csv.QUOTE_ALL on the writer to quote every field. The csv module handles quoting on both sides. Swap the delimiter arguments to go from CSV to TSV.
RFC 4180 defines CSV quoting: fields containing the delimiter, a double quote or a line break are enclosed in double quotes, and inner quotes are doubled. The IANA registration for text/tab-separated-values is stricter and bans tabs inside fields. However, most TSV exports follow the CSV quoting rules, so this tool reads and writes those.
This tool follows the specifications below. Read them when you need the exact normative wording, for example to settle an edge case between two implementations.
Paste the TSV or click Upload, leave From on Auto-detect and To on Comma. Copy the result or click Download .csv to save it.
Set From to Semicolon (or Auto-detect) and To to Comma. The file is parsed and rewritten, so quoted values that contain commas get quoted correctly in the new file.
Yes. Pick Tab in the To menu, or click Swap after a conversion to reverse the direction and feed the output back in. The download becomes a .tsv file.
A field is quoted when it contains the target delimiter, a double quote or a line break, as RFC 4180 requires. Choose Always in the Quotes menu if your importer wants every field quoted.
No. Parsing and conversion run in JavaScript on this page, and Upload reads the file from your disk with FileReader without sending it anywhere.
Tidy the rows with the CSV Cleaner, check the result in CSV to Table, or turn it into JSON or SQL INSERT statements.