Levy.Net

它山之石,可以为错; 它山之石,可以攻玉
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

用C#实现读写INI文件

Posted on 2008-04-08 16:35  LevyLiu  阅读(671)  评论(0)    收藏  举报
前段时间,老婆帮她同事问我如何在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, 255this._filePath);
    
return ret.ToString();
}

这用法和VB差不多,就是调用WritePrivateProfileStringGetPrivateProfileString这两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进行捕获.