HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

LINQish command line parser

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
parsercommandlinelinqish

Problem

I needed to parse some command line switches from a string and I tried to do it the easy way so I just wrote this:

static class CommandLineParser
{
    public static IEnumerable ParseCommandLine(this string text)
    {
        if (Regex.Matches(text, "\"").Count % 2 != 0) 
        {
            throw new ArgumentException("Invalid number of qotes.");
        }
        return text.Aggregate(new
        {
            lastChar = '\0',
            isEscaped = false,
            result = new List { new StringBuilder() }
        }, (state, next) =>
        {
            var isQuote = next == '"';
            var isEscapedQuote = state.lastChar == '\\' && isQuote;

            if (next == ' ' && !state.isEscaped)
            {
                // Ignore multiple spaces between switches.
                if (state.lastChar != ' ')
                {
                    state.result.Add(new StringBuilder());
                }
            }
            else
            {
                state.result.Last().Append(next);
            }

            return new
            {
                lastChar = next,
                isEscaped = isQuote && !isEscapedQuote ? !state.isEscaped : state.isEscaped,
                result = state.result
            };
        }).result.Select(x => x.ToString());
    }
}


Example:

var text = "foo -bar -baz=\"abc \\\"def\\\"   ghi\"    -qux";
text.ParseCommandLine().Dump();


and the result:

foo 
-bar 
-baz="abc \"def\" ghi" 
-qux


What do you think? Is this enough? Can this be even shorter?

Solution

Validate your text argument. This causes an ArgumentNullException in Regex.Matches:

((string)null).ParseCommandLine();


It would be better to check the argument before passing it on:

if (text == null) 
{
    throw new ArgumentNullException(nameof(text), "Command line arguments cannot be null");
}


Why is this better? The stack trace is more meaningful because the error is thrown closer to where the problem call is and you can supply your own context specific error message. Either that, or you can return an empty array for null.

Properties should be PascalCase - including anonymous types. lastChar should be LastChar and the same for the rest.

result should be plural as it is a collection: Results.

Use default() to get the default value, it signals intent better than a literal ('\0' == default(char)).

Name your constants! E.g. the double quote and the space. A single space is the most difficult thing to read in source code (in my experience).

I think this is good code (all the above is pretty minor). I'd never thought of using Aggregate over a string for simple parsing before :)

Code Snippets

((string)null).ParseCommandLine();
if (text == null) 
{
    throw new ArgumentNullException(nameof(text), "Command line arguments cannot be null");
}

Context

StackExchange Code Review Q#140552, answer score: 4

Revisions (0)

No revisions yet.