尝试编写一个令人愉悦的解析器

3

我正在编写一个简单的解析器,它将接收一个格式为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属性的问题
  • 它没有处理解析值的强制转换

你有什么关于如何编写类似这样的“友好”解析器的想法吗?


1
当我看到这个问题时,我首先想到的是:“对不起,您提供的输入包含一个分号,这让我感到有些不习惯。” - Sam Harwell
3个回答

2

我建议使用 Action 而不是 Func,并直接设置属性:

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);

2
我知道这并没有直接回答你的问题,但如果你发现你的“语言”变得更加复杂,我建议使用Irony来解析它: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[];

@Rob:对于我的情况,我很满意MartinStettner建议的方法,但是将来如果有更复杂的情况,我会记住FileHelpers。 - Richard Ev

0

你需要使用反射:例如:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接