Podziel łańcuch zawierający parametry wiersza poleceń na łańcuch [] w C#

Mam pojedynczy łańcuch zawierający parametry wiersza poleceń, które mają być przekazane do innego pliku wykonywalnego i muszę wyodrębnić łańcuch [] zawierający poszczególne parametry w taki sam sposób, jak w C#, gdyby polecenia zostały podane w wierszu poleceń. Łańcuch [] zostanie użyty podczas wykonywania innego punktu wejścia złożeń poprzez odbicie.

Czy istnieje do tego standardowa funkcja? Czy istnieje preferowana metoda (regex?) do prawidłowego podziału parametrów? Musi obsługuje '"'rozdzielone ciągi, które mogą zawierać spacje poprawnie, więc nie mogę po prostu podzielić na''.

Przykładowy ciąg znaków:

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

Przykładowy wynik:

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

Nie potrzebuję biblioteki parsowania wiersza poleceń, tylko sposób na uzyskanie ciągu znaków [], który powinien zostać wygenerowany.

Update : musiałem zmienić oczekiwany wynik, aby pasował do tego, co jest faktycznie generowane przez C# (usunięto dodatkowe "'s w podzielonych ciągach)

Author: Anton, 2008-11-18

19 answers

Oprócz dobrego i czystego rozwiązania zarządzanego przez Earwicker , warto wspomnieć, ze względu na kompletność, Windows zapewnia również CommandLineToArgvW funkcja dzielenia łańcucha na tablicę łańcuchów:

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

Parsuje ciąg poleceń Unicode i zwraca tablicę wskaźników do argumenty linii poleceń, wraz z liczenia takich argumentów, w pewien sposób to jest podobne do standardu C czas działania argv i argc wartości.

Przykład wywołania tego API z C# i rozpakowania wynikowej tablicy łańcuchów w kodzie zarządzanym można znaleźć pod adresem, " Konwersja ciągu wiersza poleceń do Args [] przy użyciu CommandLineToArgvW () API ."Poniżej znajduje się nieco prostsza wersja tego samego kodu:

[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
Author: Atif Aziz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-18 23:13:07

Denerwuje mnie to, że nie ma funkcji dzielącej ciąg znaków na podstawie funkcji, która bada każdy znak. Gdyby było, można by to napisać tak:

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

Chociaż po napisaniu tego, dlaczego nie napisać niezbędnych metod rozszerzenia. Przekonałeś mnie...

Po pierwsze, moja własna wersja Split, która przyjmuje funkcję, która musi zdecydować, czy podany znak powinien podzielić łańcuch:

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

Może dawać jakieś puste ciągi w zależności od sytuacja, ale może ta informacja będzie przydatna w innych przypadkach, więc nie usuwam pustych wpisów w tej funkcji.

Po drugie (i bardziej mundanely) mały pomocnik, który przycina pasującą parę cudzysłowów od początku i końca łańcucha. Jest bardziej wybredny niż standardowa metoda Trim - przycina tylko jeden znak z każdego końca i nie przycina tylko z jednego końca:

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

I przypuszczam, że będziesz też potrzebował testów. W porządku. Ale to musi być absolutnie ostatnia rzecz! Pierwsza funkcja pomocnicza porównująca wynik podziału z oczekiwaną zawartością tablicy:

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

W takim razie mogę napisać takie testy:

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

Oto test dla Twoich wymagań:

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

Zauważ, że implementacja ma dodatkową funkcję, że usuwa cudzysłowy wokół argumentu, jeśli ma to sens (dzięki funkcji TrimMatchingQuotes). Wierzę, że to część normalnej interpretacji wiersza poleceń.

 88
Author: Daniel Earwicker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-18 15:06:58

Parser wiersza poleceń systemu Windows zachowuje się tak, jak mówisz, dzieli się na spacje, chyba że przed nim jest nieczytelny cytat. Polecam samemu napisać parser. Coś takiego może:

    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
Author: Jeffrey L Whitledge,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-18 15:00:58

Wzięłam odpowiedź od Jeffreya L Whitledge ' a i trochę ją poprawiłam.

Obsługuje teraz zarówno pojedyncze, jak i podwójne cudzysłowy. Cudzysłowy można używać w samych parametrach, używając innych wpisywanych cudzysłowów.

Usuwa również cytaty z argumentów, ponieważ nie przyczyniają się one do informacji o argumentach.

    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
Author: Vapour in the Alley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-16 19:30:17

The good and pure managed solution by Earwicker failed to handle arguments like this:

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

Zwrócił 3 elementy:

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

Więc tutaj jest poprawka do obsługi "quoted \" escape \ "quote":

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

Testowane z 2 dodatkowymi przypadkami:

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

Zauważ również, że zaakceptowała ODPOWIEDŹ przez Atif Aziz, która używa CommandLineToArgvW również nie powiodła się. Zwraca 4 elementy:

He whispered to her \ 
I 
love 
you". 

Mam nadzieję, że to komuś pomoże szukając takiego rozwiązania w przyszłości.

 5
Author: Kevin Thach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:13
 4
Author: Mark Cidade,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-18 15:08:56

Lubię Iteratory, a obecnie LINQ sprawia, że IEnumerable jest tak łatwo użyteczny jak tablice łańcuchów, więc moje podejście zgodnie z duchem Jeffreya L Whitledge ' a odpowiedź jest (jako metoda rozszerzenia do łańcucha):

    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
Author: Monoman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-16 19:33:26

This The Code Project article is what I ' ve used in past. To dobry kawałek kodu, ale może zadziałać.

Ten artykuł MSDN jest jedyną rzeczą, jaką znalazłem, która wyjaśnia jak C# przetwarza argumenty linii poleceń.

 2
Author: Zachary Yates,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-16 19:25:52

W twoim pytaniu poprosiłeś o wyrażenie regularne, a ja jestem ich wielkim fanem i użytkownikiem, więc kiedy musiałem zrobić ten sam podział argumentów, co Ty, napisałem własne Wyrażenie regularne po wygooglowaniu i nie znalezieniu prostego rozwiązania. Lubię krótkie rozwiązania, więc zrobiłem jeden i oto jest:

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

Obsługuje spacje i cudzysłowy wewnątrz cudzysłowów i konwertuje zamknięte ""na". Zapraszam do korzystania z kodu!

 1
Author: Thomas Petersson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-01 06:55:18

A czysto zarządzane rozwiązanie Może być pomocne. Jest zbyt wiele komentarzy "problemowych" dla funkcji WINAPI i nie jest ona dostępna na innych platformach. Oto Mój kod, który ma dobrze zdefiniowane zachowanie (które możesz zmienić, jeśli chcesz).

Powinien robić to samo, co robi. NET / Windows, podając ten parametr string[] args, a ja porównałem go z wieloma "interesującymi" wartościami.

Jest to klasyczna implementacja maszyny stanowej, która przyjmuje każdy pojedynczy znak z łańcucha wejściowego i interpretuje go dla bieżącego stanu, produkując wyjście i nowy stan. Stan jest zdefiniowany w zmiennych escape, inQuote, hadQuote i prevCh, A wyjście jest zbierane w currentArg i args.

Niektóre specjalności, które odkryłem w wyniku eksperymentów na prawdziwym wierszu polecenia (Windows 7): \\ produkuje \, \" produkuje ", "" w cytowanym przedziale ".

^ postać też wydaje się magiczna: zawsze znika, gdy nie podwaja go. W przeciwnym razie nie ma to wpływu na rzeczywistą linię poleceń. Moje wdrożenie tego nie wspiera, ponieważ nie znalazłem wzorca w tym zachowaniu. Może ktoś wie o tym więcej.

Coś, co nie pasuje do tego wzorca, to następujące polecenie:

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

cmd wydaje się, że polecenie łapie zewnętrzne cudzysłowy i zabiera resztę dosłownie. Musi być w tym jakiś specjalny magiczny SOS.

Nie zrobiłem żadnych benchmarków na mojej metodzie, ale Uznaj to za dość szybkie. Nie używa Regex i nie wykonuje żadnej konkatenacji łańcuchów, ale zamiast tego używa StringBuilder do zbierania znaków dla argumentu i umieszczania ich na liście.

/// <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
Author: ygoe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-16 19:43:06

Obecnie jest to kod, który mam:

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

To nie działa z uciekającymi cytatami, ale działa w sprawach, z którymi do tej pory się borykałem.

 0
Author: Anton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-18 19:10:46

Jest to odpowiedź na kod Antona, który nie działa z cudzysłowami. Zmodyfikowałem 3 miejsca.

  1. konstruktor dla StringBuilder w SplitCommandLineArguments , zastępując dowolny \" z \R
  2. W for-loop W SplitCommandLineArguments , zamieniam teraz znak \R z powrotem na\".
  3. zmieniono metodęSplitCommandLineArgument z private na 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
Author: CS.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-21 22:28:40

Myślę, że nie ma pojedynczych cudzysłowów lub ^ cudzysłowów dla aplikacji C#. Następująca funkcja działa dobrze dla mnie:

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
Author: HarryP,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-28 14:00:57

Możesz rzucić okiem na kod, który wczoraj napisałem:

[C#] ciągi ścieżek i argumentów

Dzieli nazwę pliku + argumenty na łańcuch znaków []. Obsługiwane są krótkie ścieżki, zmienne środowiskowe i brakujące rozszerzenia plików.

(początkowo był to UninstallString w rejestrze.)

 0
Author: Nolmë Informatique,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-16 19:35:42

Wypróbuj ten kod:

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

Jest napisane po portugalsku.

 0
Author: Lucas De Jesus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-16 19:45:59

Oto jedna linia, która wykonuje zadanie (zobacz jedną linię, która wykonuje wszystkie prace wewnątrz BurstCmdLineArgs(...) metoda).

Nie jest to, co nazwałbym najbardziej czytelną linijką kodu, ale możesz ją wyłamać ze względu na czytelność. Jest to proste i nie działa dobrze we wszystkich przypadkach argumentów(np. argumenty nazw plików, które zawierają rozdzielacz znaków łańcuchowych).

To rozwiązanie sprawdziło się dobrze w moich rozwiązaniach, które go używają. Tak jak mówiłem, to dostaje pracę. zrobione bez szczurzego gniazda kodu do obsługi każdego możliwego formatu argumentów n-factorial.

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
Author: Vance McCorkle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-16 19:47:34

Użycie:

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

W oparciu o odpowiedź pary w alejce , ta również obsługuje ^ escapes.

Przykłady:

  • to jest test
    • to
    • jest
    • a
    • test
  • to "jest" test
    • to
    • jest
    • test
  • this ^ "is a^" test
    • to
    • "jest
    • a "
    • test
  • to "" jest ^^ badanie"
    • to
    • jest ^ test

Obsługuje również wiele spacji (łamie argumenty tylko jeden raz na blok spacji).

 0
Author: Fabio Iotti,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-17 17:49:57

Nie jestem pewien, czy Cię zrozumiałem, ale czy problem polega na tym, że znak używany jako splitter również znajduje się wewnątrz tekstu? (Z wyjątkiem tego, że jest on unikany z podwójnym "?)

Jeśli tak, utworzyłbym pętlę for i zamienił wszystkie instancje, w których występuje na (lub inny "bezpieczny" znak, ale upewnij się, że zastępuje tylko, a nie

Po iteracji napisu zrobiłbym tak, jak wcześniej napisano, podzielił łańcuch, ale teraz na znak >.

 -2
Author: Israr Khan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-16 19:22:43

Tak, obiekt string ma wbudowaną funkcję o nazwie Split(), która pobiera pojedynczy parametr określający znak, który ma być szukany jako ogranicznik i zwraca tablicę łańcuchów (string []) z pojedynczymi wartościami.

 -6
Author: Charles Bretana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-16 19:18:32