Séparer la chaîne contenant les paramètres de la ligne de commande en chaîne [] en C#

j'ai une chaîne simple qui contient les paramètres de ligne de commande à passer à un autre exécutable et je dois extraire la chaîne[] contenant les paramètres individuels de la même manière que C# le ferait si les commandes avaient été spécifiées sur la ligne de commande. La chaîne[] sera utilisée lors de l'exécution d'un autre point d'entrée des assemblages par réflexion.

y a-t-il une fonction standard pour cela? Ou est-il une méthode préférée (regex?) pour séparer les paramètres correctement? Il doit gérer les chaînes délimitées '"'qui peuvent contenir des espaces correctement, donc je ne peux pas me diviser sur' '.

exemple de chaîne de caractères:

string parameterString = @"/src:""C:tmpSome FolderSub Folder"" /users:""abcdefg@hijkl.com"" tasks:""SomeTask,Some Other Task"" -someParam foo";

exemple de résultat:

string[] parameterArray = new string[] { 
  @"/src:C:tmpSome FolderSub Folder",
  @"/users:abcdefg@hijkl.com",
  @"tasks:SomeTask,Some Other Task",
  @"-someParam",
  @"foo"
};

Je n'ai pas besoin d'une bibliothèque de parsing en ligne de commande, juste un moyen d'obtenir la chaîne[] qui devrait être générée.

mise à jour : j'ai dû changer le résultat attendu pour correspondre à ce qui est réellement généré par C# (a enlevé le "'s de la scission de chaînes de caractères)

75
demandé sur Anton 2008-11-18 17:10:30

19 réponses

en plus de la good and pure managed solution par Earwicker , il peut être intéressant de mentionner, par souci d'exhaustivité, que Windows fournit également la CommandLineToArgvW fonction pour briser une corde en un ensemble de cordes:

LPWSTR *CommandLineToArgvW(
    LPCWSTR lpCmdLine, int *pNumArgs);

Analyse Unicode chaîne de ligne de commande et retourne un tableau de pointeurs les arguments en ligne de commande, ainsi que un compte de tels arguments, d'une certaine manière qui est similaire à la norme C valeurs argv et argc de l'exécution.

un exemple d'appel de cette API à partir de C# et de décompression du tableau de chaînes résultant dans le code géré peut être trouvé à, " conversion chaîne de ligne de commande en Args[] en utilisant CommandLineToArgvW() API ."Ci-dessous une version légèrement plus simple du même code:

[DllImport("shell32.dll", SetLastError = true)]
static extern IntPtr CommandLineToArgvW(
    [MarshalAs(UnmanagedType.LPWStr)] string lpCmdLine, out int pNumArgs);

public static string[] CommandLineToArgs(string commandLine)
{
    int argc;
    var argv = CommandLineToArgvW(commandLine, out argc);        
    if (argv == IntPtr.Zero)
        throw new System.ComponentModel.Win32Exception();
    try
    {
        var args = new string[argc];
        for (var i = 0; i < args.Length; i++)
        {
            var p = Marshal.ReadIntPtr(argv, i * IntPtr.Size);
            args[i] = Marshal.PtrToStringUni(p);
        }

        return args;
    }
    finally
    {
        Marshal.FreeHGlobal(argv);
    }
}
60
répondu Atif Aziz 2018-01-18 23:13:07

cela m'ennuie qu'il n'y ait pas de fonction pour séparer une chaîne basée sur 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. Bon, vous avez parlé de moi...

tout D'abord, ma propre version de Split qui prend une fonction qui doit décider si le caractère spécifié doit séparer 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);
    }

il peut y avoir des chaînes vides selon la situation, mais peut-être que cette information sera utile dans d'autres cas, donc je ne supprime pas les entrées vides dans cette fonction.

Deuxièmement (et plus généralement) un petit helper qui va couper une paire de citations assorties du début et de la fin d'une chaîne. C'est plus compliqué que la méthode de finition standard - il ne coupera qu'un caractère de chaque extrémité, et il 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 voulez aussi des tests. Bien, très bien. Mais ce doit être absolument la dernière chose! D'abord une fonction d'assistance qui compare le résultat de la scission avec le contenu 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]);
    }

