-1

I want to take the following string as input:

first-arg second-arg "third arg with spaces" "arg with \" quotes"

and return this list of strings as output

["first-arg", "second-arg", "third arg with spaces", "arg with \" quotes"]

Are there any nuget packages or built in functions that can do this? I want it to handle edge cases like arguments containing multiple words and arguments containing quotes.

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
sighol
  • 2,678
  • 6
  • 28
  • 34
  • I have used a tool called Consolas to do something like this. It was a while ago, so I acannot remember exactly what it was capable of. https://www.nuget.org/packages/Consolas/ – Bertus van Zyl May 16 '17 at 11:59
  • 2
    Is that hard to do or what? The only exception I can see is sequence `\"`, you can e.g. replace it with something before splitting/finding/whatever by `"`. – Sinatr May 16 '17 at 11:59
  • A good CSV parser should handle that for you. – juharr May 16 '17 at 12:07
  • Since in your title you say "parse command line string" then you actually don't need to do anything. If line you provided in question is passed as command line arguments to your application - it will be parsed correctly in `Main(string[] arguments)` function, as array with 4 elements. – Evk May 16 '17 at 12:08

2 Answers2

0
string[] arguments = Environment.GetCommandLineArgs();

For more information see the MSDN website

Thinko
  • 404
  • 1
  • 5
  • 17
0

This class satisfies the requirements. It's not the most effective way, but it returns the right arguments.

public static class ArgumentLineParser
{
    public static string[] ToArguments(string cmd)
    {
        if (string.IsNullOrWhiteSpace(cmd))
        {
            return new string[0];
        }

        var argList = new List<string>();
        var parseStack = new Stack<char>();
        bool insideLiteral = false;

        for (int i = 0; i < cmd.Length; i++)
        {
            bool isLast = i + 1 >= cmd.Length;

            if (char.IsWhiteSpace(cmd[i]) && insideLiteral)
            {
                // Whitespace within literal is kept
                parseStack.Push(cmd[i]);
            }
            else if (char.IsWhiteSpace(cmd[i]))
            {
                // Whitespace delimits arguments
                MoveArgumentToList(parseStack, argList);
            }
            else if (!isLast && '\\'.Equals(cmd[i]) && '"'.Equals(cmd[i + 1]))
            {
                //Escaped double quote
                parseStack.Push(cmd[i + 1]);
                i++;
            }
            else if ('"'.Equals(cmd[i]) && !insideLiteral)
            {
                // Begin literal
                insideLiteral = true;
            }
            else if ('"'.Equals(cmd[i]) && insideLiteral)
            {
                // End literal
                insideLiteral = false;
            }
            else
            {
                parseStack.Push(cmd[i]);
            }
        }

        MoveArgumentToList(parseStack, argList);

        return argList.ToArray();
    }

    private static void MoveArgumentToList(Stack<char> stack, List<string> list)
    {
        var arg = string.Empty;
        while (stack.Count > 0)
        {
            arg = stack.Pop() + arg;
        }

        if (arg != string.Empty)
        {
            list.Add(arg);
        }
    }
}

It can be used like this:

var line = @"first-arg second-arg ""third arg with spaces"" ""arg with \"" quotes""";
var args = ArgumentLineParser.ToArguments(line);
sighol
  • 2,678
  • 6
  • 28
  • 34