it-swarm.com.de

Zeichenfolge mit Befehlszeilenparametern in Zeichenfolge [] in C # aufteilen

Ich habe eine einzelne Zeichenfolge, die die Befehlszeilenparameter enthält, die an eine andere ausführbare Datei übergeben werden sollen. Ich muss die Zeichenfolge [], die die einzelnen Parameter enthält, auf dieselbe Weise extrahieren wie C #, wenn die Befehle in der Befehlszeile angegeben wurden. Die Zeichenfolge [] wird verwendet, wenn ein anderer Einstiegspunkt einer Assembly über Reflektion ausgeführt wird.

Gibt es dafür eine Standardfunktion? Oder gibt es eine bevorzugte Methode (Regex?), Um die Parameter richtig aufzuteilen? Es muss mit begrenzten Zeichenfolgen "" "umgehen, die Leerzeichen enthalten können. Daher kann ich nicht einfach auf" "aufteilen.

Beispielstring:

string parameterString = @"/src:""C:\tmp\Some Folder\Sub Folder"" /users:""[email protected]"" tasks:""SomeTask,Some Other Task"" -someParam foo";

Beispielergebnis:

string[] parameterArray = new string[] { 
  @"/src:C:\tmp\Some Folder\Sub Folder",
  @"/users:[email protected]",
  @"tasks:SomeTask,Some Other Task",
  @"-someParam",
  @"foo"
};

Ich brauche keine Kommandozeilen-Parsing-Bibliothek, nur einen Weg, um den String [] zu erhalten, der generiert werden soll.

Update : Ich musste das erwartete Ergebnis ändern, um mit dem übereinzustimmen, was tatsächlich von C # generiert wird (die zusätzlichen "" s in den Split-Strings entfernt).

79
Anton

Neben der guten und reinen verwalteten Lösung by Earwicker kann der Vollständigkeit halber noch erwähnt werden, dass Windows auch die CommandLineToArgvW -Funktion zum Aufteilen eines Strings in ein Array bereitstellt von Saiten:

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

Analysiert eine Unicode-Befehlszeilenzeichenfolge und gibt ein Array von Zeigern an .__ zurück. die Befehlszeilenargumente zusammen mit eine Anzahl solcher Argumente in gewisser Weise das ist dem Standard C .__ ähnlich. Laufzeit argv und argc Werte.

Ein Beispiel für das Aufrufen dieser API aus C # und das Entpacken des resultierenden String-Arrays in verwaltetem Code finden Sie unter „ Befehlszeilenzeichenfolge mithilfe der CommandLineToArgvW () - API in Args [] konvertieren .“ Gleicher 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);
    }
}
64
Atif Aziz

Es ärgert mich, dass es keine Funktion gibt, eine Zeichenfolge anhand einer Funktion aufzuteilen, die jedes Zeichen untersucht. Wenn ja, könnte man es so schreiben:

    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));
    }

Obwohl das geschrieben wurde, warum nicht die notwendigen Erweiterungsmethoden schreiben. Okay, du hast mich dazu geredet ...

Zuerst meine eigene Version von Split, die eine Funktion übernimmt, die entscheiden muss, ob das angegebene Zeichen den String aufteilen soll:

    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);
    }

Abhängig von der Situation kann es einige leere Zeichenfolgen geben, aber möglicherweise werden diese Informationen in anderen Fällen nützlich sein, so dass ich die leeren Einträge in dieser Funktion nicht entferne.

Zweitens (und mehr und mehr) ein kleiner Helfer, der ein passendes Zitatpaar vom Anfang und vom Ende einer Zeichenkette trimmt. Es ist weniger wählerisch als die standardmäßige Trimmmethode - es wird nur ein Zeichen von jedem Ende beschnitten und nicht von nur einem Ende beschnitten:

    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;
    }

Und ich nehme an, Sie wollen auch ein paar Tests. Na gut, dann. Das muss aber absolut das letzte sein! Zunächst eine Hilfsfunktion, die das Ergebnis der Aufteilung mit dem erwarteten Array-Inhalt vergleicht:

    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]);
    }

Dann kann ich Tests wie folgt schreiben:

        Test("");
        Test("a", "a");
        Test(" abc ", "abc");
        Test("a b ", "a", "b");
        Test("a b \"c d\"", "a", "b", "c d");

