INI is a text type

This commit is contained in:
Matt Nadareski
2026-03-18 14:14:01 -04:00
parent 380b1c711a
commit e1b6ff37d8
11 changed files with 91 additions and 47 deletions

View File

@@ -19,7 +19,6 @@ Generic helper classes that involve custom functionality and utility.
| Class | Notes |
| --- | --- |
| `BufferedStream` | Not a true stream implementation used for buffered, single-byte reads |
| `IniFile` | Key-value pair INI file, implementing `Dictionary<string, string?>` |
| `ParentablePath` | Class that represents a path that is rooted by a parent directory |
| `PathTool` | Utility class for common Path-related functions |
| `ReadOnlyBitStream` | Read-only stream implementation allowing bitwise reading |
@@ -138,6 +137,10 @@ Various encryption implementations that are used across multiple projects. Most
Classes focused on string comparison by natural sorting. For example, "5" would be sorted before "100".
### `SabreTools.Text.INI`
Key-value pair INI file, implementing `Dictionary<string, string?>`. Includes reader and writer classes for easier access.
## Releases
For the most recent stable build, download the latest release here: [Releases Page](https://github.com/SabreTools/SabreTools.IO/releases)

View File

@@ -15,6 +15,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SabreTools.Matching.Test",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SabreTools.Text.Compare.Test", "SabreTools.Text.Compare.Test\SabreTools.Text.Compare.Test.csproj", "{2FEFC7F7-814D-4FDB-84CA-946137373160}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SabreTools.Text.INI.Test", "SabreTools.Text.INI.Test\SabreTools.Text.INI.Test.csproj", "{4054E2C0-AC30-44EA-916E-73DD9FB3F2D0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -97,6 +99,18 @@ Global
{2FEFC7F7-814D-4FDB-84CA-946137373160}.Release|x64.Build.0 = Release|Any CPU
{2FEFC7F7-814D-4FDB-84CA-946137373160}.Release|x86.ActiveCfg = Release|Any CPU
{2FEFC7F7-814D-4FDB-84CA-946137373160}.Release|x86.Build.0 = Release|Any CPU
{4054E2C0-AC30-44EA-916E-73DD9FB3F2D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4054E2C0-AC30-44EA-916E-73DD9FB3F2D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4054E2C0-AC30-44EA-916E-73DD9FB3F2D0}.Debug|x64.ActiveCfg = Debug|Any CPU
{4054E2C0-AC30-44EA-916E-73DD9FB3F2D0}.Debug|x64.Build.0 = Debug|Any CPU
{4054E2C0-AC30-44EA-916E-73DD9FB3F2D0}.Debug|x86.ActiveCfg = Debug|Any CPU
{4054E2C0-AC30-44EA-916E-73DD9FB3F2D0}.Debug|x86.Build.0 = Debug|Any CPU
{4054E2C0-AC30-44EA-916E-73DD9FB3F2D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4054E2C0-AC30-44EA-916E-73DD9FB3F2D0}.Release|Any CPU.Build.0 = Release|Any CPU
{4054E2C0-AC30-44EA-916E-73DD9FB3F2D0}.Release|x64.ActiveCfg = Release|Any CPU
{4054E2C0-AC30-44EA-916E-73DD9FB3F2D0}.Release|x64.Build.0 = Release|Any CPU
{4054E2C0-AC30-44EA-916E-73DD9FB3F2D0}.Release|x86.ActiveCfg = Release|Any CPU
{4054E2C0-AC30-44EA-916E-73DD9FB3F2D0}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -12,16 +12,4 @@
Comment,
EndTopLevel,
}
/// <summary>
/// Different types of INI rows being parsed
/// </summary>
public enum IniRowType
{
None,
SectionHeader,
KeyValue,
Comment,
Invalid,
}
}

View File

