Меня раздражает, что нет функции для разделения строки на основе функции, которая проверяет каждый символ. Если бы было, вы могли бы написать это так:
public static IEnumerable<string> SplitCommandLine(string commandLine)
{
bool inQuotes = false;
return commandLine.Split(c =>
{
if (c == '\"')
inQuotes = !inQuotes;
return !inQuotes && c == ' ';
})
.Select(arg => arg.Trim().TrimMatchingQuotes('\"'))
.Where(arg => !string.IsNullOrEmpty(arg));
}
Хотя, написав это, почему бы не написать необходимые методы расширения. Ладно, ты уговорил меня ...
Во-первых, моя собственная версия Split, которая использует функцию, которая должна решить, должен ли указанный символ разбивать строку:
public static IEnumerable<string> Split(this string str,
Func<char, bool> controller)
{
int nextPiece = 0;
for (int c = 0; c < str.Length; c++)
{
if (controller(str[c]))
{
yield return str.Substring(nextPiece, c - nextPiece);
nextPiece = c + 1;
}
}
yield return str.Substring(nextPiece);
}
Это может привести к появлению некоторых пустых строк в зависимости от ситуации, но, возможно, эта информация будет полезна в других случаях, поэтому я не удаляю пустые записи в этой функции.
Во-вторых (и более обыденно) маленький помощник, который обрежет совпадающую пару кавычек из начала и конца строки. Это более суетливый, чем стандартный метод Trim - он будет обрезать только один символ с каждого конца, и он не будет обрезать только с одного конца:
public static string TrimMatchingQuotes(this string input, char quote)
{
if ((input.Length >= 2) &&
(input[0] == quote) && (input[input.Length - 1] == quote))
return input.Substring(1, input.Length - 2);
return input;
}
И я полагаю, вам также понадобятся некоторые тесты. Ну хорошо тогда. Но это должно быть абсолютно последним! Сначала вспомогательная функция, которая сравнивает результат разделения с ожидаемым содержимым массива:
public static void Test(string cmdLine, params string[] args)
{
string[] split = SplitCommandLine(cmdLine).ToArray();
Debug.Assert(split.Length == args.Length);
for (int n = 0; n < split.Length; n++)
Debug.Assert(split[n] == args[n]);
}
Тогда я могу написать такие тесты:
Test("");
Test("a", "a");
Test(" abc ", "abc");
Test("a b ", "a", "b");
Test("a b \"c d\"", "a", "b", "c d");
Вот тест для ваших требований:
Test(@"/src:""C:\tmp\Some Folder\Sub Folder"" /users:""abcdefg@hijkl.com"" tasks:""SomeTask,Some Other Task"" -someParam",
@"/src:""C:\tmp\Some Folder\Sub Folder""", @"/users:""abcdefg@hijkl.com""", @"tasks:""SomeTask,Some Other Task""", @"-someParam");
Обратите внимание, что в реализации есть дополнительная функция, заключающаяся в удалении кавычек вокруг аргумента, если это имеет смысл (благодаря функции TrimMatchingQuotes). Я считаю, что это часть обычной интерпретации командной строки.