2018-05-15 (火)
[C#] iniファイルを読み込む方法たち
kernel32.dll(Win32API)、INIFileParser(NuGet)、独自実装(IniFile.cs)のパターンで取得します。
環境
- Windows 10 Pro 64bit 1709
- Visual Studio Community 2017 15.5.7
- .NET Framework 4.6.1
- C# 7.0
結果
読み込むiniファイルの内容です。
;セクションに属さない値は無効
RootKey = root
[Global]
;Comment = コメント行です
Count = 100
;セクション名の前後のスペースは無視される
[ Section Space Name ]
fullscreen = false
;大文字小文字は無視される
[IgnoreCase]
name = 前後のスペースはトリム
[日本語セクション]
files = img.png;title.jpg;
読み取り結果です。使用例は後述。
変数: 取得時の指定方法 | kernel32.dll | INIFileParser.dll | IniFile.cs |
---|---|---|---|
s1: “RootKey” | "" | root | root |
s2: “Section Space Name” | false | false | false |
s3: " Section Space Name " | false | null | null |
s4: “IgnoreCase” | (文字化けで取得可) | 前後のスペースはトリム | 前後のスペースはトリム |
s5: “ignorecase” | (文字化けで取得可) | null | 前後のスペースはトリム |
s6: “日本語セクション” | "" | img.png;title.jpg; | img.png;title.jpg; |
実装
kernel32.dll
注意
日本語環境ではShift_JIS
で動作するため、iniファイルはShift_JIS
で保存する必要があります。
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
public class IniFile
{
[DllImport("kernel32.dll")]
public static extern uint GetPrivateProfileString(
string lpAppName, string lpKeyName, string lpDefault,
StringBuilder lpReturnedString, uint nSize, string lpFileName);
private readonly StringBuilder _builder = new StringBuilder(255);
public string FullName { get; set; }
public IniFile(string filePath)
{
FullName = Path.GetFullPath(filePath);
}
public string Read(string section, string key, string defaultValue = null)
{
_builder.Clear();
GetPrivateProfileString(section, key, defaultValue, _builder, 255, FullName);
return _builder.ToString();
}
}
使用例
var ini = new IniFile("Sample.ini");
var s1 = ini.Read("", "RootKey"); // ""
var s2 = ini.Read("Section Space Name", "fullscreen"); // false
var s3 = ini.Read(" Section Space Name ", "fullscreen"); // false
var s4 = ini.Read("IgnoreCase", "name"); // (文字化けで取得可)
var s5 = ini.Read("ignorecase", "name"); // (文字化けで取得可)
var s6 = ini.Read("日本語セクション", "files"); // ""
// s1のケースでSectionをnullで指定すると・・・
var s1_null = ini.Read(null, "RootKey"); // Section Space Name
INIFileParser.dll
Nugetから取得できます。
rickyah/ini-parser: Read/Write an INI file the easy way!
使用例
var parser = new IniParser.FileIniDataParser();
var data = parser.ReadFile("Sample.ini");
var s1 = data.Global["RootKey"]; // root
var s2 = data["Section Space Name"]["fullscreen"]; // false
var s3 = data[" Section Space Name "]["fullscreen"]; // null
var s4 = data["IgnoreCase"]["name"]; // 前後のスペースはトリム
var s5 = data["ignorecase"]["name"]; // null
var s6 = data["日本語セクション"]["files"]; // img.png;title.jpg;
IniFile.cs (私的メモ)
都合により、少し特殊仕様なIniの実装です。
末尾に+
がある場合は次の行の文字列も連結し、\+
は+
として扱います。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
public class IniFile
{
private const StringComparison Comparison = StringComparison.Ordinal;
private static readonly StringComparer Comparer = StringComparer.OrdinalIgnoreCase;
private readonly Dictionary<string, Dictionary<string, string>> _ini = new Dictionary<string, Dictionary<string, string>>(Comparer);
public IniFile(string file, Encoding encoding = null)
{
var lines = File.ReadLines(file, encoding ?? Encoding.GetEncoding("shift_jis"))
.Where(x => !string.IsNullOrWhiteSpace(x))
.Select(x => x.Trim())
.Where(x => !x.StartsWith(";", Comparison));
var currentSection = new Dictionary<string, string>(Comparer);
var key = "";
var isNextLine = false;
_ini[""] = currentSection;
foreach (var line in lines)
{
if (isNextLine)
{
isNextLine = IsNextLine(line);
currentSection[key] += GetValue(line, 0, isNextLine).TrimEnd();
continue;
}
if (line[0] == '[' && line[line.Length - 1] == ']')
{
currentSection = new Dictionary<string, string>(Comparer);
var section = line.Substring(1, line.Length - 2).Trim();
_ini[section] = currentSection;
continue;
}
var index = line.IndexOf("=", Comparison);
if (index == -1) continue;
key = line.Substring(0, index).Trim();
isNextLine = IsNextLine(line);
currentSection[key] = GetValue(line, index + 1, isNextLine).Trim();
}
const char nextLineChar = '+';
const char escapeChar = '\\';
bool IsNextLine(string line) => line[line.Length - 1] == nextLineChar && line.Length >= 2 &&
line[line.Length - 2] != escapeChar;
string GetValue(string line, int startIndex, bool isNextLineChar)
{
var length = line.Length - startIndex - (isNextLineChar ? 1 : 0);
var sb = new StringBuilder();
var escape = false;
var ignoreSpace = false;
// "\Ima+ ge\+123.png"のようになっていたら、"\Image+123.png"と見なす
foreach (var c in line.Skip(startIndex).Take(length))
{
if (escape)
{
if (c != nextLineChar)
sb.Append(escapeChar);
}
else
{
switch (c)
{
case escapeChar:
escape = true;
ignoreSpace = false;
continue;
case nextLineChar:
ignoreSpace = true;
continue;
case ' ' when ignoreSpace:
continue;
}
}
escape = false;
ignoreSpace = false;
sb.Append(c);
}
return sb.ToString();
}
}
public string GetValueRoot(string key, string @default = null) => GetValue("", key, @default);
public string GetValue(string section, string key = "", string @default = null)
{
return _ini.TryGetValue(section, out var iniSection) &&
iniSection.TryGetValue(key, out var value)
? value
: @default;
}
public IEnumerable<string> GetKeys(string section)
{
return _ini.TryGetValue(section, out var iniSection) ? iniSection.Keys : Enumerable.Empty<string>();
}
public IEnumerable<string> GetSections() => _ini.Keys.Where(x => x != "");
}
使用例
var m = new IniFile("Sample.ini"); // root
var x1 = m.GetValue("", "RootKey"); // false
var x2 = m.GetValue("Section Space Name", "fullscreen"); // null
var x3 = m.GetValue(" Section Space Name ", "fullscreen"); // ""
var x4 = m.GetValue("IgnoreCase", "name"); // 前後のスペースはトリム
var x5 = m.GetValue("ignorecase", "name"); // 前後のスペースはトリム
var x6 = m.GetValue("日本語セクション", "files"); // img.png;title.jpg;
感謝
- kernel32.dll
- INIFileParser.dll
- 実装ヒント
関連記事
新着記事