Как создать строку [] из пользовательского ввода, чтобы проанализировать ее с помощью Mongo.Options или аналогичных библиотек? [Дубликат]

Здесь я предлагаю больше способов, которые кажутся быстрее во многих отношениях:

Вариант 1: Разделить строку на. или [или] или «или», отмените его, пропустите пустые элементы.

function getValue(path, origin) {
    if (origin === void 0 || origin === null) origin = self ? self : this;
    if (typeof path !== 'string') path = '' + path;
    var parts = path.split(/\[|\]|\.|'|"/g).reverse(), name; // (why reverse? because it's usually faster to pop off the end of an array)
    while (parts.length) { name=parts.pop(); if (name) origin=origin[name]; }
    return origin;
}

Вариант 2 (самый быстрый из всех, кроме eval): Масштабирование с низким уровнем (без регулярного выражения / разделения / etc , просто быстрое сканирование символов). Примечание. Этот код не поддерживает кавычки для индексов.

function getValue(path, origin) {
    if (origin === void 0 || origin === null) origin = self ? self : this;
    if (typeof path !== 'string') path = '' + path;
    var c = '', pc, i = 0, n = path.length, name = '';
    if (n) while (i<=n) ((c = path[i++]) == '.' || c == '[' || c == ']' || c == void 0) ? (name?(origin = origin[name], name = ''):(pc=='.'||pc=='['||pc==']'&&c==']'?i=n+2:void 0),pc=c) : name += c;
    if (i==n+2) throw "Invalid path: "+path;
    return origin;
} // (around 1,000,000+/- ops/sec)

Вариант 3: (новый: параметр 2 расширен для поддержки кавычки - немного медленнее, но все же быстро)

function getValue(path, origin) {
    if (origin === void 0 || origin === null) origin = self ? self : this;
    if (typeof path !== 'string') path = '' + path;
    var c, pc, i = 0, n = path.length, name = '', q;
    while (i<=n)
        ((c = path[i++]) == '.' || c == '[' || c == ']' || c == "'" || c == '"' || c == void 0) ? (c==q&&path[i]==']'?q='':q?name+=c:name?(origin?origin=origin[name]:i=n+2,name='') : (pc=='['&&(c=='"'||c=="'")?q=c:pc=='.'||pc=='['||pc==']'&&c==']'||pc=='"'||pc=="'"?i=n+2:void 0), pc=c) : name += c;
    if (i==n+2 || name) throw "Invalid path: "+path;
    return origin;
}

JSPerf: http://jsperf.com/ways-to-dereference-a-delimited-property-string / 3

«eval (...)» по-прежнему остается королем (если это так). Если у вас есть пути собственности прямо под вашим контролем, не должно быть никаких проблем с используя «eval» (особенно если требуется скорость). Если вы тянете дорожки свойств «по проводам» ( на линии !? lol: P), то да, используйте что-то еще, чтобы быть в безопасности. идиот сказал бы, что никогда не будет использовать «eval» вообще, так как там есть веские причины , когда его использовать. Также «Он используется в парсере JSON Дуга Крокфорда ». Если вход безопасен, тогда никаких проблем вообще. Используйте правильный инструмент для правильной работы, вот и все.

75
задан 5 revs 18 November 2008 в 19:48
поделиться

19 ответов

В дополнение к хорошему и чисто управляемому решению на Earwicker , для полноты можно отметить, что Windows также предоставляет CommandLineToArgvW для разбиения строки на массив строк:

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

Разбирает строку командной строки Unicode и возвращает массив указателей на аргументы командной строки, а также количество таких аргументов, которое аналогично стандартным значениям времени выполнения argv и argc C.

Пример вызова этого API из C # и распаковки результирующего массива строк в управляемый код можно найти на странице « Преобразование строки командной строки в Args [] с использованием CommandLineToArgvW () API ». Ниже приведена несколько более простая версия того же кода:

[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
ответ дан 5 revs, 3 users 82% 23 August 2018 в 18:35
поделиться
  • 1
    Эта функция требует, чтобы вы избегали конечной обратной косой черты пути внутри кавычек. "C: \ Program Files \ & quot; должен быть «C: \ Program Files \\ & quot; для этого, чтобы правильно разбирать строку. – Magnus Lindhe 25 August 2009 в 14:35
  • 2
    Также стоит отметить, что CommandLineArgvW ожидает, что первым аргументом будет имя программы, и применяемая маска синтаксического анализа не совсем то же самое, если она не передана. Вы можете подделать ее с помощью чего-то вроде: CommandLineToArgs("foo.exe " + commandLine).Skip(1).ToArray(); – Scott Wegner 15 August 2012 в 06:40
  • 3
    Для полноты MSVCRT не использует CommandLineToArgvW () для преобразования командной строки в argc / argv. Он использует свой собственный код, который отличается. Например, попробуйте вызвать CreateProcess с помощью этой строки: a & quot; b c & quot; d e f. В main () вы получите 3 аргумента (как описано в MSDN), но CommandLineToArgvW () / GetCommandLineW () комбо даст вам 2. – LRN 14 November 2012 в 12:20
  • 4
    OMG это такой беспорядок. типичный суп MS. ничто не канонизируется, и никогда KISS не уважают в мире MS. – v.oddou 2 July 2015 в 05:49
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' });
}

