Because I wanted the same behavior as OP (split a string exactly the same as windows cmd would do it) I wrote a bunch of test cases and tested the here posted answers:
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 cases Anton
Test(36, m, @"/src:""C:\tmp\Some Folder\Sub Folder"" /users:""[email protected]"" tasks:""SomeTask,Some Other Task"" -someParam foo", new[] { @"/src:C:\tmp\Some Folder\Sub Folder", @"/users:[email protected]", @"tasks:SomeTask,Some Other Task", @"-someParam", @"foo" });
// Test cases Daniel Earwicker
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 cases Fabio Iotti
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 cases Kevin Thach
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\"." });
the "expected" value comes from directly testing it with cmd.exe on my machine (Win10 x64) and a simple print program:
static void Main(string[] args) => Console.Out.WriteLine($"Count := {args.Length}\n{string.Join("\n", args.Select((v,i) => $"[{i}] => '{v}'"))}");
These are the results:
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) | -
Because no answer seemed correct (at least based on my use case) here is my solution, it currently passes all test cases (but if anyone has additional (failing) corner cases please comment):
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();
}
The code I used to generate the test results can be found here