是否有一个很好的C#设计模式来解析分裂时具有不同数据量的字符串?

时间:2011-03-12 06:06:19

标签: c# parsing types design-patterns

我正在处理由作为字符串发送给我的逗号分隔的值。字符串有许多不同的结构(意味着字符串的不同位置中的不同数据类型以及不同数量的数据)。因此,虽然一个字符串可能表示为:

- 公共数据,标识符,整数,字符串,字符串,字符串。

另一个可能表示为:

- 公共数据,标识符,整数,整数,字符串,字符串,字符串。

设计目标:

  • 通用解析方法
  • 常见验证(即int.TryParse()返回true)
  • 随时可以添加不同的结构

是否有良好的设计模式或设计模式的组合,只有在拉入适量的值并且这些值是预期的数据类型时,才允许我解析值,检查它们并返回对象?

注意:我正在处理超过30种不同的字符串结构。

5 个答案:

答案 0 :(得分:1)

只需使用string.Split()分割它们,然后根据需要在结果数组中的每个int int.Parse()int.TryParse()分割它们。

var myStrings = string.Split(sourceString);
int myint1 = int.Parse(myStrings[0]);

答案 1 :(得分:1)

有几种方法可以解决这个问题。这是一个简单的(只输出一个对象数组):

class Template
{
    // map identifiers to templates
    static Dictionary<string, string> templates = new Dictionary<string, string>
    {
        { "type1", "isss" },
        { "type2", "iisss" },
    };

    static bool ParseItem(string input, char type, out object output)
    {
        output = null;
        switch (type)
        {
            case 'i':
                int i;
                bool valid = int.TryParse(input, out i);
                output = i;
                return valid;
            case 's':
                output = input;
                return true;
        }
        return false;
    }

    public static object[] ParseString(string input)
    {
        string[] items = input.Split(',');
        // make sure we have enough items
        if (items.Length < 2)
            return null;
        object[] output = new object[items.Length - 2];
        string identifier = items[1];
        string template;
        // make sure a valid identifier was specified
        if (!templates.TryGetValue(identifier, out template))
            return null;
        // make sure we have the right amount of data
        if (template.Length != output.Length)
            return null;
        // parse each item
        for (int i = 0; i < template.Length; i++)
            if (!ParseItem(items[i + 2], template[i], out output[i]))
                return null;
        return output;
    }
}

答案 2 :(得分:1)

如果所有行都以common data, identifier开头,然后是一个变量但预期(即基于标识符已知)的值集合,那么表格方法可以很好地工作。要继续您的示例,请说您有两种不同的类型:

  • 公共数据,标识符,整数,字符串,字符串,字符串。
  • 公共数据,标识符,int,int,字符串,字符串,字符串。

您可以构建一个定义您要查找内容的类:

class ItemDesc
{
    public string Ident { get; private set; }
    public string Fields { get; private set; }
    public ItemDesc(string id, string flds)
    {
        Ident = id;
        Fields = flds;
    }
}

Fields属性只是一个字符串,其中包含可变数据的单字符类型描述。也就是说,“isss”将被解释为int,string,string,string

然后,您可以构建一个Dictionary<string, ItemDesc>,您可以使用它来查看这些内容:

Dictionary<string, ItemDesc> ItemLookup = new Dictionary<string, ItemDesc>
{
    { "ItemType1", new ItemDesc("ItemType1", "isss") },
    { "ItemType2", new ItemDesc("ItemType2", "iisss") },
};

现在,当您阅读一行时,请使用string.Split()将其拆分为字段。获取标识符,查找字典以获取项目描述,然后解析其余字段。类似的东西:

string line = GetLine();
var fields = line.Split(',');
// somehow get the identifier
string id = GetIdentifier();
ItemDesc desc;
if (!ItemLookup.TryGetValue(id, out desc))
{
    // unrecognized identifier
}
else
{
    int fieldNo = 3; // or whatever field is after the identifier
    foreach (var c in desc.Fields)
    {
        switch (c)
        {
            case 'i' :
               // try to parse an int and save it.
               break;
            case 's' :
               // save the string
               break;
            default:
               // error, unknown field type
               break;
         }
         ++fieldNo;
    }
}
// at this point if no errors occurred, then you have a collection
// of parsed fields that you saved.  You can now create your object.

答案 3 :(得分:0)

需要更多细节,根据您的问题域,它可能会完全改变。但是以下似乎是第一组模式,它们是按照适用性排序的。

答案 4 :(得分:0)

如果您对返回实际对象而不仅仅是对象数组感兴趣,可以将元数据放入要返回的对象的类定义中。然后,当您获得对象类型时,您将查找元数据以确定在输入数组中找到其值的位置。这是一个简单的例子:

namespace Parser
{
    // create metadata attribute
    class CsvPositionAttribute : Attribute
    {
        public int Position { get; set; }
        public CsvPositionAttribute(int position)
        {
            Position = position;
        }
    }

    // define some classes that use our metadata
    public class type1
    {
        [CsvPosition(0)]
        public int int1;
        [CsvPosition(1)]
        public string str1;
        [CsvPosition(2)]
        public string str2;
        [CsvPosition(3)]
        public string str3;
    }

    public class type2
    {
        [CsvPosition(0)]
        public int int1;
        [CsvPosition(1)]
        public int int2;
        [CsvPosition(2)]
        public string str1;
        [CsvPosition(3)]
        public string str2;
        [CsvPosition(4)]
        public string str3;
    }

    public class CsvParser
    {
        public static object ParseString(string input)
        {
            string[] items = input.Split(',');
            // make sure we have enough items
            if (items.Length < 2)
                return null;
            string identifier = items[1];
            // assume that our identifiers refer to a type in our namespace
            Type type = Type.GetType("Parser." + identifier, false);
            if (type == null)
                return null;
            object output = Activator.CreateInstance(type);
            // iterate over fields in the type -- you may want to use properties
            foreach (var field in type.GetFields())
                // find the members that have our position attribute
                foreach (CsvPositionAttribute attr in
                    field.GetCustomAttributes(typeof(CsvPositionAttribute),
                                              false))
                    // if the item exists, convert it to the type of the field
                    if (attr.Position + 2 >= items.Length)
                        return null;
                    else
                        // ChangeType may throw exceptions on failure;
                        // catch them and return an error
                        try { field.SetValue(output,
                            Convert.ChangeType(items[attr.Position + 2],
                                               field.FieldType));
                        } catch { return null; }
            return output;
        }
    }
}