Home

If you find any mistakes here or anywhere else on my website, email website@CoryHufford.com.


Python Functions for Reading Delimiter Separated Value (DSV) Files

September 14, 2026

Below are two Python functions for reading DSV files, useful for ephemeral data processing or visualization scripts. The second is a simplified version of the first with a few of the optional parameters missing, and it behaves as if the missing optional parameters are set to their defaults.

These functions are made for reading files that look like this:

category, count, fraction
foo,        123,      0.4
bar,        456,      0.7
baz,        789,      0.0

With the optional parameters left at their defaults, this example would be parsed as the structure:

[
    {"category": "foo", "count": "123", "fraction": "0.4"},
    {"category": "bar", "count": "456", "fraction": "0.7"},
    {"category": "baz", "count": "789", "fraction": "0.0"},
]

Parameters:

delimiter
Value separator. Can be any strictly positive length.
strip_elements
Whether to strip whitespace from values. For example, with this set to False the first line of the above example would be parsed as {"category": "foo", " count": "        123", " fraction": "      0.4"}.
skip_lines
How many leading lines should be skipped before attempting to parse the rest of the file. Useful if the file to be parsed has some sort of header before the line containing the column names.
column_types
Per-column types/conversion functions. Any column without a conversion is left as strings. For example, column_types = {"count": int, "fraction": float} would produce {"category": "foo", "count": 123, "fraction": 0.4} for the first line of the example above.

Full Version

import collections.abc
import typing

def read_dsv_file_rowwise(
    file_path: str,
    *,
    delimiter:      str  = ",",
    strip_elements: bool = True,
    skip_lines:     int  = 0,
    column_types: collections.abc.Mapping[
        str,
        collections.abc.Callable[[str], typing.Any]
    ] = {},
) -> list[dict[str, typing.Any]]:
    split_str: collections.abc.Callable[[str], list[str]] = (
        (lambda s: [sub.strip() for sub in s.split(delimiter)])
        if strip_elements else
        (lambda s: s.split(delimiter))
    )
    with open(file_path, "r") as file:
        for _ in range(skip_lines):
            file.readline()
        column_names: list[str] = split_str(file.readline().rstrip("\r\n"))
        column_funcs: list[
            collections.abc.Callable[[str], typing.Any] | None
        ] = [column_types.get(name) for name in column_names]
        rows: list[dict[str, typing.Any]] = []
        while line := file.readline():
            if len(line := line.rstrip("\r\n")) == 0:
                continue
            rows.append({
                name: (part if func is None else func(part))
                for name, func, part
                in zip(column_names, column_funcs, split_str(line))
            })
    return rows

Simplified Version

import collections.abc
import typing

def read_dsv_file_rowwise(
    file_path: str,
    *,
    delimiter: str = ",",
    column_types: collections.abc.Mapping[
        str,
        collections.abc.Callable[[str], typing.Any]
    ] = {},
) -> list[dict[str, typing.Any]]:
    def split_str(s: str) -> list[str]:
        return [sub.strip() for sub in s.split(delimiter)]
    with open(file_path, "r") as file:
        column_names: list[str] = split_str(file.readline().rstrip("\r\n"))
        rows: list[dict[str, typing.Any]] = []
        while line := file.readline():
            if len(line := line.rstrip("\r\n")) == 0:
                continue
            rows.append({
                name: column_types.get(name, lambda x: x)(part)
                for name, part in zip(column_names, split_str(line))
            })
    return rows

Back to top

Home