alors 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 exigences:

        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 caractéristique supplémentaire de supprimer les guillemets autour d'un argument si cela a du sens (grâce à la fonction TrimMatchingQuotes). Je crois que cela fait partie de l'interprétation normale en ligne de commande.

88
répondu Daniel Earwicker 2008-11-18 15:06:58

L'analyseur de ligne de commande de Windows se comporte comme vous le dites, divisé sur l'espace à moins qu'il n'y ait une citation non fermée avant lui. Je recommande d'écrire l'analyseur vous-même. Quelque chose comme ceci peut-être:

    static string[] ParseArguments(string commandLine)
    {
        char[] parmChars = commandLine.ToCharArray();
        bool inQuote = false;
        for (int index = 0; index < parmChars.Length; index++)
        {
            if (parmChars[index] == '"')
                inQuote = !inQuote;
            if (!inQuote && parmChars[index] == ' ')
                parmChars[index] = '\n';
        }
        return (new string(parmChars)).Split('\n');
    }
21
répondu Jeffrey L Whitledge 2008-11-18 15:00:58

j'ai pris la réponse de Jeffrey L Whitledge et il amélioré un peu.

il supporte maintenant les guillemets simples et doubles. Vous pouvez utiliser des guillemets dans les paramètres eux-mêmes en utilisant d'autres guillemets dactylographiés.

il supprime également les citations des arguments car ceux-ci ne contribuent pas à l'information de l'argument.

    public static string[] SplitArguments(string commandLine)
    {
        var parmChars = commandLine.ToCharArray();
        var inSingleQuote = false;
        var inDoubleQuote = false;
        for (var index = 0; index < parmChars.Length; index++)
        {
            if (parmChars[index] == '"' && !inSingleQuote)
            {
                inDoubleQuote = !inDoubleQuote;
                parmChars[index] = '\n';
            }
            if (parmChars[index] == '\'' && !inDoubleQuote)
            {
                inSingleQuote = !inSingleQuote;
                parmChars[index] = '\n';
            }
            if (!inSingleQuote && !inDoubleQuote && parmChars[index] == ' ')
                parmChars[index] = '\n';
        }
        return (new string(parmChars)).Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
    }
10
répondu Vapour in the Alley 2018-09-16 19:30:17

Le bonne et pure solution gérée par Earwicker n'a pas pu gérer des arguments comme:

Test("\"He whispered to her \\"I love you\\".\"", "He whispered to her \"I love you\".");

Il a retourné 3 éléments:

"He whispered to her \"I
love
you\"."

donc voici un correctif pour soutenir le "cité \" escape\" citation":

