2008年6月11日
C#でGetPrivateProfile*を使わずにINIファイルを扱う
ほんとにWindowsの世界では、定義をどこに浮かすのか紆余曲折が激しくて良く分からなくなる
考えられる候補は以下のような感じなのだが、INIファイルはそのお手軽さから結構生き残っていたりする。
- XMLファイル(動的プロパティも含む)
- レジストリ
- INIファイル
INIファイルを扱うためには、GetPrivateProfileStringとかのWin APIが用意されているわけだが…これらは、MSDNで以下のように書かれており、推奨されないAPIになっている。
注意 この関数は、16 ビット Windows ベースのアプリケーションとの互換性を保つ目的でのみ提供されています。Win32 ベースのアプリケーションでは、初期化情報をレジストリに格納してください。
しかし、このAPIは時期的に微妙だったのか、理由はわからないのだがWindows CEではサポートされてなかったりする
なので、C#でINIファイルを扱う方法を検索するとヒットするページで書かれているGetPrivateProfile*系のAPIを呼び出す方法はWindows CEでは使えない
と言うことで、INIファイルを扱うクラスを適当にでっちあげてみる
INIファイルには、RFCもないし方言もあるので以下のような形式を前提としてみた。
; comment [section] ; comment KEY = VALUE ; comment
まぁ、基本的なINIファイル形式ですが…以下のような制約があるので注意してください。普通に使用する分には問題ないと思います
- セクションは行の先頭から記述されている必要がある
- セクション名の空白文字は有効になる
- キー名の空白文字は無効になる
- キー名にセミコロンは使用できる
- セミコロン(;)以降はコメントとして扱うためVALUE(値)にセミコロンを含めることはできない
- VALUE(値)の後ろの空白文字(スペース, タブ)はTrimする
他にもありそうな気もしますが…。
まず、呼び出し側と言うか使い方は以下のような感じ。
IniFile ini = new IniFile(); // INIファイルインスタンス生成 ini.Filename = "\\TEST.INI"; // INIファイル名設定(フルパス) if (ini.Read() == false) // INIファイルの読み込み { // 読み込み失敗 Console.Error.WriteLine("INI file error."); return; } // [SECTION1] // KEY1=HOGE FUGA // KEY2=8080 string value1 = ini.GetString("SECTION1", "KEY1"); // value1 = "HOGE FUGA" int value2 = ini.GetStringInt("SECTION1", "KEY2", 80); // value2 = 8080
で、INIファイルを扱うクラス側のコード。バグなど含んでる可能性があるので、自己責任で
using System; using System.IO; using System.Collections.Generic; using System.Text; using System.Collections; using System.Text.RegularExpressions; namespace Slashcolon.Common { /// <summary> /// INIファイルを扱うクラス /// </summary> public class IniFile { /// <summary> /// INIファイル名 /// </summary> private string filename; /// <summary> /// セクションとキー名をキーにした値のハッシュテーブル /// </summary> private Hashtable hash; /// <summary> /// 定義ファイル名 /// </summary> public string Filename { get { return filename; } set { filename = value; } } /// <summary> /// INIファイルを扱うクラスのコンストラクタ /// </summary> public IniFile() { filename = null; hash = new Hashtable(); } /// <summary> /// 定義ファイルから整数値を取得する /// </summary> /// <param name="section">取得するセクション名</param> /// <param name="key">取得するキー名</param> /// <param name="value">取得できない場合に返すデフォルト値</param> /// <returns>取得した値</returns> public int GetInt(string section, string key, int value) { int result = value; try { // 文字列で取得して整数値へ変換 result = int.Parse(GetString(section, key)); } catch { // 変換失敗 } return result; } /// <summary> /// 定義ファイルから文字列を取得する /// </summary> /// <param name="section">取得するセクション名</param> /// <param name="key">取得するキー名</param> /// <returns>取得した文字列。取得失敗の場合はnullを返す</returns> public string GetString(string section, string key) { return (string)hash[section + "#" + key]; } /// <summary> /// 定義ファイルをメモリに読み込む /// </summary> /// <returns>読み込み結果 true 成功 false 失敗</returns> public bool Read() { int codePage = 932; // MS932(Shift_JIS)コードページ string section = ""; string buffer; string[] result; try { using (StreamReader sr = new StreamReader(filename, Encoding.GetEncoding(codePage))) { while ((buffer = sr.ReadLine()) != null) { if (buffer == "") { continue; } result = IniParse(buffer); if (result.Length == 0) { continue; } if (result.Length == 1) { // セクション名設定 section = result[0]; } if (result.Length == 2) { // ハッシュテーブル設定 hash.Add(section + "#" + result[0], result[1]); } } } } catch { return false; } return true; } private string[] IniParse(string buffer) { ArrayList list = new ArrayList(); if (Regex.IsMatch(buffer, "^\\[(.+)\\](\\s*);(.*)$|^\\[(.+)\\]$")) { // セクション string section = Regex.Replace(buffer, "\\](.*)$", ""); section = Regex.Replace(section, "^\\[", ""); list.Add(section); } if (Regex.IsMatch(buffer, "^(.+)=(.*)$")) { // キー string key = Regex.Replace(buffer, "(\\s*)=(.*)", ""); key = Regex.Replace(key, "(\\s+)", ""); list.Add(key); string value = Regex.Replace(buffer, "(.*)=(\\s*)", ""); value = Regex.Replace(value, "(\\s*);(.*)$", ""); list.Add(value); } return (string[])list.ToArray(typeof(string)); } } }
もっと、効率的でスマートな方法があると思うので教えていただければうれしいです。
追記 2008-08-28 01:17:11
usingキーワードを使う形に修正して、finallyブロックを削除してみました。using便利ですね ![]()
TrackBack URL :
Comments(1)

CでGetPrivateProfile*を使わずにINIファイルを扱う
以前、『Slashcolon /: » C#でGetPrivateProfile*を使わずにINIファイルを扱う』というエントリで超適当なコードを晒したわけですが…今回は、そのC言語版です
要するに客先指定で、C#ではなくC/…