Home / Blog / Other text utilities

Text and CSV conversion

Convert a Tab-Delimited Text File to CSV in Python

Parse tab-delimited text with Python’s CSV reader, validate the expected columns, and write comma-delimited output without damaging quoted values.

Convert a Tab-Delimited Text File to CSV in Python

To convert a text file to CSV in Python, first identify how the text represents records and fields. A tab-delimited table can be read with csv.reader using a tab delimiter, then written with csv.writer using commas. Renaming the extension does not perform that transformation.

Define the source contract

The example below expects UTF-8 text, an optional byte-order mark, a header containing id, name and note in that order, and exactly three fields in every record. Fields follow the usual CSV quoting convention even though tabs separate them. Those assumptions are part of the converter, not facts implied by the .txt extension.

A valid source could contain the identifier 00123, the name Rao, Anika and a note containing a quotation mark. The comma inside the name is ordinary data in the tab-delimited source. The writer will add the necessary quotes when that field becomes part of comma-delimited output.

Read, validate and write

import csv

with open("input.txt", encoding="utf-8-sig", newline="") as source:
    rows = list(csv.reader(source, delimiter="\t", strict=True))
if not rows or rows[0] != ["id", "name", "note"]:
    raise ValueError("Expected the header id, name, note")
if any(len(row) != 3 for row in rows):
    raise ValueError("Every record must contain three fields")

with open("output.csv", "w", encoding="utf-8", newline="") as target:
    csv.writer(target, lineterminator="\n").writerows(rows)

Save this as a Python script beside input.txt and run it with your Python interpreter. It creates output.csv only after the source has been parsed and its header and row widths checked. Use a separate destination filename, as shown, so an input mistake does not overwrite the source.

Let the parser handle records

Opening both files with newline="" lets the CSV module handle newline conventions. A quoted field may contain a real line break, so one logical record can span more than one physical line. Splitting the entire file with splitlines() before parsing would destroy that distinction.

The reader returns strings. The identifier 00123 stays five characters, and an empty field stays empty. This code does not automatically turn dates, identifiers or decimal amounts into Python numeric values. A spreadsheet may still infer types when you later open the CSV, so check its import settings separately.

Interpret a rejected file

A header mismatch means the input does not meet the declared column order or names. Inspect the actual header rather than deleting that check until the program runs. A field-count error may indicate a wrong delimiter, an unescaped delimiter in a value, or a source record with missing data.

Strict parsing can reject malformed quoting, but it is not a full business-data validator. The program does not know whether an identifier exists or whether a note is appropriate. Add those checks according to the receiving system’s requirements after the structural checks are understood.

Verify values after conversion

Read output.csv again with csv.reader and compare its rows with the parsed source rows. Check the number of records and fields as well as representative values. Include a comma-containing name, a quotation mark, a blank final field, a non-English name and a multiline note in your test set.

For a second run, change one identifier from 00123 to 00999 and confirm that the new output contains 00999. A successful exit message alone does not prove that you opened the current output file or used the intended input path.

Adjust the design for large inputs

This example stores all rows in memory so validation finishes before the destination is opened. For a large file, use a validated temporary output and replace the final destination only after processing succeeds. Streaming directly into the final filename can leave a partial CSV when a later row fails.

If the source is fixed-width, one item per line, or arbitrary prose, choose a different parser. Changing delimiter="\t" to a guessed character cannot recover a table that was never represented that way.

Reference

The Python CSV documentation describes reader, writer, dialect parameters and the newline requirement. The example here adds an explicit three-column contract.