Cela m'ennuie qu'il n'y ait pas de fonction pour diviser une chaîne en fonction d'une fonction qui examine chaque caractère. S'il y en avait, vous pourriez l'écrire comme ceci:
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));
}
Bien qu'ayant écrit cela, pourquoi ne pas écrire les méthodes d'extension nécessaires. Ok, tu m'as parlé de ça ...
Tout d'abord, ma propre version de Split qui prend une fonction qui doit décider si le caractère spécifié doit diviser la chaîne:
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);
}
Cela peut produire des chaînes vides selon la situation, mais peut-être que ces informations seront utiles dans d'autres cas, donc je ne supprime pas les entrées vides dans cette fonction.
Deuxièmement (et plus banalement) un petit assistant qui coupera une paire de guillemets correspondante du début et de la fin d'une chaîne. C'est plus difficile que la méthode Trim standard - elle ne coupera qu'un seul caractère à chaque extrémité, et elle ne coupera pas d'une seule extrémité:
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;
}
Et je suppose que vous voudrez aussi des tests. Eh bien, très bien alors. Mais ce doit être absolument la dernière chose! Tout d'abord, une fonction d'assistance qui compare le résultat de la division avec le contenu attendu du tableau:
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]);
}
Ensuite, je peux écrire des tests comme celui-ci:
Test("");
Test("a", "a");
Test(" abc ", "abc");
Test("a b ", "a", "b");
Test("a b \"c d\"", "a", "b", "c d");
Voici le test pour vos besoins:
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");
Notez que l'implémentation a la fonctionnalité supplémentaire qu'elle supprimera les guillemets autour d'un argument si cela a du sens (grâce à la fonction TrimMatchingQuotes). Je pense que cela fait partie de l'interprétation normale de la ligne de commande.