Ponieważ chciałem zachować takie samo zachowanie jak OP (podzielić ciąg dokładnie tak samo, jak zrobiłoby to cmd systemu Windows), napisałem kilka przypadków testowych i przetestowałem tutaj opublikowane odpowiedzi:
Test( 0, m, "One", new[] { "One" });
Test( 1, m, "One ", new[] { "One" });
Test( 2, m, " One", new[] { "One" });
Test( 3, m, " One ", new[] { "One" });
Test( 4, m, "One Two", new[] { "One", "Two" });
Test( 5, m, "One Two", new[] { "One", "Two" });
Test( 6, m, "One Two", new[] { "One", "Two" });
Test( 7, m, "\"One Two\"", new[] { "One Two" });
Test( 8, m, "One \"Two Three\"", new[] { "One", "Two Three" });
Test( 9, m, "One \"Two Three\" Four", new[] { "One", "Two Three", "Four" });
Test(10, m, "One=\"Two Three\" Four", new[] { "One=Two Three", "Four" });
Test(11, m, "One\"Two Three\" Four", new[] { "OneTwo Three", "Four" });
Test(12, m, "One\"Two Three Four", new[] { "OneTwo Three Four" });
Test(13, m, "\"One Two\"", new[] { "One Two" });
Test(14, m, "One\" \"Two", new[] { "One Two" });
Test(15, m, "\"One\" \"Two\"", new[] { "One", "Two" });
Test(16, m, "One\\\" Two", new[] { "One\"", "Two" });
Test(17, m, "\\\"One\\\" Two", new[] { "\"One\"", "Two" });
Test(18, m, "One\"", new[] { "One" });
Test(19, m, "\"One", new[] { "One" });
Test(20, m, "One \"\"", new[] { "One", "" });
Test(21, m, "One \"", new[] { "One", "" });
Test(22, m, "1 A=\"B C\"=D 2", new[] { "1", "A=B C=D", "2" });
Test(23, m, "1 A=\"B \\\" C\"=D 2", new[] { "1", "A=B \" C=D", "2" });
Test(24, m, "1 \\A 2", new[] { "1", "\\A", "2" });
Test(25, m, "1 \\\" 2", new[] { "1", "\"", "2" });
Test(26, m, "1 \\\\\" 2", new[] { "1", "\\\"", "2" });
Test(27, m, "\"", new[] { "" });
Test(28, m, "\\\"", new[] { "\"" });
Test(29, m, "'A B'", new[] { "'A", "B'" });
Test(30, m, "^", new[] { "^" });
Test(31, m, "^A", new[] { "A" });
Test(32, m, "^^", new[] { "^" });
Test(33, m, "\\^^", new[] { "\\^" });
Test(34, m, "^\\\\", new[] { "\\\\" });
Test(35, m, "^\"A B\"", new[] { "A B" });
Test(36, m, @"/src:""C:\tmp\Some Folder\Sub Folder"" /users:""abcdefg@hijkl.com"" tasks:""SomeTask,Some Other Task"" -someParam foo", new[] { @"/src:C:\tmp\Some Folder\Sub Folder", @"/users:abcdefg@hijkl.com", @"tasks:SomeTask,Some Other Task", @"-someParam", @"foo" });
Test(37, m, "", new string[] { });
Test(38, m, "a", new[] { "a" });
Test(39, m, " abc ", new[] { "abc" });
Test(40, m, "a b ", new[] { "a", "b" });
Test(41, m, "a b \"c d\"", new[] { "a", "b", "c d" });
Test(42, m, "this is a test ", new[] { "this", "is", "a", "test" });
Test(43, m, "this \"is a\" test", new[] { "this", "is a", "test" });
Test(44, m, "\"C:\\Program Files\"", new[] { "C:\\Program Files" });
Test(45, m, "\"He whispered to her \\\"I love you\\\".\"", new[] { "He whispered to her \"I love you\"." });
„oczekiwana” wartość pochodzi z bezpośredniego przetestowania go za pomocą cmd.exe na moim komputerze (Win10 x64) i prostego programu do drukowania:
static void Main(string[] args) => Console.Out.WriteLine($"Count := {args.Length}\n{string.Join("\n", args.Select((v,i) => $"[{i}] => '{v}'"))}");
Oto wyniki:
Solution | Failed Tests
------------------------------|-------------------------------------
Atif Aziz (749653) | 2, 3, 10, 11, 12, 14, 16, 17, 18, 26, 28, 31, 32, 33, 34, 35, 36, 37, 39, 45
Jeffrey L Whitledge (298968) | 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 31, 32, 33, 34, 35, 36, 37, 39, 40, 41, 42, 43, 44, 45
Daniel Earwicker (298990) | 10, 11, 12, 14, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 31, 32, 33, 34, 35, 36, 45
Anton (299795) | 12, 16, 17, 18, 19, 21, 23, 25, 26, 27, 28, 31, 32, 33, 34, 35, 45
CS. (467313) | 12, 18, 19, 21, 27, 31, 32, 33, 34, 35
Vapour in the Alley (2132004) | 10, 11, 12, 14, 16, 17, 20, 21, 22, 23, 25, 26, 27, 28, 29, 31, 32, 33, 34, 35, 36, 45
Monoman (7774211) | 14, 16, 17, 20, 21, 22, 23, 25, 26, 27, 28, 31, 32, 33, 34, 35, 45
Thomas Petersson (19091999) | 2, 3, 10, 11, 12, 14, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 31, 32, 33, 34, 35, 36, 39, 45
Fabio Iotti (19725880) | 1, 2, 3, 7, 10, 11, 12, 13, 14, 15, 16, 17, 19, 21, 22, 23, 25, 26, 28, 29, 30, 35, 36, 37, 39, 40, 42, 44, 45
ygoe (23961658) | 26, 31, 32, 33, 34, 35
Kevin Thach (24829691) | 10, 11, 12, 14, 18, 19, 20, 21, 22, 23, 26, 27, 31, 32, 33, 34, 35, 36
Lucas De Jesus (31621370) | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45
HarryP (48008872) | 24, 26, 31, 32, 33, 34, 35
TylerY86 (53290784) | 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 31, 32, 33, 34, 35, 36, 41, 43, 44, 45
Louis Somers (55903304) | 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 29, 31, 32, 33, 34, 35, 36, 39, 41, 43, 44, 45
user2126375 (58233585) | 5, 6, 15, 16, 17, 31, 32, 33, 34, 35
DilipNannaware (59131568) | 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 31, 32, 33, 34, 35, 36, 37, 39, 40, 41, 42, 43, 44, 45
Mikescher (this) | -
Ponieważ żadna odpowiedź nie wydawała się poprawna (przynajmniej w oparciu o mój przypadek użycia), oto moje rozwiązanie, obecnie przechodzi wszystkie przypadki testowe (ale jeśli ktoś ma dodatkowe (nieudane) przypadki narożne, proszę o komentarz):
public static IEnumerable<string> SplitArgs(string commandLine)
{
var result = new StringBuilder();
var quoted = false;
var escaped = false;
var started = false;
var allowcaret = false;
for (int i = 0; i < commandLine.Length; i++)
{
var chr = commandLine[i];
if (chr == '^' && !quoted)
{
if (allowcaret)
{
result.Append(chr);
started = true;
escaped = false;
allowcaret = false;
}
else if (i + 1 < commandLine.Length && commandLine[i + 1] == '^')
{
allowcaret = true;
}
else if (i + 1 == commandLine.Length)
{
result.Append(chr);
started = true;
escaped = false;
}
}
else if (escaped)
{
result.Append(chr);
started = true;
escaped = false;
}
else if (chr == '"')
{
quoted = !quoted;
started = true;
}
else if (chr == '\\' && i + 1 < commandLine.Length && commandLine[i + 1] == '"')
{
escaped = true;
}
else if (chr == ' ' && !quoted)
{
if (started) yield return result.ToString();
result.Clear();
started = false;
}
else
{
result.Append(chr);
started = true;
}
}
if (started) yield return result.ToString();
}
Kod, którego użyłem do wygenerowania wyników testu, można znaleźć tutaj