@@ -25,7 +25,7 @@ namespace SabreTools.IO
public static string GetRuntimeDirectory()
{
#if NET20 || NET35 || NET40 || NET452
return Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
return System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
#else
return AppContext.BaseDirectory;
#endif

View File

@@ -0,0 +1,14 @@
namespace SabreTools.Text.INI
{
/// <summary>
/// Different types of INI rows being parsed
/// </summary>
public enum RowType
{
None,
SectionHeader,
KeyValue,
Comment,
Invalid,
}
}

View File

@@ -3,10 +3,8 @@ using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using SabreTools.IO.Readers;
using SabreTools.IO.Writers;
namespace SabreTools.IO
namespace SabreTools.Text.INI
{
/// <summary>
/// Key-value pair INI file
@@ -111,7 +109,7 @@ namespace SabreTools.IO
try
{
using IniWriter writer = new(stream, Encoding.UTF8);
using Writer writer = new(stream, Encoding.UTF8);
// Order the keys to link sections together
var orderedKeys = new string[_keyValuePairs.Keys.Count];
@@ -171,7 +169,7 @@ namespace SabreTools.IO
try
{
// TODO: Can we use the section header in the reader?
using var reader = new IniReader(stream, Encoding.UTF8);
using var reader = new Reader(stream, Encoding.UTF8);
string? section = string.Empty;
while (!reader.EndOfStream)
@@ -183,11 +181,11 @@ namespace SabreTools.IO
// Process the row according to type
switch (reader.RowType)
{
case IniRowType.SectionHeader:
case RowType.SectionHeader:
section = reader.Section;
break;
case IniRowType.KeyValue:
case RowType.KeyValue:
string? key = reader.KeyValuePair?.Key;
// Section names are prepended to the key with a '.' separating
@@ -198,9 +196,9 @@ namespace SabreTools.IO
this[key] = reader.KeyValuePair?.Value;
break;
case IniRowType.None:
case IniRowType.Comment:
case IniRowType.Invalid:
case RowType.None:
case RowType.Comment:
case RowType.Invalid:
default:
// No-op
break;

View File

@@ -3,9 +3,9 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
namespace SabreTools.IO.Readers
namespace SabreTools.Text.INI
{
public class IniReader : IDisposable
public class Reader : IDisposable
{
#region Fields
@@ -32,7 +32,7 @@ namespace SabreTools.IO.Readers
/// <summary>
/// Current row type
/// </summary>
public IniRowType RowType { get; private set; } = IniRowType.None;
public RowType RowType { get; private set; } = RowType.None;
/// <summary>
/// Current section being read
@@ -60,7 +60,7 @@ namespace SabreTools.IO.Readers
/// <summary>
/// Constructor for reading from a file
/// </summary>
public IniReader(string filename)
public Reader(string filename)
{
_reader = new StreamReader(filename);
}
@@ -68,7 +68,7 @@ namespace SabreTools.IO.Readers
/// <summary>
/// Constructor for reading from a stream
/// </summary>
public IniReader(Stream stream, Encoding encoding)
public Reader(Stream stream, Encoding encoding)
{
_reader = new StreamReader(stream, encoding);
}
@@ -76,7 +76,7 @@ namespace SabreTools.IO.Readers
/// <summary>
/// Constructor for reading from a stream reader
/// </summary>
public IniReader(StreamReader streamReader)
public Reader(StreamReader streamReader)
{
_reader = streamReader;
}
@@ -116,14 +116,14 @@ namespace SabreTools.IO.Readers
if (CurrentLine.StartsWith(";"))
{
KeyValuePair = null;
RowType = IniRowType.Comment;
RowType = RowType.Comment;
}
// Section
else if (CurrentLine.StartsWith("[") && CurrentLine.EndsWith("]"))
{
KeyValuePair = null;
RowType = IniRowType.SectionHeader;
RowType = RowType.SectionHeader;
Section = CurrentLine.TrimStart('[').TrimEnd(']');
}
@@ -140,7 +140,7 @@ namespace SabreTools.IO.Readers
string value = string.Join("=", valueArr).Trim();
KeyValuePair = new KeyValuePair<string, string>(key, value);
RowType = IniRowType.KeyValue;
RowType = RowType.KeyValue;
}
// Empty
@@ -148,14 +148,14 @@ namespace SabreTools.IO.Readers
{
KeyValuePair = null;
CurrentLine = string.Empty;
RowType = IniRowType.None;
RowType = RowType.None;
}
// Invalid
else
{
KeyValuePair = null;
RowType = IniRowType.Invalid;
RowType = RowType.Invalid;
if (ValidateRows)
throw new InvalidDataException($"Invalid INI row found, cannot continue: {CurrentLine}");

View File

@@ -2,9 +2,9 @@
using System.IO;
using System.Text;
namespace SabreTools.IO.Writers
namespace SabreTools.Text.INI
{
public class IniWriter : IDisposable
public class Writer : IDisposable
{
#region Private Properties
@@ -20,7 +20,7 @@ namespace SabreTools.IO.Writers
/// <summary>
/// Constructor for writing to a file
/// </summary>
public IniWriter(string filename)
public Writer(string filename)
{
_writer = new StreamWriter(filename);
}
@@ -28,7 +28,7 @@ namespace SabreTools.IO.Writers
/// <summary>
/// Consturctor for writing to a stream
/// </summary>
public IniWriter(Stream stream, Encoding encoding)
public Writer(Stream stream, Encoding encoding)
{
#if NET20 || NET35 || NET40
_writer = new StreamWriter(stream, encoding);
@@ -40,7 +40,7 @@ namespace SabreTools.IO.Writers
/// <summary>
/// Constructor for writing to a stream writer
/// </summary>
public IniWriter(StreamWriter streamWriter)
public Writer(StreamWriter streamWriter)
{
_writer = streamWriter;
}

View File

@@ -2,7 +2,7 @@ using System.IO;
using System.Text;
using Xunit;
namespace SabreTools.IO.Test
namespace SabreTools.Text.INI.Test
{
public class IniFileTests
{

View File

@@ -1,12 +1,10 @@
using System.IO;
using System.Text;
using SabreTools.IO.Readers;
using SabreTools.IO.Writers;
using Xunit;
namespace SabreTools.IO.Test.ReadersWriters
namespace SabreTools.Text.INI.Test
{
public class IniTests
public class ReaderWriterTests
{
[Fact]
public void EndToEndTest()
@@ -15,7 +13,7 @@ namespace SabreTools.IO.Test.ReadersWriters
// Build and write the INI
var stream = new MemoryStream();
var writer = new IniWriter(stream, Encoding.UTF8);
var writer = new Writer(stream, Encoding.UTF8);
writer.WriteSection("section1");
writer.WriteKeyValuePair("key1", "value1");
@@ -34,7 +32,7 @@ namespace SabreTools.IO.Test.ReadersWriters
// Parse the INI
stream.Seek(0, SeekOrigin.Begin);
var reader = new IniReader(stream, Encoding.UTF8);
var reader = new Reader(stream, Encoding.UTF8);
while (!reader.EndOfStream)
{

View File

@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
<IsPackable>false</IsPackable>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsNotAsErrors>CS0618</WarningsNotAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="8.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SabreTools.IO\SabreTools.IO.csproj" />
</ItemGroup>
</Project>