试着写一个愉快的解析器

时间:2010-02-01 15:34:00

标签: c# .net string parsing

我正在编写一个简单的解析器,它将采用格式20100101,1,2,foo的字符串并创建以下类的实例:

public class Foo
{
    public DateTime TheDate { get; set; }
    public int TheFirstInt { get; set; }
    public int TheSecondInt { get; set; }
    public string TheString { get; set; }
}

我希望能够将每个属性的解析器声明为(例如)Func<>s的数组,以试图使代码更具可读性(从关联字符串中的项目的角度来看)使用的解析代码。)

// Production code would contain parsers with error checking etc.
Func<string, object>[] parsers = new Func<string, object>[]
{
    s => DateTime.ParseExact(s, "yyyyMMdd", CultureInfo.InvariantCulture),
    s => int.Parse(s),
    s => int.Parse(s),
    s => s
};

然后,我希望能够在一个循环中遍历解析器,FooClass的属性和fooItems中的值:

Foo fooInstance = new Foo();
string[] fooItems = fooString.Split(',');

for (int i = 0; i < parsers.Length; i++)
{
    fooInstance.Properties[i] = parsers[i](fooItems[i]);
    // range-checking and error handling excluded from this example
}

然而,这当然不会起作用,因为:

  • 它没有解决您如何能够遍历fooInstance
  • 的属性
  • 它不处理已解析的值的转换

关于如何编写像这样的“愉快”解析器?

3 个答案:

答案 0 :(得分:2)

我尝试使用Action代替Func s并直接设置属性:

Action<string, FooClass>[] actions = new Action<string, FooClass>[] {
    (s, c) => c.TheDate = DateTime.ParseExact(s, "yyyyMMdd", CultureInfo.InvariantCulture),
    (s, c) => c.TheFirstInt = Int32.Parse(s)
    // ...
}

for (int i = 0; i < fooItems.Length; ++i)
    actions[i](fooItems[i], fooInstance);

答案 1 :(得分:2)

我知道这不是直接回答你的问题,但如果你发现你的“语言”变得复杂得多,那么我建议使用反讽解析它:http://www.codeplex.com/irony

如果您的语言将保持平面格式(如CSV),则值得查看http://www.filehelpers.com/

在您的示例中,您只需要为您的班级添加注释:

[DelimitedRecord(",")]
public class Foo
{
    [FieldConverter(ConverterKind.Date, "yyyyMMdd")]
    public DateTime TheDate { get; set; }
    public int TheFirstInt { get; set; }
    public int TheSecondInt { get; set; }
    public string TheString { get; set; }
}

然后用:

解析它
FileHelperEngine engine = new FileHelperEngine(typeof(Foo));
Foo[] fs = engine.ReadFile("FileIn.txt") as Foo[];

答案 2 :(得分:0)

您需要使用反射:例如:

fooInstance.GetType().GetProperty("SomeProp").SetValue(fooInstance, "SomeProp", val);