Hier ist der Test für Ihre Anforderungen:

        Test(@"/src:""C:\tmp\Some Folder\Sub Folder"" /users:""[email protected]"" tasks:""SomeTask,Some Other Task"" -someParam",
             @"/src:""C:\tmp\Some Folder\Sub Folder""", @"/users:""[email protected]""", @"tasks:""SomeTask,Some Other Task""", @"-someParam");

Beachten Sie, dass die Implementierung die zusätzliche Funktion hat, dass Anführungszeichen um ein Argument entfernt werden, wenn dies sinnvoll ist (dank der TrimMatchingQuotes-Funktion). Ich glaube, das gehört zur normalen Interpretation der Befehlszeile.

93

Der Windows-Befehlszeilen-Parser verhält sich genau so, wie Sie sagen, im Leerzeichen aufgeteilt, es sei denn, es befindet sich ein nicht geschlossenes Anführungszeichen. Ich würde empfehlen, den Parser selbst zu schreiben. So etwas vielleicht:

    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');
    }
23

Ich nahm die Antwort von Jeffrey L Whitledge und verbesserte es ein wenig.

Es unterstützt jetzt sowohl einfache als auch doppelte Anführungszeichen. Sie können Anführungszeichen in den Parametern selbst verwenden, indem Sie andere eingegebene Anführungszeichen verwenden.

Es entfernt auch die Anführungszeichen aus den Argumenten, da diese nicht zu den Argumentinformationen beitragen.

    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);
    }
12

Die gute und reine verwaltete Lösung by Earwicker hat Argumente wie folgt nicht verarbeitet:

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

Es wurden 3 Elemente zurückgegeben:

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

Hier ist also ein Fix, um das "quoted \" escape\"quote" zu unterstützen:

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));
}

Getestet mit 2 zusätzlichen Fällen:

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

Beachten Sie auch, dass die akzeptierte Antwort von Atif Aziz , die CommandLineToArgvW verwendet, ebenfalls fehlgeschlagen ist. Es wurden 4 Elemente zurückgegeben:

He whispered to her \ 
I 
love 
you". 

Ich hoffe, dies hilft jemandem, der in Zukunft nach einer solchen Lösung sucht.

6
Kevin Thach

Ich mag Iteratoren und heutzutage LINQ macht IEnumerable<String> so einfach verwendbar wie Arrays von Strings, also folge ich dem Geiste von Jeffrey L Whitledge's Antwort (als Erweiterungsmethode zu string):

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();
}
4
Monoman
4
Mark Cidade

Dieser The Code Project-Artikel habe ich in der Vergangenheit verwendet. Es ist ein guter Code, aber es könnte funktionieren.

Dieser MSDN-Artikel ist das einzige, was ich finden konnte, das erklärt, wie C # Befehlszeilenargumente analysiert.

2
Zachary Yates

In Ihrer Frage haben Sie nach einem Regex gefragt, und ich bin ein großer Fan und Benutzer von ihnen. Wenn ich also das gleiche Argument wie Sie machen musste, schrieb ich meinen eigenen Regex nach dem Googeln und keine einfache Lösung. Ich mag kurze Lösungen, also habe ich eine gemacht und hier ist es:

            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();

Es behandelt Leerzeichen und Anführungszeichen in Anführungszeichen und konvertiert eingeschlossene "" in ". Sie können den Code gerne verwenden!"

1

Benutzen:

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' });
}

Basierend auf Vapor in der Alley , unterstützt diese auch Fluchtwege.

Beispiele:

  • dies ist ein Test
    • diese
    • ist
    • ein
    • prüfung
  • dies ist ein Test
    • diese
    • ist ein
    • prüfung
  • dieser ^ "ist ein ^" Test
    • diese
    • "ist
    • ein"
    • prüfung
  • dieses "" "ist ein ^^ Test"
    • diese
    • Das 
    • ist ein ^ Test

Es unterstützt auch mehrere Leerzeichen (bricht Argumente nur einmal pro Block von Leerzeichen).

1
Fabio Iotti

Eine rein verwaltete Lösung kann hilfreich sein. Für die WINAPI-Funktion gibt es zu viele "Problem" -Kommentare, die auf anderen Plattformen nicht verfügbar sind. Hier ist mein Code, der ein genau definiertes Verhalten aufweist (das Sie ändern können, wenn Sie möchten).