public static IEnumerable<string> SplitCommandLine(string commandLine)
{
    bool inQuotes = false;
    bool isEscaping = false;

    return commandLine.Split(c => {
        if (c == '\' && !isEscaping) { isEscaping = true; return false; }

        if (c == '\"' && !isEscaping)
            inQuotes = !inQuotes;

        isEscaping = false;

        return !inQuotes && Char.IsWhiteSpace(c)/*c == ' '*/;
        })
        .Select(arg => arg.Trim().TrimMatchingQuotes('\"').Replace("\\"", "\""))
        .Where(arg => !string.IsNullOrEmpty(arg));
}

Testé avec 2 cas supplémentaires:

Test("\"C:\Program Files\"", "C:\Program Files");
Test("\"He whispered to her \\"I love you\\".\"", "He whispered to her \"I love you\".");

a également noté que le réponse acceptée par Atif Aziz qui utilise CommandLineToArgvW aussi échoué. Il a retourné 4 éléments:

He whispered to her \ 
I 
love 
you". 

Espérons que cela aide quelqu'un à la recherche d'une telle solution dans le futur.

5
répondu Kevin Thach 2017-05-23 11:55:13
4
répondu Mark Cidade 2008-11-18 15:08:56

j'aime les itérateurs, et de nos jours LINQ rend IEnumerable < String> aussi facilement utilisable que des tableaux de chaîne, donc mon point de vue suivant l'esprit de la réponse de Jeffrey l Whitledge est (comme une méthode d'extension de chaîne):

    public static IEnumerable<string> ParseArguments(this string commandLine)
    {
        if (string.IsNullOrWhiteSpace(commandLine))
            yield break;
        var sb = new StringBuilder();
        bool inQuote = false;
        foreach (char c in commandLine) {
            if (c == '"' && !inQuote) {
                inQuote = true;
                continue;
            }
            if (c != '"' && !(char.IsWhiteSpace(c) && !inQuote)) {
                sb.Append(c);
                continue;
            }
            if (sb.Length > 0) {
                var result = sb.ToString();
                sb.Clear();
                inQuote = false;
                yield return result;
            }
        }
        if (sb.Length > 0)
            yield return sb.ToString();
    }
3
répondu Monoman 2018-09-16 19:33:26

Ce Le Projet de Code de l'article est ce que j'ai utilisé dans le passé. C'est un bon code, mais ça pourrait marcher.

ce article MSDN est la seule chose que j'ai pu trouver qui explique comment C# analyse les arguments en ligne de commande.

2
répondu Zachary Yates 2018-09-16 19:25:52

dans votre question vous avez demandé un regex, et je suis un grand fan et l'utilisateur d'eux, donc quand j'ai eu besoin de faire ce même argument divisé que vous, j'ai écrit mon propre regex après googler autour et ne pas trouver une solution simple. J'aime les solutions courtes, donc j'en ai fait une et la voici:

            var re = @"\G(""((""""|[^""])+)""|(\S+)) *";
            var ms = Regex.Matches(CmdLine, re);
            var list = ms.Cast<Match>()
                         .Select(m => Regex.Replace(
                             m.Groups[2].Success
                                 ? m.Groups[2].Value
                                 : m.Groups[4].Value, @"""""", @"""")).ToArray();

il traite les blancs et les guillemets à l'intérieur des guillemets, et convertit " "fermé à". N'hésitez pas à utiliser le code!

1
répondu Thomas Petersson 2013-10-01 06:55:18

Un purement solution gérée pourrait être utile. Il y a trop de commentaires de "problèmes" pour la fonction WINAPI et elle n'est pas disponible sur d'autres plateformes. Voici mon code a un comportement défini (que vous pouvez modifier si vous le souhaitez).

il devrait faire la même chose que .NET/Windows en fournissant ce paramètre string[] args , et je l'ai comparé avec un certain nombre de valeurs" intéressantes".

C'est un mise en œuvre classique state-machine qui prend chaque caractère de la chaîne d'entrée et l'interprète pour l'état actuel, produisant la sortie et un nouvel état. L'état est défini dans les variables escape , inQuote , hadQuote et prevCh , et la sortie est collectée en currentArg et args .

quelques-unes des spécialités que j'ai découvertes par des expériences sur une invite de commande réelle (Windows 7): \ produit \ , \" produit " , "" produit " .

le personnage de ^ semble aussi magique: il disparaît toujours quand il ne le double pas. Sinon, cela n'a aucun effet sur une ligne de commande réelle. Mon implémentation ne supporte pas cela, car je n'ai pas trouvé de modèle dans ce comportement. Peut-être que quelqu'un en sait plus à ce sujet.

quelque chose qui ne s'inscrit pas dans ce modèle est le suivant commande:

cmd /c "argdump.exe "a b c""

la commande cmd semble saisir les citations externes et prendre le reste mot à mot. Il doit y avoir une sauce magique spéciale là-dedans.

Je n'ai pas fait de benchmarks sur ma méthode, mais considérez-la raisonnablement rapide. Il n'utilise pas Regex et ne fait pas de concaténation de chaîne de caractères mais utilise plutôt un StringBuilder pour collecter les caractères d'un argument et les met dans une liste.

/// <summary>
/// Reads command line arguments from a single string.
/// </summary>
/// <param name="argsString">The string that contains the entire command line.</param>
/// <returns>An array of the parsed arguments.</returns>
public string[] ReadArgs(string argsString)
{
    // Collects the split argument strings
    List<string> args = new List<string>();
    // Builds the current argument
    var currentArg = new StringBuilder();
    // Indicates whether the last character was a backslash escape character
    bool escape = false;
    // Indicates whether we're in a quoted range
    bool inQuote = false;
    // Indicates whether there were quotes in the current arguments
    bool hadQuote = false;
    // Remembers the previous character
    char prevCh = '"151910920"';
    // Iterate all characters from the input string
    for (int i = 0; i < argsString.Length; i++)
    {
        char ch = argsString[i];
        if (ch == '\' && !escape)
        {
            // Beginning of a backslash-escape sequence
            escape = true;
        }
        else if (ch == '\' && escape)
        {
            // Double backslash, keep one
            currentArg.Append(ch);
            escape = false;
        }
        else if (ch == '"' && !escape)
        {
            // Toggle quoted range
            inQuote = !inQuote;
            hadQuote = true;
            if (inQuote && prevCh == '"')
            {
                // Doubled quote within a quoted range is like escaping
                currentArg.Append(ch);
            }
        }
        else if (ch == '"' && escape)
        {
            // Backslash-escaped quote, keep it
            currentArg.Append(ch);
            escape = false;
        }
        else if (char.IsWhiteSpace(ch) && !inQuote)
        {
            if (escape)
            {
                // Add pending escape char
                currentArg.Append('\');
                escape = false;
            }
            // Accept empty arguments only if they are quoted
            if (currentArg.Length > 0 || hadQuote)
            {
                args.Add(currentArg.ToString());
            }
            // Reset for next argument
            currentArg.Clear();
            hadQuote = false;
        }
        else
        {
            if (escape)
            {
                // Add pending escape char
                currentArg.Append('\');
                escape = false;
            }
            // Copy character from input, no special meaning
            currentArg.Append(ch);
        }
        prevCh = ch;
    }
    // Save last argument
    if (currentArg.Length > 0 || hadQuote)
    {
        args.Add(currentArg.ToString());
    }
    return args.ToArray();
}
1
répondu ygoe 2018-09-16 19:43:06

Actuellement, c'est le code que j'ai:

    private String[] SplitCommandLineArgument(String argumentString)
    {
        StringBuilder translatedArguments = new StringBuilder(argumentString);
        bool escaped = false;
        for (int i = 0; i < translatedArguments.Length; i++)
        {
            if (translatedArguments[i] == '"')
            {
                escaped = !escaped;
            }
            if (translatedArguments[i] == ' ' && !escaped)
            {
                translatedArguments[i] = '\n';
            }
        }

        string[] toReturn = translatedArguments.ToString().Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
        for(int i = 0; i < toReturn.Length; i++)
        {
            toReturn[i] = RemoveMatchingQuotes(toReturn[i]);
        }
        return toReturn;
    }

    public static string RemoveMatchingQuotes(string stringToTrim)
    {
        int firstQuoteIndex = stringToTrim.IndexOf('"');
        int lastQuoteIndex = stringToTrim.LastIndexOf('"');
        while (firstQuoteIndex != lastQuoteIndex)
        {
            stringToTrim = stringToTrim.Remove(firstQuoteIndex, 1);
            stringToTrim = stringToTrim.Remove(lastQuoteIndex - 1, 1); //-1 because we've shifted the indicies left by one
            firstQuoteIndex = stringToTrim.IndexOf('"');
            lastQuoteIndex = stringToTrim.LastIndexOf('"');
        }
        return stringToTrim;
    }

ça ne marche pas avec des citations évitées, mais ça marche pour les cas que j'ai rencontrés jusqu'à présent.

0
répondu Anton 2008-11-18 19:10:46

il s'agit d'une réponse au code D'Anton, qui ne fonctionne pas avec des citations échappées. J'ai modifié 3 endroits.

  1. Le constructeur pour StringBuilder dans SplitCommandLineArguments à la place \" avec \r
  2. Dans le pour "en boucle 151960920" dans SplitCommandLineArguments , j'ai maintenant remplacer \r par \ .
  3. a changé la méthode SplitCommandLineArgument de privé à public static .

public static string[] SplitCommandLineArgument( String argumentString )
{
    StringBuilder translatedArguments = new StringBuilder( argumentString ).Replace( "\\"", "\r" );
    bool InsideQuote = false;
    for ( int i = 0; i < translatedArguments.Length; i++ )
    {
        if ( translatedArguments[i] == '"' )
        {
            InsideQuote = !InsideQuote;
        }
        if ( translatedArguments[i] == ' ' && !InsideQuote )
        {
            translatedArguments[i] = '\n';
        }
    }

    string[] toReturn = translatedArguments.ToString().Split( new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries );
    for ( int i = 0; i < toReturn.Length; i++ )
    {
        toReturn[i] = RemoveMatchingQuotes( toReturn[i] );
        toReturn[i] = toReturn[i].Replace( "\r", "\"" );
    }
    return toReturn;
}

public static string RemoveMatchingQuotes( string stringToTrim )
{
    int firstQuoteIndex = stringToTrim.IndexOf( '"' );
    int lastQuoteIndex = stringToTrim.LastIndexOf( '"' );
    while ( firstQuoteIndex != lastQuoteIndex )
    {
        stringToTrim = stringToTrim.Remove( firstQuoteIndex, 1 );
        stringToTrim = stringToTrim.Remove( lastQuoteIndex - 1, 1 ); //-1 because we've shifted the indicies left by one
        firstQuoteIndex = stringToTrim.IndexOf( '"' );
        lastQuoteIndex = stringToTrim.LastIndexOf( '"' );
    }
    return stringToTrim;
}
0
répondu CS. 2009-01-21 22:28:40

Je ne pense pas qu'il y ait des guillemets simples ou ^ des guillemets pour les applications C#. La fonction suivante fonctionne très bien pour moi:

public static IEnumerable<String> SplitArguments(string commandLine)
{
    Char quoteChar = '"';
    Char escapeChar = '\';
    Boolean insideQuote = false;
    Boolean insideEscape = false;

    StringBuilder currentArg = new StringBuilder();

    // needed to keep "" as argument but drop whitespaces between arguments
    Int32 currentArgCharCount = 0;                  

    for (Int32 i = 0; i < commandLine.Length; i++)
    {
        Char c = commandLine[i];
        if (c == quoteChar)
        {
            currentArgCharCount++;

            if (insideEscape)
            {
                currentArg.Append(c);       // found \" -> add " to arg
                insideEscape = false;
            }
            else if (insideQuote)
            {
                insideQuote = false;        // quote ended
            }
            else
            {
                insideQuote = true;         // quote started
            }
        }
        else if (c == escapeChar)
        {
            currentArgCharCount++;

            if (insideEscape)   // found \ -> add \ (only \" will be ")
                currentArg.Append(escapeChar + escapeChar);       

            insideEscape = !insideEscape;
        }
        else if (Char.IsWhiteSpace(c))
        {
            if (insideQuote)
            {
                currentArgCharCount++;
                currentArg.Append(c);       // append whitespace inside quote
            }
            else
            {
                if (currentArgCharCount > 0)
                    yield return currentArg.ToString();

                currentArgCharCount = 0;
                currentArg.Clear();
            }
        }
        else
        {
            currentArgCharCount++;
            if (insideEscape)
            {
                // found non-escaping backslash -> add \ (only \" will be ")
                currentArg.Append(escapeChar);                       
                currentArgCharCount = 0;
                insideEscape = false;
            }
            currentArg.Append(c);
        }
    }

    if (currentArgCharCount > 0)
        yield return currentArg.ToString();
}
0
répondu HarryP 2017-12-28 14:00:57

Vous pouvez regarder le code que j'ai posté hier:

[C#] Chemin d'accès et les arguments des chaînes

il divise un nom de fichier + arguments en chaîne[]. Les chemins courts, les variables d'environnement et les extensions de fichiers manquantes sont traités.

(initialement, il était pour désinstaller la chaîne dans le Registre.)

0
répondu Nolmë Informatique 2018-09-16 19:35:42

essayez ce code:

    string[] str_para_linha_comando(string str, out int argumentos)
    {
        string[] linhaComando = new string[32];
        bool entre_aspas = false;
        int posicao_ponteiro = 0;
        int argc = 0;
        int inicio = 0;
        int fim = 0;
        string sub;

        for(int i = 0; i < str.Length;)
        {
            if (entre_aspas)
            {
                // Está entre aspas
                sub = str.Substring(inicio+1, fim - (inicio+1));
                linhaComando[argc - 1] = sub;

                posicao_ponteiro += ((fim - posicao_ponteiro)+1);
                entre_aspas = false;
                i = posicao_ponteiro;
            }
            else
            {
            tratar_aspas:
                if (str.ElementAt(i) == '\"')
                {
                    inicio = i;
                    fim = str.IndexOf('\"', inicio + 1);
                    entre_aspas = true;
                    argc++;
                }
                else
                {
                    // Se não for aspas, então ler até achar o primeiro espaço em branco
                    if (str.ElementAt(i) == ' ')
                    {
                        if (str.ElementAt(i + 1) == '\"')
                        {
                            i++;
                            goto tratar_aspas;
                        }

                        // Pular os espaços em branco adiconais
                        while(str.ElementAt(i) == ' ') i++;

                        argc++;
                        inicio = i;
                        fim = str.IndexOf(' ', inicio);
                        if (fim == -1) fim = str.Length;
                        sub = str.Substring(inicio, fim - inicio);
                        linhaComando[argc - 1] = sub;
                        posicao_ponteiro += (fim - posicao_ponteiro);

                        i = posicao_ponteiro;
                        if (posicao_ponteiro == str.Length) break;
                    }
                    else
                    {
                        argc++;
                        inicio = i;
                        fim = str.IndexOf(' ', inicio);
                        if (fim == -1) fim = str.Length;

                        sub = str.Substring(inicio, fim - inicio);
                        linhaComando[argc - 1] = sub;
                        posicao_ponteiro += fim - posicao_ponteiro;
                        i = posicao_ponteiro;
                        if (posicao_ponteiro == str.Length) break;
                    }
                }
            }
        }

        argumentos = argc;

        return linhaComando;
    }

c'est écrit en portugais.

0
répondu Lucas De Jesus 2018-09-16 19:45:59

voici un liner qui obtient le travail fait (voir la ligne qui fait tout le travail à L'intérieur des Liner Burstcmd (...) méthode.)

ce n'est pas ce que j'appelle la ligne de code la plus lisible, mais vous pouvez la déchiffrer pour la lisibilité. Il est simple sur le but et ne fonctionne pas bien pour tous les cas d'argument (comme les arguments de nom de fichier qui contiennent le délimiteur de caractère de chaîne fendue dans eux).

Cette solution a bien fonctionné dans Mes solutions qui l'utilisent. Comme je l'ai dit, il obtient le travail sans un nid de rat de code pour gérer tous les formats d'argument n-factoriel.

using System;
using System.Collections.Generic;
using System.Linq;

namespace CmdArgProcessor
{
    class Program
    {
        static void Main(string[] args)
        {
            // test switches and switches with values
            // -test1 1 -test2 2 -test3 -test4 -test5 5

            string dummyString = string.Empty;

            var argDict = BurstCmdLineArgs(args);

            Console.WriteLine("Value for switch = -test1: {0}", argDict["test1"]);
            Console.WriteLine("Value for switch = -test2: {0}", argDict["test2"]);
            Console.WriteLine("Switch -test3 is present? {0}", argDict.TryGetValue("test3", out dummyString));
            Console.WriteLine("Switch -test4 is present? {0}", argDict.TryGetValue("test4", out dummyString));
            Console.WriteLine("Value for switch = -test5: {0}", argDict["test5"]);

            // Console output:
            //
            // Value for switch = -test1: 1
            // Value for switch = -test2: 2
            // Switch -test3 is present? True
            // Switch -test4 is present? True
            // Value for switch = -test5: 5
        }

        public static Dictionary<string, string> BurstCmdLineArgs(string[] args)
        {
            var argDict = new Dictionary<string, string>();

            // Flatten the args in to a single string separated by a space.
            // Then split the args on the dash delimiter of a cmd line "switch".
            // E.g. -mySwitch myValue
            //  or -JustMySwitch (no value)
            //  where: all values must follow a switch.
            // Then loop through each string returned by the split operation.
            // If the string can be split again by a space character,
            // then the second string is a value to be paired with a switch,
            // otherwise, only the switch is added as a key with an empty string as the value.
            // Use dictionary indexer to retrieve values for cmd line switches.
            // Use Dictionary::ContainsKey(...) where only a switch is recorded as the key.
            string.Join(" ", args).Split('-').ToList().ForEach(s => argDict.Add(s.Split()[0], (s.Split().Count() > 1 ? s.Split()[1] : "")));

            return argDict;
        }
    }
}
0
répondu Vance McCorkle 2018-09-16 19:47:34

utiliser:

public static string[] SplitArguments(string args) {
    char[] parmChars = args.ToCharArray();
    bool inSingleQuote = false;
    bool inDoubleQuote = false;
    bool escaped = false;
    bool lastSplitted = false;
    bool justSplitted = false;
    bool lastQuoted = false;
    bool justQuoted = false;

    int i, j;

    for(i=0, j=0; i<parmChars.Length; i++, j++) {
        parmChars[j] = parmChars[i];

        if(!escaped) {
            if(parmChars[i] == '^') {
                escaped = true;
                j--;
            } else if(parmChars[i] == '"' && !inSingleQuote) {
                inDoubleQuote = !inDoubleQuote;
                parmChars[j] = '\n';
                justSplitted = true;
                justQuoted = true;
            } else if(parmChars[i] == '\'' && !inDoubleQuote) {
                inSingleQuote = !inSingleQuote;
                parmChars[j] = '\n';
                justSplitted = true;
                justQuoted = true;
            } else if(!inSingleQuote && !inDoubleQuote && parmChars[i] == ' ') {
                parmChars[j] = '\n';
                justSplitted = true;
            }

            if(justSplitted && lastSplitted && (!lastQuoted || !justQuoted))
                j--;

            lastSplitted = justSplitted;
            justSplitted = false;

            lastQuoted = justQuoted;
            justQuoted = false;
        } else {
            escaped = false;
        }
    }

    if(lastQuoted)
        j--;

    return (new string(parmChars, 0, j)).Split(new[] { '\n' });
}

basé sur vapeur dans la Ruelle 'S réponse, celui-ci soutient également ^ évasions.

exemples:

  • il s'agit d'un test
    • ce
    • est
    • un
    • test
  • ce "est un" test
    • ce
    • est un
    • test
  • ce ^"est un^" test
    • ce
    • "est
    • "
    • test
  • ce "" "est un ^^ test"
    • ce
    • est un essai

il supporte aussi plusieurs espaces (casse les arguments juste une fois par bloc d'espaces).

0
répondu Fabio Iotti 2018-09-17 17:49:57

je ne suis pas sûr si je vous ai compris, mais le problème est que le caractère utilisé comme séparateur, se retrouve également à l'intérieur du texte? (Sauf qu'il s'est échappé avec double "?)

si c'est le cas, je créerais une boucle for , et je remplacerais toutes les instances où <"> est présent avec <|> (ou un autre caractère "sûr", mais je m'assurerais qu'il ne remplace que <">, et non < " >

après avoir itéré la chaîne, je ferais comme précédemment posté, divisé le la chaîne, mais maintenant, sur le caractère <|>.

-2
répondu Israr Khan 2018-09-16 19:22:43

Oui, la chaîne de l'objet a une fonction intégrée appelée Split() qui prend un seul paramètre spécifiant le caractère de chercher comme un délimiteur, et retourne un tableau de chaînes de caractères (string[]) avec les valeurs individuelles.

-6
répondu Charles Bretana 2018-09-16 19:18:32