Is there a good C# design pattern for parsing strings that when split have different amounts of data?-Collection of common programming errors

  • If all the lines start with common data, identifier, and then are followed by a variable but expected (i.e. known based on the identifier) set of values, then a table approach could work well. To continue your example, say you have two different types:

    • common data,identifier,int,string,string,string.
    • common data,identifier,int,int,string,string,string.

    You can build a class that defines what you’re looking for:

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

    The Fields property is just a string that contains one-character type descriptions for the variable data. That is, “isss” would be interpreted as int,string,string,string.

    You can then build a Dictionary that you can use to look these up:

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

    Now when you read a line, use string.Split() to split it into fields. Get the identifier, look it up the dictionary to get the item descriptions, and then parse the rest of the fields. Something like:

    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.
    
  • Just split them using string.Split(), and then int.Parse() or int.TryParse() each int value in the resulting array as needed.

    var myStrings = string.Split(sourceString);
    int myint1 = int.Parse(myStrings[0]);
    
  • There are several ways of dealing with this. Here’s a simple one (outputting just an object array):

    class Template
    {
        // map identifiers to templates
        static Dictionary templates = new Dictionary
        {
            { "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;
        }
    }
    
  • would need little more details, based on your problem domain it could entirely change. but following seem to be the first set of patterns, they are ordered on suitability.

    • Interpreter
    • Strategy
    • Builder

Originally posted 2013-11-09 19:43:58.