前段时间,老婆帮她同事问我如何在C#中读取INI文件,我心想,现在还用INI文件啊.用了N年的C#,不记得.Net Framework有提供读写INI文件的类. 存在既是有理,也许是旧系统的文件吧,既然有这样的需术,就做一个吧.
记得N年前用VB的时候读取INI文件是用API做的,在C#中自然也可以调用API来实现:
[DllImport("kernel32")]
private static extern long WritePrivateProfileString(string section, string key, string val, string filePath);

[DllImport("kernel32")]
private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);

public void WriteValueAPI(string section, string key, string value)
{
long result = WritePrivateProfileString(section, key, value, this._filePath);
}

public string ReadValueAPI(string section, string key)
{
if (!File.Exists(this._filePath))
{
throw new FileNotFoundException();
}

StringBuilder ret = new StringBuilder(255);
int count = GetPrivateProfileString(section, key, "", ret, 255, this._filePath);
return ret.ToString();
}
这用法和VB差不多,就是调用WritePrivateProfileString和GetPrivateProfileString这两API.
其实INI文件就是一种文本文件,里面就是用[Section]/Key来存储信息,那么何不用正则表达式来实现呢? 于是,动手用正则表达式来实现INI文件的读写:
public void WriteValue(string section, string key, string value)
{
if (Path.GetExtension(this._filePath) != ".ini")
{
throw new Exception("The file name is invalid");
}
if (string.IsNullOrEmpty(section) || string.IsNullOrEmpty(key))
{
throw new ArgumentException();
}

FileStream fs = File.Open(this._filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
fs.Close();

string allText = File.ReadAllText(this._filePath);

bool isAppend = false, isAppendSection = false;
string pattern = "\\[" + section + "\\]";//查找有没有该Section
Match m1 = Regex.Match(allText, pattern,RegexOptions.IgnoreCase);
if (!m1.Success)
{
isAppend = isAppendSection = true;
}
else
{

pattern = "\\[" + section + "\\]" + @"[\s\w=\-]*(?<key>" + key + @"[ ]*=)[ ]*\w+?(?=\r\n)";//查找该Section下没有没该Key
Regex r = new Regex(pattern, RegexOptions.IgnoreCase);
if (r.IsMatch(allText))
{
this.replaceValue = value;
allText = r.Replace(allText, new MatchEvaluator(Replace));
}
else
{
pattern = "\\[" + section + "\\]" + @"[\s|\w|=]*?\[";//查找本Section后是否还有Section.
Match m2 = Regex.Match(allText, pattern, RegexOptions.IgnoreCase);
if (m2.Success)//如果有,则在下一个Section之前插入Key
{
int insertPosition = m2.Index + (m2.Length - 1);
string insertStr = key + "=" + value + "\r\n";
allText = allText.Insert(insertPosition, insertStr);
}
else//否则追加
{
isAppend = true;
}
}
}
if (isAppend)
{
StreamWriter sw = File.AppendText(this._filePath);
if (isAppendSection)
{
sw.WriteLine("[" + section + "]");
}
sw.WriteLine(key + "=" + value);
sw.Close();
}
else
{
StreamWriter sw = new StreamWriter(this._filePath);
sw.Write(allText);
sw.Close();
}
}

private string replaceValue = null;
private string Replace(Match m)
{
string text = m.Value;
int keyPosition = text.IndexOf(m.Groups["key"].Value);
text = text.Substring(0, keyPosition + m.Groups["key"].Value.Length) + replaceValue;
return text;
}

public string ReadValue(string section, string key)
{
if (!File.Exists(this._filePath))
{
throw new FileNotFoundException();
}
string allText = File.ReadAllText(this._filePath);

string pattern = "\\[" + section + "\\]" + @"[\s\w=\-]*" + key + @"[ ]*=[ ]*(?<value>\w+?)(?=\r\n)";

Regex r = new Regex(pattern,RegexOptions.IgnoreCase);
Match m = r.Match(allText);
if (m.Success)
{
return m.Groups["value"].Value;
}
return "";
}
在这里,ReadValue的代码比较少,但WriteValue的代码就比较多一点,是因为正则表达式并不是用来实现"写"的,但可以用它来辅助查询.
下面解释一下ReadValue中的正则表达式:
pattern = "\\[" + section + "\\]" + @"[\s\w=\-]*" + key + @"[ ]*=[ ]*(?<value>\w+?)(?=\r\n)";
1."\\[" + section + "\\]"这个不用多说了,是为了匹配[Section]的.
2.@"[\s\w=\-]*" + key这里是为了匹配Section和Key之间的所有字符(空白符,单词字符)以及Key的.
3.@"[ ]*=[ ]*(?<value>\w+?)(?=\r\n)",这里的[ ]*=[ ]*是匹配Key后面的=号两边可能出现的零个或多个空格;(?<value>\w+?)(?=\r\n)"这是一个捕获组,捕获就是Key对应的值,匹配\r\n,但不捕获.所以m.Groups["value"].Value就是所要查找的值.
在WriteValue中:pattern = "\\[" + section + "\\]" + @"[\s\w=\-]*(?<key>" + key + @"[ ]*=)[ ]*\w+?(?=\r\n)"和上面差不多,只是这里的对是Key进行捕获.
记得N年前用VB的时候读取INI文件是用API做的,在C#中自然也可以调用API来实现:
[DllImport("kernel32")]
private static extern long WritePrivateProfileString(string section, string key, string val, string filePath);
[DllImport("kernel32")]
private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);
public void WriteValueAPI(string section, string key, string value)
{
long result = WritePrivateProfileString(section, key, value, this._filePath);
}
public string ReadValueAPI(string section, string key)
{
if (!File.Exists(this._filePath))
{
throw new FileNotFoundException();
}
StringBuilder ret = new StringBuilder(255);
int count = GetPrivateProfileString(section, key, "", ret, 255, this._filePath);
return ret.ToString();
}
其实INI文件就是一种文本文件,里面就是用[Section]/Key来存储信息,那么何不用正则表达式来实现呢? 于是,动手用正则表达式来实现INI文件的读写:
public void WriteValue(string section, string key, string value)
{
if (Path.GetExtension(this._filePath) != ".ini")
{
throw new Exception("The file name is invalid");
}
if (string.IsNullOrEmpty(section) || string.IsNullOrEmpty(key))
{
throw new ArgumentException();
}
FileStream fs = File.Open(this._filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
fs.Close();
string allText = File.ReadAllText(this._filePath);
bool isAppend = false, isAppendSection = false;
string pattern = "\\[" + section + "\\]";//查找有没有该Section
Match m1 = Regex.Match(allText, pattern,RegexOptions.IgnoreCase);
if (!m1.Success)
{
isAppend = isAppendSection = true;
}
else
{
pattern = "\\[" + section + "\\]" + @"[\s\w=\-]*(?<key>" + key + @"[ ]*=)[ ]*\w+?(?=\r\n)";//查找该Section下没有没该Key
Regex r = new Regex(pattern, RegexOptions.IgnoreCase);
if (r.IsMatch(allText))
{
this.replaceValue = value;
allText = r.Replace(allText, new MatchEvaluator(Replace));
}
else
{
pattern = "\\[" + section + "\\]" + @"[\s|\w|=]*?\[";//查找本Section后是否还有Section.
Match m2 = Regex.Match(allText, pattern, RegexOptions.IgnoreCase);
if (m2.Success)//如果有,则在下一个Section之前插入Key
{
int insertPosition = m2.Index + (m2.Length - 1);
string insertStr = key + "=" + value + "\r\n";
allText = allText.Insert(insertPosition, insertStr);
}
else//否则追加
{
isAppend = true;
}
}
}
if (isAppend)
{
StreamWriter sw = File.AppendText(this._filePath);
if (isAppendSection)
{
sw.WriteLine("[" + section + "]");
}
sw.WriteLine(key + "=" + value);
sw.Close();
}
else
{
StreamWriter sw = new StreamWriter(this._filePath);
sw.Write(allText);
sw.Close();
}
}
private string replaceValue = null;
private string Replace(Match m)
{
string text = m.Value;
int keyPosition = text.IndexOf(m.Groups["key"].Value);
text = text.Substring(0, keyPosition + m.Groups["key"].Value.Length) + replaceValue;
return text;
}
public string ReadValue(string section, string key)
{
if (!File.Exists(this._filePath))
{
throw new FileNotFoundException();
}
string allText = File.ReadAllText(this._filePath);
string pattern = "\\[" + section + "\\]" + @"[\s\w=\-]*" + key + @"[ ]*=[ ]*(?<value>\w+?)(?=\r\n)";
Regex r = new Regex(pattern,RegexOptions.IgnoreCase);
Match m = r.Match(allText);
if (m.Success)
{
return m.Groups["value"].Value;
}
return "";
}下面解释一下ReadValue中的正则表达式:
pattern = "\\[" + section + "\\]" + @"[\s\w=\-]*" + key + @"[ ]*=[ ]*(?<value>\w+?)(?=\r\n)";
1."\\[" + section + "\\]"这个不用多说了,是为了匹配[Section]的.
2.@"[\s\w=\-]*" + key这里是为了匹配Section和Key之间的所有字符(空白符,单词字符)以及Key的.
3.@"[ ]*=[ ]*(?<value>\w+?)(?=\r\n)",这里的[ ]*=[ ]*是匹配Key后面的=号两边可能出现的零个或多个空格;(?<value>\w+?)(?=\r\n)"这是一个捕获组,捕获就是Key对应的值,匹配\r\n,但不捕获.所以m.Groups["value"].Value就是所要查找的值.
在WriteValue中:pattern = "\\[" + section + "\\]" + @"[\s\w=\-]*(?<key>" + key + @"[ ]*=)[ ]*\w+?(?=\r\n)"和上面差不多,只是这里的对是Key进行捕获.


浙公网安备 33010602011771号