Es sollte dasselbe tun wie bei .NET/Windows, wenn der string[] args-Parameter angegeben wird, und ich habe es mit einer Reihe "interessanter" Werte verglichen.

Dies ist eine klassische Implementierung der Zustandsmaschine, die jedes einzelne Zeichen aus der Eingabezeichenfolge entnimmt und für den aktuellen Zustand interpretiert, wobei eine Ausgabe und ein neuer Zustand erzeugt wird. Der Zustand ist in den Variablen escape, inQuote, hadQuote und prevCh definiert, und die Ausgabe wird in currentArg und args gesammelt.

Einige der Besonderheiten, die ich durch Experimente mit einem echten Befehl entdeckt habe (Windows 7): \\ erzeugt \, \" erzeugt ", "" innerhalb eines angegebenen Bereichs erzeugt ".

Das ^-Zeichen scheint auch magisch zu sein: Es verschwindet immer, wenn es nicht verdoppelt wird. Andernfalls hat dies keine Auswirkungen auf eine echte Befehlszeile. Meine Implementierung unterstützt dies nicht, da ich in diesem Verhalten kein Muster gefunden habe. Vielleicht weiß jemand mehr darüber.

Etwas, das nicht in dieses Muster passt, ist der folgende Befehl:

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

Der Befehl cmd scheint die äußeren Anführungszeichen einzufangen und den Rest wörtlich zu nehmen. Es muss eine spezielle magische Sauce geben.

Ich habe keine Benchmarks für meine Methode gemacht, halte sie aber für ziemlich schnell. Es verwendet keine Regex und führt keine String-Verkettung aus, sondern verwendet stattdessen eine StringBuilder, um die Zeichen für ein Argument zu sammeln und sie in eine Liste aufzunehmen.

/// <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 = '\0';
    // 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
ygoe

Derzeit ist dies der Code, den ich habe:

    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;
    }

Es funktioniert nicht mit vergebenen Zitaten, aber es funktioniert für die Fälle, auf die ich bisher gestoßen bin.

0
Anton

Oh bitte. Es ist alles ... Eugh. Aber das ist echt offiziell. Von Microsoft in C # für .NET Core, möglicherweise nur Windows, möglicherweise plattformübergreifend, aber MIT lizenziert.

Wählen Sie Leckerbissen, Methodendeklarationen und bemerkenswerte Kommentare.

internal static unsafe string[] InternalCreateCommandLine(bool includeArg0)
private static unsafe int SegmentCommandLine(char * pCmdLine, string[] argArray, bool includeArg0)
private static unsafe int ScanArgument0(ref char* psrc, char[] arg)
private static unsafe int ScanArgument(ref char* psrc, ref bool inquote, char[] arg)

// First, parse the program name (argv[0]). Argv[0] is parsed under special rules. Anything up to 
// the first whitespace outside a quoted subtring is accepted. Backslashes are treated as normal 
// characters.

// Rules: 2N backslashes + " ==> N backslashes and begin/end quote
//      2N+1 backslashes + " ==> N backslashes + literal "
//         N backslashes     ==> N backslashes

Dies ist Code, der von .NET Framework aus in .NET Core portiert wurde. Ich nehme an, dass es sich entweder um die MSVC-C-Bibliothek oder um CommandLineToArgvW handelt.

Hier ist mein halbherziger Versuch, mit den regulären Ausdrücken einige der Shenanigans zu behandeln und das Null-Argument zu ignorieren. Es ist ein bisschen magisch.

private static readonly Regex RxWinArgs
  = new Regex("([^\\s\"]+\"|((?<=\\s|^)(?!\"\"(?!\"))\")+)(\"\"|.*?)*\"[^\\s\"]*|[^\\s]+",
    RegexOptions.Compiled
    | RegexOptions.Singleline
    | RegexOptions.ExplicitCapture
    | RegexOptions.CultureInvariant);

internal static IEnumerable<string> ParseArgumentsWindows(string args) {
  var match = RxWinArgs.Match(args);

  while (match.Success) {
    yield return match.Value;
    match = match.NextMatch();
  }
}

Getestet einiges auf verrückte generierte Ausgabe. Die Ausgabe entspricht einem guten Prozentsatz dessen, was die Affen eingegeben haben und durch CommandLineToArgvW liefen.