на основе ответа Vapor in the Alley , это также поддерживает ^ escape-файлы

Примеры:

  • это тест - это тест
  • , это «является» тестом, это тест
  • , этот ^ «является« тест », это« является »тестом
  • this "" "является тегом ^^", это a ^ test

также поддерживает несколько пробелов (разрывает аргументы всего один раз на блок пробелов)

1
ответ дан 2 revs 23 August 2018 в 18:35
поделиться

Попробуйте этот код:

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

Это написано на португальском языке.

0
ответ дан 2 revs, 2 users 96% 23 August 2018 в 18:35
поделиться
  • 1
    скорее документация является португальской – Enamul Hassan 25 July 2015 в 00:07

В вашем вопросе вы спросили о регулярном выражении, и я большой поклонник и пользователь из них, поэтому, когда мне нужно было сделать такой же аргумент, как и вы, я написал свое собственное регулярное выражение после того, как искал его и не нашел простого решения , Мне нравятся короткие решения, поэтому я сделал один, и вот он:

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

Он обрабатывает пробелы и кавычки внутри кавычек и преобразует закрытые «" в ". Не стесняйтесь использовать код!

1
ответ дан 3 revs 23 August 2018 в 18:35
поделиться

Хорошее и чистое управляемое решение от Earwicker не справилось с такими аргументами:

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

Он вернул 3 элемента:

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

Итак, вот исправление, которое поддерживает «цитируемый» escape \ quot 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));
}

Протестировано с двумя дополнительными случаями:

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

Также отметил, что принятый ответ на Atif Aziz , который использует CommandLineToArgvW , также потерпел неудачу. Он возвратил 4 элемента:

He whispered to her \ 
I 
love 
you". 

Надеюсь, это поможет кому-то искать такое решение в будущем.

4
ответ дан 5 revs 23 August 2018 в 18:35
поделиться
  • 1
    Извините за некромантию, но это решение по-прежнему пропускает такие вещи, как bla.exe aAAA"b\"ASDS\"c"dSADSD, что приводит к aAAAb"ASDS"cdSADSD, где это решение выводит aAAA"b"ASDS"c"dSADSD. Я мог бы рассмотреть возможность изменения TrimMatchingQuotes на Regex("(?<!\\\\)\\\"") и использовать его , как это . – Scis 21 February 2016 в 15:06

В настоящее время это код, который у меня есть:

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

Он не работает с экранированными кавычками, но он работает для тех случаев, с которыми я сталкивался до сих пор.

0
ответ дан Anton 23 August 2018 в 18:35
поделиться

Да, строковый объект имеет встроенную функцию Split (), которая принимает один параметр, указывающий символ, который нужно искать в качестве разделителя, и возвращает массив строк (string []) с отдельными значениями в нем

-6
ответ дан Charles Bretana 23 August 2018 в 18:35
поделиться
  • 1
    Это разделило бы src: "C: \ tmp \ Some Folder \ Sub Folder & quot; неправильно. – Anton 18 November 2008 в 15:16
  • 2
    Как насчет цитат внутри строки, которые временно отключают разделение на пробелы? – Daniel Earwicker 18 November 2008 в 15:16

Это ответ на код Антона, который не работает с экранированными кавычками. Я изменил 3 места.

  1. Конструктор для StringBuilder в SplitCommandLineArguments , заменив любой \ " на \r
  2. В for-loop в SplitCommandLineArguments я теперь заменяю символ \r на \ " .
  3. Изменен метод SplitCommandLineArgument от частного к публичному 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
ответ дан CS. 23 August 2018 в 18:35
поделиться
  • 1
    Я решаю эту же проблему, вы бы подумали, что в наши дни будет существовать простое решение для строк аргументов командной строки для модуляции. Все, что я хочу быть уверенным, - это поведение, которое возникнет в результате заданной строки аргумента командной строки. Я сейчас отказываюсь и создаю модульные тесты для строки [], но может добавить некоторые интеграционные тесты, чтобы скрыть это. – Charlie Barker 19 October 2011 в 23:02

Меня раздражает, что нет функции разбить строку на основе функции, которая проверяет каждый символ. Если бы это было так, вы могли бы написать это следующим образом:

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

