CSV to INI Converter

A CSV to INI converter turns each row into a section. This one lets you name the sections from any column, makes duplicate names unique so a row cannot vanish into a reader that merges them, quotes the values a reader would otherwise misread, and gives you the dialect choices INI actually varies on. Nothing is uploaded.

Need to drop columns or dedupe the ids first? Open the app

The duplicate section that eats a row

An INI file is a list of named sections, and the names are supposed to be unique. What happens when they are not depends on the reader, and none of the answers are good: some merge the two sections into one, some keep the last and discard the first, and a few raise an error.

A CSV, meanwhile, has no such rule. Two rows can perfectly well share an id, a name or a code, and often do: a customer with two addresses, a product with two suppliers, a host with two ports. Turn that into an INI with one section per row and one of those rows disappears.

So duplicates are detected before anything is written, and the second occurrence becomes [name_2], the third [name_3], with a count in the warnings:

2 section names were already taken and got a numeric suffix. Two sections
with the same name make every INI reader either merge them or keep only
the last, so a row would have vanished.

The section column is still written as an ordinary key inside its section, which is what makes the rename non-destructive: [prod_2] still has env = prod inside it, so the original value is there and a round trip recovers it.

A worked example

Connection profiles, one per environment:

env,host,port,pool_size,ssl_mode,region_code
production,db-prod-01.internal,5432,20,require,01730
staging,db-staging-01.internal,5432,5,require,00412
development,localhost,5433,2,disable,00000

With env as the section column:

; 3 rows, one section each
; Section names come from the "env" column

[production]
env = production
host = db-prod-01.internal
port = 5432
pool_size = 20
ssl_mode = require
region_code = 01730

[staging]
env = staging
host = db-staging-01.internal
...

The region code keeps its leading zeros, because every value is written as text and INI has no numeric type to lose them to. That is one of the few places where INI's lack of a type system is an advantage.

Feed that back through the INI to CSV converter and you get the same three rows and the same six columns, which is the test that matters for a round trip.

INI has no specification, so these are options

There is no INI standard. There is a widely shared core, bracketed section headers and key = value pairs, and after that implementations disagree about almost everything: which character starts a comment, whether values can be quoted, whether quotes are stripped on read, whether keys may repeat, whether sections nest, and whether a colon can stand in for the equals sign.

Rather than picking a house style and hoping, the parts that vary are exposed.

Comment character. The semicolon is the older Windows convention and what classic INI parsers expect. The hash is what Python's configparser and most Unix-adjacent tools use. Many readers take both. Pick the one your consumer definitely knows, because an unrecognised comment character is a syntax error rather than a comment.

Quoting. A value with a leading or trailing space, or containing a # or ;, is quoted by default, because otherwise the space gets stripped and everything after the comment character may be discarded. The catch is that not every reader takes the quotes back off; configparser keeps them as part of the value. Hence the switch.

Empty cells. By default the key is written with nothing after the equals sign, which most readers return as an empty string. The alternative is omitting the key entirely, so it is absent rather than blank, which is a different claim and one some readers distinguish.

A newline inside a value becomes a \n escape. A raw one would end the value and leave the rest as a malformed line, and there is no continuation syntax portable enough to rely on. Column headings containing =, : or brackets are rewritten with underscores, since all of those end a key.

Questions

What happens if two rows produce the same section name?

The second gets a numeric suffix and the change is reported. This matters more than it sounds: two sections with the same name make every INI reader either merge them or keep only the last, so one of your rows would silently disappear from the file. A duplicate id column is exactly the kind of thing you discover this way.

Which column becomes the section name?

Whichever you pick from the dropdown, which lists the columns of the file you just loaded. An id, code or name column is the usual choice. If you pick none, sections are numbered with a prefix you can set, so you get row1, row2 and so on. The chosen column is still written as a key inside its section, so nothing is lost when a name has to be uniquified.

Semicolon or hash for comments?

Both are in the wild and it is a choice here. The semicolon is the older Windows convention and what most classic INI parsers expect; the hash is what Python's configparser and a lot of Unix tools use, and many readers accept both. Pick whichever your consumer definitely handles, since a comment character your reader does not know becomes a syntax error rather than a comment.

Why are some values quoted?

Because they have leading or trailing spaces, or contain a comment character. Without quotes, a trailing space is usually stripped and anything after a hash or semicolon may be read as a comment, so the value silently changes. The catch worth knowing is that not every reader strips the quotes back off: Python's configparser keeps them as part of the value, so the option can be turned off.

What happens to an empty cell?

By default the key is written with nothing after the equals sign, which most readers give back as an empty string. There is an option to leave the key out entirely instead, which means the key is absent rather than blank. Which one you want depends on whether your reader distinguishes the two, and plenty do.

What if a value contains a line break?

It is written as a backslash-n escape. A raw newline would end the value and turn the rest of it into a malformed line, and INI has no portable continuation syntax. Python's configparser with interpolation off and Rust's rust-ini both leave the escape alone, and a person reading the file can see what happened.

Is INI actually a standard?

No, and that is why this page has options rather than a house style. There is a widely shared core of bracketed section headers, key equals value pairs and one comment character, and after that implementations disagree about quoting, escapes, repeated keys, nesting and whether a colon can replace the equals sign. What is written here is the shared core, with the variable parts exposed.

Convert your CSV to INI

No sign-up, no upload, no row cap. A section per row, no row lost to a duplicate name, dialect choices where INI actually varies.

Back to the converter