๐งฐ File Operations ยท 7 min read
CSV Delimiter in pandas
Pass sep to read_csv: sep=";" for semicolons, sep="\t" for tabs. The default is a comma, and delimiter= is an alias for the same argument. If you leave the default on a file that uses something else, pandas does not complain โ it returns one column whose name is the entire header line. That silent wrong shape is the reason this question keeps coming up: nothing in the output says "I could not find a comma", so the mistake surfaces later as aKeyError on a column that should exist.
The default is a comma, and it fails quietly
pandas documents sep as a string with a default of ','. There is no delimiter detection unless you ask for it. So a file exported by software that writes semicolons โ the common case in locales where the comma is the decimal mark โ is not rejected. It is parsed as a single column of text.
Measured on pandas 2.3.3, on a three-row file whose header is id;name;city:
| Call | Shape | Columns |
|---|---|---|
read_csv("semi.csv") | (3, 1) | id;name;city |
read_csv(..., sep=";") | (3, 3) | id, name, city |
read_csv(..., delimiter=";") | (3, 3) | identical to sep |
read_csv(..., sep=None, engine="python") | (3, 3) | sniffed correctly |
The first row is the failure mode worth memorising. A shape of (3, 1) with a long column name is what a wrong separator looks like โ not an exception. The general version of this symptom, in Excel as well as pandas, is covered in why a CSV opens in one column.
sep and delimiter are the same argument
The documentation calls delimiteran "alias for sep", so the two are interchangeable: read_csv(path, delimiter=";") and read_csv(path, sep=";") produce the same frame. Use one style in a codebase and keep it. sep is the shorter name and the one the rest of the documentation uses.
The cases where you must pass something: semicolon-delimited European exports, tab-delimited files that were saved with a .csv name, pipe-delimited output from older tools, and files whose first column is an ID containing no commas so that the mistake is easy to miss.
Let pandas guess: sep=None
From the read_csv documentation: "sep=Nonedetects the separator from the first valid row of the file with Python's builtin sniffer tool, csv.Sniffer; it is supported only by the Python parsing engine, which will be used automatically."
Two details matter in practice. It sniffs the first valid row, so a file with a preamble of comment lines or a title row can lead to a wrong guess. And it runs on the Python engine, which is slower on large files than the C engine. For a one-off look at an unfamiliar export, sep=None, engine="python" is the right call; for a nightly job reading the same file shape every day, hard-code the separator.
Multi-character separators are regex, and the C engine refuses them
The same documentation paragraph continues: separators longer than 1 character and different from \s+"will be interpreted as regular expressions and will force the use of the Python parsing engine. Note that regex delimiters are prone to ignoring quoted data."
If you force the C engine anyway, you get an error instead of a quiet engine switch. Measured on pandas 2.3.3 with sep=r";+", engine="c":
ValueError: the 'c' engine does not support regex separators
(separators > 1 char and different from '\s+' are interpreted as regex)The same regex separator with engine="python" returned the expected shape (3, 3). That is the whole trade: regex separators buy flexibility and cost speed, and they can misread quoted fields. If a quoted value legitimately contains the separator pattern, the regex path will split it. Run the file through the delimiter converter once and read a clean file afterwards.
Tabs, and the delim_whitespace deprecation
A tab-separated file loaded with the comma default shows the same failure. Measured: a two-line TSV read with defaults returned shape (2, 1) with a single column named id followed by a tab character and name; with sep="\t" it returned shape (2, 2).
Older code often uses delim_whitespace=True. On pandas 2.3.3 it still works but warns: "The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use sep='\s+'instead." The replacement is not identical in spirit โ \s+ matches any run of whitespace, tabs included, which is usually what people wanted from the keyword in the first place.
When the separator is not the problem
Two other faults produce frames that look like a separator mistake.
A UTF-8 byte order mark attaches itself to the first column name, so df['id'] raises a KeyError while df.columns[0] prints something that looks right. Fix it with encoding="utf-8-sig". The encoding guide covers how to confirm which encoding you actually have.
Missing or unbalanced quotes make a parser merge rows or shift fields right. That usually surfaces as a field-count error rather than a one-column frame, which the field-count guide and the quoting rules deal with. And if you want the plain-language version of reading a file at all, opening a CSV in Python starts from zero.
Fixing it in the file instead
Passing sep is the right fix for a one-off analysis. It is the wrong fix when anything else will open the file: a spreadsheet, an upload form, a client. In that case the file should be a real comma CSV, and changing the delimiter explains what has to stay intact when you rewrite it โ quoting, embedded line breaks, and the decision not to use find-and-replace on a file whose values contain commas.
A related question is what the extension claims. A tab-separated file called data.csv will be read as CSV by every tool that trusts names; CSV vs TXT explains why the extension matters even though pandas ignores it.
Frequently Asked Questions
How do I set the CSV delimiter in pandas?
Pass sep to read_csv: sep=';' for semicolons, sep='\t' for tabs, sep='|' for pipes. The default is ',', and delimiter= is an alias for the same argument, so read_csv('f.csv', delimiter=';') behaves identically. If you do not know the separator, sep=None asks pandas to detect it with Python's csv.Sniffer.
Why does pandas read my CSV as a single column?
Because the separator in the file is not the one pandas assumed. The default is a comma, so a semicolon-delimited export becomes one column whose name is the whole header line, and no exception is raised. We measured it on pandas 2.3.3 with a three-row semicolon file: the result was shape (3, 1) with a single column named id;name;city. Passing sep=';' returned shape (3, 3).
What does sep=None do in read_csv?
It detects the separator from the first valid row of the file using Python's built-in csv.Sniffer, and pandas switches to the Python parsing engine automatically because the C engine cannot do it. The documentation notes that sniffing works from that first valid row, so a file with a long preamble of comments can still be sniffed wrong.
Why do I get a ValueError about the C engine not supporting regex separators?
Because the separator you passed is longer than one character, or is not the whitespace pattern \s+. The documentation states that separators longer than 1 character and different from '\s+' are interpreted as regular expressions and force the Python parsing engine. If you also pin engine='c', pandas refuses: the 'c' engine does not support regex separators. Drop engine='c' or pass a single character.
What replaced delim_whitespace in pandas?
sep='\s+'. On pandas 2.3.3, delim_whitespace=True still runs but emits a FutureWarning: the keyword is deprecated and will be removed in a future version, and the message tells you to use sep='\s+' instead. Note that \s+ collapses runs of whitespace, so a field containing a space is still one field, but two spaces between fields do not create an empty column.
Is it better to fix the delimiter in pandas or in the file?
Fix it in pandas when the analysis is the only consumer. Convert the file when anything else will read it โ a spreadsheet, an upload form, a colleague. Quoting rules make hand-editing risky: a value like Smith, John inside a semicolon-separated file is a single field, and a find-and-replace on commas turns it into two. The delimiter converter rewrites separators while keeping quoted fields intact.
Sources
- pandas documentation, pandas.read_csv:
sepis a string with a default of',';sep=Nonedetects the separator from the first valid row withcsv.Snifferand is supported only by the Python engine, which is used automatically; separators longer than one character and different from\s+are interpreted as regular expressions and force the Python engine; regex delimiters are prone to ignoring quoted data;delimiteris an alias forsep. Retrieved 2026-10-31. - Local measurement, 2026-10-31, pandas 2.3.3 on Python 3.13.5: semicolon file
id;name;citywith three data rows gave shape (3, 1) and one column namedid;name;citywith the default separator, shape (3, 3) withsep=";", the same withdelimiter=";", and shape (3, 3) withsep=None, engine="python".sep=r";+"raisedValueError: the 'c' engine does not support regex separators (separators > 1 char and different from '\s+' are interpreted as regex)on the C engine and returned shape (3, 3) on the Python engine. A tab-separated file gave shape (2, 1) with a column namedid\tnameby default and (2, 2) withsep="\t".delim_whitespace=Trueemitted the deprecation FutureWarning quoted above.
Stop Guessing the Separator
Drop the file into the delimiter converter and it tells you what is actually between the fields, then rewrites the file as a standard comma CSV with quoting intact โ so the pandas call works without a special argument.
Related reading
File Operations โ other guides that pair well with this one.
- Split a Large CSV File Online
- Compare Two CSV Files Online
- Convert CSV to PDF
- Convert CSV to Excel Without Excel
Browse all guides in the NoCodeCSV blog.