INI文件是Windows平臺(tái)上的一種較常用的軟件配置文件格式,Windows應(yīng)用程序常常使用它來(lái)保存一些配置信息。它一般是由數(shù)個(gè)包含key-value對(duì)的Section組成,每個(gè)key-value對(duì)保存著一些軟件配置信息。例如最典型的NT系列的啟動(dòng)配置文件boot.ini:
[boot loader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(2)\WINDOWS
[operating systems]
multi(0)disk(0)rdisk(0)partition(2)\WINDOWS="Microsoft Windows XP Professional" /fastdetect
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" /fastdetect
在這個(gè)文件中,方括號(hào)中的字符串是Section的名字,兩個(gè)方括號(hào)之間的內(nèi)容為一個(gè)Section。Section的內(nèi)容是一些key-value對(duì),每個(gè)key-value對(duì)占據(jù)一行,例如timeout=30就是一對(duì)key-value對(duì),timeout是key,對(duì)應(yīng)的value是30。Windows平臺(tái)專門(mén)提供了一組API可以方便地操作INI文件,例如GetPrivateProfileSection()、GetPrivateProfileInt()等。
隨著Windows系列操作系統(tǒng)的不斷發(fā)展,INI文件的作用逐漸被注冊(cè)表、XML格式的config文件等所取代,很少再用于系統(tǒng)配置,但我們?nèi)钥梢栽趹?yīng)用程序中使用它。在.NET平臺(tái)上推薦使用的軟件配置文件格式是基于XML的config文件,因此在.NET Framework中并沒(méi)有提供對(duì)INI文件讀寫(xiě)的特殊支持,使得我們有時(shí)在需要讀寫(xiě)INI文件時(shí)不是很方便。本文將探討如何使INI文件的讀寫(xiě)在.NET平臺(tái)上變得更加容易。當(dāng)然,我們可以直接引入上述的API,但本文將不使用API,而是完全基于.NET Framework。
創(chuàng)建INI文件讀寫(xiě)類
要在.NET平臺(tái)上處理INI文件,很自然的想法就是創(chuàng)建一個(gè)專門(mén)的class來(lái)負(fù)責(zé)INI文件的讀寫(xiě)工作,這個(gè)class暴露適當(dāng)?shù)慕涌诠┩獠空{(diào)用。一般的INI文件的尺寸很小,因此最簡(jiǎn)單的做法就是以文本的方式將整個(gè)文件讀入一個(gè)string變量中。類定義如下:
public class FileIni
{
private string fileContents = null;
public FileIni(string fileName)
{
if(File.Exists(fileName))
{
StreamReader r = File.OpenText(fileName);
fileContents = r.ReadToEnd();
r.Close();
}
}
}
接下來(lái)我們要提供一些方法來(lái)操作這個(gè)字符串,比如從中返回所有的Section Name、取得特定的key所對(duì)應(yīng)的value等。我們可以使用字符串查找之類的方法來(lái)完成這些工作,但是.NET Framework為我們提供了更好的方法,那就是正則表達(dá)式。
正則表達(dá)式
所謂正則表達(dá)式是一種被設(shè)計(jì)用來(lái)優(yōu)化字符串操作的語(yǔ)言。它使用一組元字符(Metacharacters)來(lái)實(shí)現(xiàn)強(qiáng)勁的字符串操作能力。這組元字符最早來(lái)自于對(duì)DOS文件系統(tǒng)中?和*的擴(kuò)展。在DOS文件系統(tǒng)中,?和*分別被用來(lái)代替單個(gè)字符和字符群組,它們可以被認(rèn)為是最早的元字符。正則表達(dá)式在它們的基礎(chǔ)上不斷擴(kuò)充,形成了一套元字符集,能夠表達(dá)非常復(fù)雜的字符串。
舉例來(lái)說(shuō),網(wǎng)上注冊(cè)時(shí)常常需要用戶輸入一個(gè)有效的Email地址。當(dāng)用戶輸入一個(gè)字符串后,我們?nèi)绾悟?yàn)證這個(gè)Email地址是否合法呢?使用下面這個(gè)正則表達(dá)式可以輕易地實(shí)現(xiàn)目的:
@"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$"
關(guān)于這個(gè)正則比表達(dá)式的含義,在此不做過(guò)多解釋,有興趣的朋友可以參考相關(guān)的正則表達(dá)式資料。這個(gè)正則表達(dá)式雖不能保證用戶輸入的Email地址100%的真實(shí)有效,但至少可以保證用戶輸入的Email地址看上去是合法有效的。
.NET Framework中提供了一些使用正則表達(dá)式的類,這些類位于System.Text.RegularExpressions名字空間下。
使用正則表達(dá)式實(shí)現(xiàn)FileIni類的功能
現(xiàn)在我們可以使用正則表達(dá)式來(lái)實(shí)現(xiàn)FileIni類的相應(yīng)功能了。為了返回INI文件中所有Section的名字,我們可以使用一個(gè)只讀屬性SectionNames來(lái)返回一個(gè)Section Name的字符串?dāng)?shù)組。
public string[] SectionNames
{
get
{
// Using regular expression to get all section names.
string regexPattern = @"\[(?<SectionName>\w*)\]";
Regex r = new Regex(regexPattern); // Match "[anywords]"
MatchCollection matches = r.Matches(fileContents);
// Writing all section names to a string array.
string[] results = new string[matches.Count];
for(int i = 0; i < matches.Count; i++)
{
results[i] = matches[i].Result("${SectionName}");
}
return results;
}
}
在上面的代碼中,我們使用一個(gè)正則表達(dá)式:@"\[(?<SectionName>\w*)\]",對(duì)源字符串進(jìn)行一次匹配就取出了所有的Section Name。
為了取得特定Section下的特定的key的value,我們先要取得此Section下的所有內(nèi)容,然后再?gòu)闹腥〕鎏囟?/span>key的value。
public string GetSectionString(string sectionName)
{
string regexPattern = @"(\[" + sectionName + @"\]"
+ @"(?<SectionString>.*)\[)";
Regex r = new Regex(regexPattern, RegexOptions.Singleline);
if(r.IsMatch(fileContents))
{
return r.Match(fileContents).Result("${SectionString}");
}
return string.Empty;
}
GetSectionString()根據(jù)特定的sectionName取得此Section的全部?jī)?nèi)容。假設(shè)sectionName為字符串boot loader,此時(shí)的正則表達(dá)式為@”(\[boot loader\](?<SetionString>.*)\[]”。得到Section下的所有內(nèi)容后,我們?cè)購(gòu)钠渲械玫轿覀兿胍?/span>value值。
public string GetKeyString(string sectionName, string keyName)
{
string sectionString = this.GetSectionString(sectionName);
string regexPattern = @"(" + keyName + @"=(?<value>.*)\r\n)";
Regex r = new Regex(regexPattern);
if(r.IsMatch(fileContents))
{
return r.Match(fileContents).Result("${value}");
}
return string.Empty;
在此基礎(chǔ)上,可以得到更多的諸如GetKeyInt()之類的方法。至于寫(xiě)方法,利用Regex的Replace()方法也是很容易實(shí)現(xiàn)的,在此就不做過(guò)多的敘述了。
總結(jié)
本文著重演示了正則表達(dá)式在讀寫(xiě)INI文件時(shí)的應(yīng)用。所實(shí)現(xiàn)的INI文件讀寫(xiě)類FileIni擴(kuò)展性稍顯不足,例如,這個(gè)類只能處理通用格式的INI文件,對(duì)于格式稍有變化的INI文件,此類中的正則表達(dá)式就需要修改了??傊?,正則表達(dá)式是處理字符串的強(qiáng)大工具,掌握了它
聯(lián)系客服