0
TylerY86

Hier ist ein Einzeiler, der die Arbeit erledigt (siehe die eine Zeile, die die gesamte Arbeit innerhalb der BurstCmdLineArgs (...) -Methode erledigt).

Nicht das, was ich als die lesbarste Codezeile bezeichnen würde, aber Sie können es aus Gründen der besseren Lesbarkeit herausfinden. Es ist absichtlich einfach und funktioniert nicht für alle Argumente (wie Dateinamenargumente, die das Trennzeichen für Trennzeichen enthalten).

Diese Lösung hat bei meinen Lösungen, die sie verwenden, gut funktioniert. Wie ich schon sagte, wird die Arbeit ohne ein Code-Ratten-Nest erledigt, um jedes mögliche Argument-Format n-Fakultat zu handhaben.

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
Vance McCorkle

Sie können sich den Code ansehen, den ich gestern gepostet habe:

[C #] Pfad und Argumente

Es teilt einen Dateinamen + Argumente in Zeichenfolge [] auf. Es werden kurze Pfade, Umgebungsvariablen und fehlende Dateierweiterungen behandelt.

(Anfänglich war es für UninstallString in der Registrierung.)

0

Konnte hier nichts finden, was mir gefiel. Ich hasse es, den Stack mit Yield Magic für eine kleine Befehlszeile durcheinander zu bringen (wenn es ein Terabyte-Stream wäre, wäre es eine andere Geschichte).

Hier ist meine Einstellung, sie unterstützt Anführungszeichen mit Anführungszeichen wie diesen:

param = "a 15" "Bildschirm ist nicht schlecht" param2 = 'a 15 "Bildschirm ist nicht schlecht' param3 =" "param4 =/param5

ergebnis:

param = "ein 15" Bildschirm ist nicht schlecht "

param2 = 'ein 15 "Bildschirm ist nicht schlecht'

param3 = ""

param4 =

/ param5

public static string[] SplitArguments(string commandLine)
{
    List<string> args         = new List<string>();
    List<char>   currentArg   = new List<char>();
    char?        quoteSection = null; // Keeps track of a quoted section (and the type of quote that was used to open it)
    char[]       quoteChars   = new[] {'\'', '\"'};
    char         previous     = ' '; // Used for escaping double quotes

    for (var index = 0; index < commandLine.Length; index++)
    {
        char c = commandLine[index];
        if (quoteChars.Contains(c))
        {
            if (previous == c) // Escape sequence detected
            {
                previous = ' '; // Prevent re-escaping
                if (!quoteSection.HasValue)
                {
                    quoteSection = c; // oops, we ended the quoted section prematurely
                    continue;         // don't add the 2nd quote (un-escape)
                }

                if (quoteSection.Value == c)
                    quoteSection = null; // appears to be an empty string (not an escape sequence)
            }
            else if (quoteSection.HasValue)
            {
                if (quoteSection == c)
                    quoteSection = null; // End quoted section
            }
            else
                quoteSection = c; // Start quoted section
        }
        else if (char.IsWhiteSpace(c))
        {
            if (!quoteSection.HasValue)
            {
                args.Add(new string(currentArg.ToArray()));
                currentArg.Clear();
                previous = c;
                continue;
            }
        }

        currentArg.Add(c);
        previous = c;
    }

    if (currentArg.Count > 0)
        args.Add(new string(currentArg.ToArray()));

    return args.ToArray();
}
0
Louis Somers

Dies ist eine Antwort auf Antons Code, der nicht mit Escape-Anführungszeichen funktioniert. Ich habe 3 Stellen geändert.

  1. Der Konstruktor für StringBuilder in SplitCommandLineArguments ersetzt alle \ " durch \ r
  2. In der for-loop in SplitCommandLineArguments ersetze ich jetzt das Zeichen \ r wieder in \ ".
  3. Die Methode SplitCommandLineArgument wurde von private in public static geändert.

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
CS.

Ich glaube nicht, dass es einfache Anführungszeichen oder ^ -Anführungszeichen für C # -Anwendungen gibt .. Die folgende Funktion funktioniert für mich gut:

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
HarryP

Versuchen Sie diesen 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;
    }

Es ist auf Portugiesisch geschrieben.

0
Lucas De Jesus