Repository navigation
Parser Options
IniParserOptions is a configuration class that controls how IniFileParser
interprets INI file content. All options default to the most common / lenient
behaviour so that existing code continues to work unchanged.
Use the fluent IniConfigBuilder methods to apply options when loading via the
registry, or pass an IniParserOptions instance directly to IniFileParser.Parse
when using the low-level API.
using var config = IniConfigRegistry.ForFile("app.ini")
.AddSearchPath(AppContext.BaseDirectory)
.EnableEscapeSequences() // decode \n, \t, \\, \xHH, …
.EnableQuotedValues() // strip surrounding "…" / '…'
.EnableLineContinuation() // join lines ending with \
.AssignmentDelimiters("=:") // accept '=' and ':'
.CaseSensitiveKeys() // AppName ≠ appname
.WithDuplicateKeyHandling(DuplicateKeyHandling.FirstWins)
.RegisterSection<IAppSettings>(new AppSettingsImpl())
.Build();Or supply a pre-built IniParserOptions object:
var opts = new IniParserOptions
{
AssignmentDelimiters = "=:",
EscapeSequences = true,
QuotedValues = true,
};
using var config = IniConfigRegistry.ForFile("app.ini")
.AddSearchPath(AppContext.BaseDirectory)
.WithParserOptions(opts)
.RegisterSection<IAppSettings>(new AppSettingsImpl())
.Build();- A repeated
[Section]header continues the existing section instead of replacing it, so keys above the second header are kept andDuplicateKeyHandlingapplies across both parts. - A leading byte order mark (
\uFEFF) in the string passed toIniFileParser.Parse(string)is ignored.
Controls which characters are treated as key/value assignment delimiters.
Default is =: (both equals and colon are accepted).
[Database]
Host = server
Port: 5432var opts = new IniParserOptions { AssignmentDelimiters = "=:" };
var file = IniFileParser.Parse(content, opts);Builder shorthand:
.AssignmentDelimiters("=:")Controls what happens when the same key appears more than once inside one section.
| Value | Behaviour |
|---|---|
LastWins (default)
|
Each occurrence overwrites the previous one; the last value wins |
FirstWins |
The first occurrence is kept; later duplicates are silently ignored |
ThrowError |
An InvalidOperationException is thrown on the first duplicate |
[Database]
Host = server1
Host = server2 ; duplicate!// LastWins (default) → Host = "server2"
var file = IniFileParser.Parse(content);
// FirstWins → Host = "server1"
var file = IniFileParser.Parse(content,
new IniParserOptions { DuplicateKeyHandling = DuplicateKeyHandling.FirstWins });
// ThrowError → throws InvalidOperationException
var file = IniFileParser.Parse(content,
new IniParserOptions { DuplicateKeyHandling = DuplicateKeyHandling.ThrowError });Builder shorthand:
.WithDuplicateKeyHandling(DuplicateKeyHandling.FirstWins)When true, values enclosed in matching double-quotes "…" or single-quotes '…'
have their surrounding quote characters stripped. Interior whitespace is preserved.
QuotedValues |
INI line | Parsed value |
|---|---|---|
false (default)
|
key = "hello world" |
"hello world" |
true |
key = "hello world" |
hello world |
true |
key = ' spaces ' |
spaces |
true |
key = plain value |
plain value (unchanged — no quotes)
|
When QuotedValues is on and EscapeSequences is off, the parser also undoes the quote
escaping the writer applies to quoted values (\" inside "…"), so values written with
QuoteValuesOnWrite() round-trip. Hand-written values such as "C:\Temp\" are kept as written.
var opts = new IniParserOptions { QuotedValues = true };
var file = IniFileParser.Parse(content, opts);Builder shorthand:
.EnableQuotedValues()When true, a backslash (\) at the very end of a value line causes the parser to
append the trimmed content of the following line, forming a single value.
The chain continues as long as each continuation line also ends with \.
[Message]
Text = Hello, \
World!LineContinuation |
Parsed value |
|---|---|
false (default)
|
Hello, \ |
true |
Hello, World! |
Multi-line example:
[Script]
Command = first \
second \
thirdParsed value with LineContinuation = true: first second third
Details:
- Only an odd number of trailing backslashes continues the line:
abc\continues,C:\Temp\\(an escaped backslash) does not. - A section header is never swallowed: continuation stops before a
[Section]line. When the next line is a section header, a blank line or the end of the file, the value is kept as written, including its trailing backslash (e.g.Dir = C:\Temp\followed by[Next]). - An empty continuation line ends the value.
var opts = new IniParserOptions { LineContinuation = true };
var file = IniFileParser.Parse(content, opts);Builder shorthand:
.EnableLineContinuation()When true, standard C-style escape sequences in values are decoded.
| Sequence | Decoded character |
|---|---|
\\ |
Literal backslash \
|
\n |
Newline (LF, U+000A) |
\r |
Carriage return (CR, U+000D) |
\t |
Horizontal tab (U+0009) |
\0 |
Null character (U+0000) |
\" |
Double quote "
|
\' |
Single quote '
|
\a |
Bell / alert (U+0007) |
\b |
Backspace (U+0008) |
\xHH |
Character with hex code HH (two hex digits) |
Unrecognised sequences (e.g. \q) are left unchanged — the backslash is preserved.
[Paths]
DataDir = C:\\ProgramData\\MyApp
Greeting = Hello\nWorld
Tab = col1\tcol2
Bullet = \x2022 itemWith EscapeSequences = true:
| Key | Value |
|---|---|
DataDir |
C:\ProgramData\MyApp |
Greeting |
Hello + newline + World
|
Tab |
col1 + tab + col2
|
Bullet |
• item |
var opts = new IniParserOptions { EscapeSequences = true };
var file = IniFileParser.Parse(content, opts);Builder shorthand:
.EnableEscapeSequences()By default (false) key names within a section are compared
case-insensitively — AppName, appname, and APPNAME all refer to the
same entry.
When set to true, key comparisons use ordinal case-sensitive equality, so
AppName and appname are treated as different keys.
[S]
AppName = Pascal
appname = lowerCaseSensitiveKeys |
GetValue("AppName") |
GetValue("appname") |
Entry count |
|---|---|---|---|
false (default)
|
lower (LastWins)
|
lower |
1 |
true |
Pascal |
lower |
2 |
var opts = new IniParserOptions { CaseSensitiveKeys = true };
var file = IniFileParser.Parse(content, opts);Builder shorthand:
.CaseSensitiveKeys()When true, blank lines, comment lines (exactly as written) and lines that cannot be parsed are
kept on the parsed IniFile (IniSection.LeadingTrivia, IniEntry.LeadingTrivia,
IniFile.TrailingTrivia), and IniFileWriter writes them back unchanged. Default false.
IniConfigBuilder.PreserveFormatting() turns it on (see Saving).
By default (false) section names are compared case-insensitively —
[General], [GENERAL], and [general] all refer to the same section.
When set to true, section-name comparisons use ordinal case-sensitive
equality, so [General] and [GENERAL] are independent sections.
[General]
key = v1
[GENERAL]
key = v2CaseSensitiveSections |
GetSection("General") |
GetSection("GENERAL") |
Section count |
|---|---|---|---|
false (default)
|
v2 (LastWins)
|
v2 |
1 |
true |
v1 |
v2 |
2 |
var opts = new IniParserOptions { CaseSensitiveSections = true };
var file = IniFileParser.Parse(content, opts);Builder shorthand:
.CaseSensitiveSections()All options can be combined freely. Use the fluent builder methods to compose the exact set you need:
using var config = IniConfigRegistry.ForFile("app.ini")
.AddSearchPath(AppContext.BaseDirectory)
.EnableEscapeSequences()
.EnableQuotedValues()
.EnableLineContinuation()
.CaseSensitiveKeys()
.CaseSensitiveSections()
.WithDuplicateKeyHandling(DuplicateKeyHandling.ThrowError)
.RegisterSection<IAppSettings>(new AppSettingsImpl())
.Build();Or build the options object once and reuse it across multiple configs:
var strictOpts = new IniParserOptions
{
EscapeSequences = true,
QuotedValues = true,
CaseSensitiveKeys = true,
DuplicateKeyHandling = DuplicateKeyHandling.ThrowError,
};
using var configA = IniConfigRegistry.ForFile("a.ini")
.WithParserOptions(strictOpts)
.RegisterSection<IA>(new AImpl())
.Build();
using var configB = IniConfigRegistry.ForFile("b.ini")
.WithParserOptions(strictOpts)
.RegisterSection<IB>(new BImpl())
.Build();If you use IniFileParser directly (without IniConfig / IniConfigBuilder),
pass IniParserOptions as the second argument:
var opts = new IniParserOptions
{
EscapeSequences = true,
QuotedValues = true,
};
// From a string
var iniFile = IniFileParser.Parse(rawText, opts);
// From disk (sync)
var iniFile = IniFileParser.ParseFile("/path/to/file.ini", Encoding.UTF8, opts);
// From disk (async)
var iniFile = await IniFileParser.ParseFileAsync("/path/to/file.ini",
Encoding.UTF8, opts, cancellationToken);
// From any TextReader (e.g. an embedded resource); the reader is not disposed
using var reader = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream("MyProduct.defaults.ini")!);
var iniFile = IniFileParser.Parse(reader, opts);Files and readers are parsed through one small pooled buffer: a file that fits into it (up to 16 KB, most configuration files) is parsed as a whole, a larger one line by line while it is read. No string or buffer of the whole content is created, so large files do not end up on the large object heap. Only the section names, keys, values and comments of the result are allocated.
- Ini-File-Format — INI syntax overview, value formats, comments
-
Loading-Configuration —
IniConfigBuilderfluent API reference -
Registry-API — complete
IniConfigBuildermethod table