1

I'm looking for an algorithm/library to parse this command line:

var cmd = "foo /option:value /option2:valueof /etc:.. baa /t:4 etc xd";

to:

foo => { "option", "value" },
       { "option2", "valueof" },
       {"etc", ".."},
baa => {"t", "4"},
etc => {"xd", ""},

I tried in 'pure mode'(C-like) just using if and for. But an solution with regular expression or linq is very appreciated.

my code(not working):

var cmd = "foo /option:value /option2:valueof /etc:.. baa /t:4 etc xd";
            var keys = new Dictionary<string, Dictionary<string,string>>();

            for (int pos = 0, len = cmd.Length; pos < len; pos++)
            {
                StringBuilder buffer = new StringBuilder();
                char c = cmd[pos];

                if (c == '/') 
                {
                    StringBuilder optionName = new StringBuilder();
                    StringBuilder optionValue = new StringBuilder();

                    do
                    {
                        c = cmd[pos++];

                        if (c == ':')
                        {
                            do
                            {
                                c = cmd[pos++];
                                optionValue.Append(c);

                            } while (c != '/' || c != ' ');
                        }
                        else
                        {
                            optionName.Append(c);
                        }

                    } while (c != ' ');

                    keys.Add(buffer.ToString(),
                        new Dictionary<string, string>
                        {
                            {optionName.ToString(), optionValue.ToString()}
                        });
                }
            }

it given an Index was outside the bounds of the array.

Thanks in advance.

user834697
  • 235
  • 6
  • 15
  • Can option values have spaces in them? If so, can you escape them by enclosing them in quotes? Can arguments occur without an option? Can an option occur without a value? And is there a reason that you need to try to parse three commands on one line? Answer to these questions will help us answer your question. – David Merriman Feb 10 '12 at 18:37

3 Answers3

2

There are certainly libraries to handle command-line parsing (SO recommends NDesk, but I haven't personally used it).

I'd use string.Split() over iterating character by character through the string.

            var tokenCmd = cmd.Split(' ');
            string currentKey = "";

            foreach (var token in tokenCmd)
            {
                if ((char.IsLetterOrDigit(token[0])) &&
                    (!keys.ContainsKey(currentKey)) ||
                    (keys[currentKey].Any()))
                {
                    currentKey = token;
                    keys.Add(currentKey,
                             new Dictionary<string, string>());
                }
                else
                {
                    var splitToken = new[] { token, "" };

                    if (token.Contains(':'))
                    {
                        splitToken = token
                            .Replace("/", "")
                            .Split(':');
                    }

                    keys[currentKey].Add(splitToken[0],
                                         splitToken[1]);
                }
            }
Community
  • 1
  • 1
48klocs
  • 6,073
  • 3
  • 27
  • 34
1

change:

} while (c != '/' || c != ' '); 

to:

} while (c != '/' && c != ' '); 
Brandon
  • 983
  • 6
  • 15
0

If you can retain keep the command lines separate (they're tarred together in your input var) and retain the string[] object type that the args came in on then I think this is the droid you're looking for:

static void Main(string[] args)
{
    Hashtable parsedArgs = new Hashtable();
    args.ToList().ForEach(x => {
        int dpos = x.IndexOf(":");
        if (dpos > -1)
            parsedArgs[x.Substring(1, dpos - 1)] = x.Substring(dpos + 1);
        else
            parsedArgs[x.Substring(1)] = true;
    });
}

Edit: Revised to Linq into a Hashtable as shown here. The Hashtable is much easier to work with than the more elegant-seeming (less code) KeyValuePair<> in my original answer.

xcud
  • 14,422
  • 3
  • 33
  • 29