Хотя написав это, почему бы не написать необходимые методы расширения. Хорошо, вы говорили мне об этом ...

Во-первых, моя собственная версия Split, которая принимает функцию, которая должна решить, должен ли указанный символ разбивать строку:

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

Это может привести к некоторым пустым строкам в зависимости от ситуации, но, возможно, эта информация будет полезна в других случаях, поэтому я не удаляю пустые записи в этой функции.

Во-вторых (и более обыденно) a маленький помощник, который обрезает совпадающую пару кавычек от начала и конца строки. Это более суетливый, чем стандартный метод Trim - он будет обрезать только один символ с каждого конца, и он не будет обрезать только с одного конца:

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

И я полагаю, вам понадобятся и некоторые тесты , Ну, ладно. Но это должно быть абсолютно последнее! Сначала вспомогательная функция, которая сравнивает результат разделения с ожидаемым содержимым массива:

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

Затем я могу написать тесты следующим образом:

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

Вот тест для вашего Требования:

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

Обратите внимание, что в реализации есть дополнительная функция, которая будет удалять кавычки вокруг аргумента, если это имеет смысл (благодаря функции TrimMatchingQuotes). Я считаю, что это часть обычной интерпретации в командной строке.

87
ответ дан Daniel Earwicker 23 August 2018 в 18:35
поделиться
  • 1
    Я должен был отменить это как ответ, потому что у меня не было ожидаемых результатов. Фактический вывод не должен иметь «& quot;» в конечном массиве – Anton 18 November 2008 в 17:23
  • 2
    Я прихожу к Stack Overflow, чтобы уйти от требований, которые меняются все время! :) Вместо TrimMatchingQuotes () вы можете использовать Replace (& quot; \ & quot; & quot ;, & quot; & quot;), чтобы избавиться от всех котировок. Но Windows поддерживает \ & quot; чтобы пропускать символ кавычки. Моя функция Split не может этого сделать. – Daniel Earwicker 18 November 2008 в 19:15
  • 3
    Приятный один Earwicker :) Антон: Это решение, которое я пытался описать вам в своем предыдущем посте, но Earwicker сделал гораздо лучшую работу в написании его;) И также сильно его расширил;) – Israr Khan 19 November 2008 в 09:57
  • 4
    пробел не является единственным разделительным символом для аргументов командной строки, не так ли? – Louis Rhys 8 September 2010 в 05:31
  • 5
    @Louis Rhys - Я не уверен. Если это проблема, ее довольно легко решить: используйте char.IsWhiteSpace вместо == ' ' – Daniel Earwicker 8 September 2010 в 10:03

Я не думаю, что есть одиночные кавычки или ^ кавычки для приложений C #. Следующая функция работает отлично для меня:

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 23 August 2018 в 18:35
поделиться

Не уверен, понял ли я вас, но является ли проблема, что символ, используемый как сплиттер, также находится внутри текста? [За исключением того, что он экранирован с помощью double??)

Если это так, я бы создал цикл for и заменил все экземпляры, где & lt; "> присутствует с & lt; |> (или другим" безопасным ", но убедитесь, что он заменяет & lt;">, а не & lt; "">

. После итерации строки я сделал бы то же, что и ранее, разделил строку, но теперь на символ & lt; |>

РЕДАКТИРОВАТЬ: Для читаемости я добавил, что «записано как & lt;»>, так как стало немного неясным, что я имел в виду, когда я писал только «» и «, или |

-2
ответ дан Israr Khan 23 August 2018 в 18:35
поделиться
  • 1
    Двойные «" имеют следующий вид: «@». строковый литерал, двойной символ "внутри" @ ". строка эквивалентна \ экранированному & quot; в нормальной строке – Anton 18 November 2008 в 15:24
  • 2
    «единственным ограничением (я верю) является то, что строки разделены пробелами, если пространство не выполняется внутри« ... ». Блок & Quot; - & GT; Может быть, стрелять в птицу с базукой, но поставить булеву, которая идет «истина»? когда внутри цитаты, и если пространство обнаружено внутри, пока «true», continue, else & lt; & GT; = & lt; | & gt; – Israr Khan 18 November 2008 в 15:43

Парсер синтаксиса командной строки Windows ведет себя так же, как вы говорите, разбивается на пробел, если перед ним нет закрытой цитаты. Я бы рекомендовал написать парсер самостоятельно. Что-то вроде этого возможно:

    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
