如何读取和解析具有可配置内容的文本文件?

时间:2014-02-09 13:35:24

标签: c# regex file-io configuration-files

我在C#(WinForm)中编写了一个名为address_parser.exe的应用程序,针对运行Windows XP,Vista,7和8的PC。使用.NET Framework 3.5版是最小的设置...

应用程序读入并解析文本文件(仅限纯文本文件,因为我无法控制输入文件,因此不可能选择XML)。

这些文本文件包含一组数据,比如一个地址,分成多个非连续的行。

请查看以下两个文本文件作为演示:

address_type_1.txt:

Elm Grove
47

PO5 1JF


Southsea

address_type_2.txt:

Southsea

Albert Road



147b


PO4 0JW

现在,我现在已经在我的代码中硬编码了输入文件中的街道,门牌号码,邮政编码和城市所在的信息。因此,对于每个地址文件类型,如果已创建一组规则,则哪一行包含哪些信息。

另外,我有一套正则表达式,用于检查每个信息(街道,门牌号码,邮政编码,城市)的有效性。

由于这两组规则/检查(哪一行包含每个信息的信息/正则表达式模式)因每种不同的地址类型而异,我想将这些规则存储在一种配置文件中。因此,我想为每个地址类型配置一个配置文件,而不是硬编码,我的应用程序可以读取并配置自己如何解析特定的地址文件类型。

我想从你那里得到一些想法和灵感。请分享您的想法和最佳做法!

谢谢!

以下是我的一些想法,以及我目前使用的代码片段......

我目前的硬编码地址文件解析运行如下:

public static Address Parse(string fileName)
{
    var a = new Address();
    a.OriginalFile = fileName;
    int i = 0;
    using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.None))
    {
        using (var reader = new StreamReader(fs, Encoding.GetEncoding(65001)))
        {
            Regex rgxStreet = new Regex(@"^([\w\.,:\/\\\-öäüÖÄÜß_\s\(\)\[\]-[=;]]){0,128}$");
            Regex rgxNumber = new Regex(@"^([\w\.,:\/\\\-öäüÖÄÜß_\s\(\)\[\]-[=;]]){0,20}$");
            Regex rgxCity = new Regex(@"^([\w\.,:\/\\\-öäüÖÄÜß_\s\(\)\[\]-[=;]]){0,128}$");
            Regex rgxZIP = new Regex(@"^([0-9]){5}$");
            while (!reader.EndOfStream)
            {
                var line = reader.ReadLine().TrimEnd(';').Trim();
                if (line != null)
                {
                    if (i == 4 && rgxStreet.IsMatch(line))
                    {
                        a.Street = line;
                    }
                    else if (i == 7 && rgxNumber.IsMatch(line))
                    {
                        a.Number = line;
                    }
                    else if (i == 12 && (rgxZIP.IsMatch(line) || String.IsNullOrEmpty(line)))
                    {
                        a.Zip = line;
                    }
                    else if (i == 15 && rgxCity.IsMatch(line))
                    {
                        a.City = line;
                    }
                }
                i++;
            }
        }
    }
    return a;
}

正如您所看到的,我还在这4个属性上使用单独的正则表达式来检查我正在阅读的内容是否有效。

现在,我想修改这个硬编码信息(第X行包含带正则表达式Z的字段Y),这样我就可以支持读取和解析相同信息存储在不同文件中的文件订单,或使用不同的有效值。

上面的示例针对的是包含德国地址的文件(邮政编码为5位数字)。

在英国解析另一种包含地址的文本文件可能如下所示:

line 1: city;
line 2: zip;
line 20: street;
line 159: number;

在此示例中,信息的顺序以及邮政编码所需的注册号(英国的邮政编码长度为6位,包含字母和数字)。

而不是硬编码如何解析这种类型的文件的信息,我想像一个配置文件告诉我的应用程序如何解析特定类型的文件。像这样:

#config file for UK address files:
#line;field;regex;
1;city;@"^([\w\.,:\/\\\-öäüÖÄÜß_\s\(\)\[\]-[=;]]){0,128}$";
2;zip;@"^([A-Za-z0-9]){6}$";
20;street;@"^([\w\.,:\/\\\-öäüÖÄÜß_\s\(\)\[\]-[=;]]){0,128}$";
150;number;@"^([\w\.,:\/\\\-öäüÖÄÜß_\s\(\)\[\]-[=;]]){0,20}$";

我的问题是:这是一个好主意,还是有更好的方法来实现这一点(告诉我的应用程序需要如何读取和解析特定文件以及解释和验证其内容) ?

谢谢!

2 个答案:

答案 0 :(得分:3)

是的,这是一个好主意,请使用Newtonsoft.Json来帮助您完成配置加载,例如

private class StartSettings
{
    public string CityReg;
    public int CityNum;
    public string ZipReg;
    public int ZipNum;
    public string StreetReg;
    public int StreetNum;
    public string NumberReg;
    public int NumberNum;
}

var configString = File.ReadAllText(configFilePath);
var config = JsonConvert.DeserializeObject<StartSettings>(configString);

要阅读文件,请使用

Regex rgxStreet = new Regex(config.StreetReg);
Regex rgxNumber = new Regex(config.NumberReg);
Regex rgxCity = new Regex(config.CityReg);
Regex rgxZIP = new Regex(config.ZipReg");

foreach (var line = File.ReadLines(fileName, Encoding.GetEncoding(65001))
                        .Select(l => l.TrimEnd(';').Trim())
{
    if(config.CityNum == i && rgxCity.IsMatch(line))
        a.City = line;
    ...
    i++;
}
return a;

答案 1 :(得分:0)

由于我怀疑是否可以确定某个值是街道还是城市名称,因此您需要以数据的“格式”来指定至少一些关于输入数据的信息。

如果你仍然可以决定使用dataformat来获取XML。

像这样使用XML和XmlSerializer:

[Serializable]
public class AdressData
{
    [XmlArrayItem("Adress")]
    public Adress[] Adresses

}

[Serializable]
public class Adress
{
    public string Street {get; set;}
    public int Number {get; set;}
    public int Zip{get; set;}
    public string City{get; set;}
    public string State{get; set;}
}

然后像这样使用它:

XmlSerializer serializer = new XmlSerializer(typeof(AdressData));
AdressData data = (AdressData)serializer.Deserialize(File.Open(fileName));

foreach(Adress adress in data.Adresses)
{
    checkIfItExists(adress);
}

您的XMl应如下所示:

<AdressData>
  <Adresses>
    <Adress>
         <Street>WhateverStr</Street>
         <Number>7</Number>
         <Zip>5675765</Zip>
         <City>Citytown</City>
         <State>Alabama</State>
    </Adress>
      <Adress>
         <!-- Order doesnt matter here -->
         <Number>7</Number>
         <Zip>5675765</Zip>
         <City>Citytown</City>
         <State>Alabama</State>
         <Street>WhateverStr</Street>
    </Adress>
  </Adresses>
</AdressData>

XML中的数据顺序无关紧要,只要它适合于雇佣军。 序列化程序进行一些验证,例如尝试解析数值。您需要做的就是检查信息本身是否有效。

它能够解析Enums,所以你可以(不建议)创建一个包含所有US-Statenames的枚举......