ответ дан Jeffrey L Whitledge 23 August 2018 в 18:35
поделиться
  • 1
    Я закончил с тем же, за исключением того, что я использовал .Split (новый char [] {'\n'}, StringSplitOptions.RemoveEmptyEntries) в последней строке, если между параметрами были дополнительные ''. Кажется, работает. – Anton 18 November 2008 в 16:05
  • 2
    Я полагаю, что Windows должна иметь способ избежать кавычек в параметрах ... этот алгоритм не учитывает это. – rmeador 18 November 2008 в 16:16
  • 3
    Удаление пустых строк, удаление внешних кавычек и обработка экранированных кавычек остаются в качестве упражнения для читателя. – Jeffrey L Whitledge 18 November 2008 в 17:54
  • 4
    Char.IsWhiteSpace () может помочь здесь – Sam Mackrill 15 March 2011 в 16:02

[д0] Environment.GetCommandLineArgs ()

5
ответ дан Mark Cidade 23 August 2018 в 18:35
поделиться
  • 1
    Полезно - но это приведет только к тому, что аргументы командной строки отправляются в текущий процесс. Требование состояло в том, чтобы получить строку [] из строки & quot; таким же образом, что C # будет , если команды были указаны в командной строке & quot ;. Я думаю, мы могли бы использовать декомпилятор, чтобы посмотреть, как MS реализовала это, хотя ... – rohancragg 12 October 2011 в 09:35
  • 2
    Поскольку Джон Гэллоуэй также нашел ( weblogs.asp.net/jgalloway/archive/2006/09/13/… ) декомпилятор не очень помогает, что возвращает нас к ответу Атифа ( stackoverflow.com/questions/298830/… ) – rohancragg 12 October 2011 в 09:42

Мне нравятся итераторы, и в настоящее время Linq делает IEnumerable так же легко используемым, как массивы строки, поэтому я беру его за дух Jeffrey L Whitledge: (как метод расширения для строки):

    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
ответ дан Monoman 23 August 2018 в 18:35
поделиться

вы можете посмотреть код, который я опубликовал вчера:

http://social.msdn.microsoft.com/Forums/fr-FR/netfx64bit/thread/2dfe45f5- 7940-48cd-bd57-add8f3d94102

Он разделил имя файла + аргументы на строку []. Короткие пути, переменная окружения, отсутствие расширения файла обрабатываются.

(Первоначально это было для UninstallString в реестре).

0
ответ дан Nolmë Informatique 23 August 2018 в 18:35
поделиться
4
ответ дан spoulson 23 August 2018 в 18:35
поделиться
  • 1
    Я не хочу пары ключ-значение, я хочу то же самое, что система сделала бы для создания строки [] параметров. – Anton 18 November 2008 в 15:20

Вот один лайнер, который выполняет задание (см. одну строку, которая выполняет всю работу внутри метода BurstCmdLineArgs (...)). Не то, что я бы назвал наиболее читаемой строкой кода, но вы можете раскрыть ее ради удобочитаемости. Это простое назначение и не работает хорошо для всех аргументов (например, аргументы имени файла, которые содержат разделитель символов разделенных строк). Это решение хорошо зарекомендовало себя в моих решениях, которые его используют. Как я уже сказал, он выполняет свою работу без гнезда кода крысы, чтобы обрабатывать все возможные аргументы 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
ответ дан Vance McCorkle 23 August 2018 в 18:35
поделиться

Я взял ответ от Джеффри Л Уитледжа и немного укрепил его. У меня пока нет комментариев, чтобы комментировать его ответ.

Теперь он поддерживает как одиночные, так и двойные кавычки. Вы можете использовать кавычки в самих параметрах, используя другие типизированные кавычки.

Он также удаляет кавычки из аргументов, поскольку они не вносят вклад в информацию о аргументе.

    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);
    }
9
ответ дан Vapour in the Alley 23 August 2018 в 18:35
поделиться

Эта статья проекта кода - это то, что я использовал в прошлом, это хороший бит кода, но он может работать.

Эта статья msdn - единственное, что я могу найти, что объясняет, как C # анализирует аргументы командной строки.

Надеюсь, что это поможет!

2
ответ дан Zachary Yates 23 August 2018 в 18:35
поделиться
  • 1
    Я попробовал отразить в библиотеке C #, но он переходит на собственный вызов C ++, для которого у меня нет кода, и не вижу никакого способа вызова без p-вызова его. Мне также не нужна библиотека синтаксического анализа командной строки, мне просто нужна строка []. – Anton 18 November 2008 в 15:27
  • 2
    Отражение .NET принесло мне и никуда. Глядя на источник Mono , предположил , что это разделение аргументов не выполняется CLR, а уже происходит из операционной системы. Подумайте о параметрах argc, argv основной функции C. Таким образом, нечего использовать повторно, кроме OS API. – ygoe 30 May 2014 в 20:20
Другие вопросы по тегам:

Похожие вопросы: