Имитация парсера аргумента оболочки в C ++ - PullRequest
4 голосов
/ 04 апреля 2011

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

$> ./foo some arguments

Можно ожидать, что аргументы, передаваемые программе, будут массивом (в предположении C / C ++):

char ** argv = {"foo", "some" "arguments"}

Однако, если бы аргументы были:

$> ./foo "My name is foo" bar

Массив был бы:

char ** argv = {"foo", "My name is foo", "bar"}

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

vector<string> splitArgs(string allArgs); или string[] splitArgs(string allArgs);

Я, конечно, могу просто перебирать и переключаться между состояниями 'чтение слов "/" чтение цитируемого текста ", но я чувствую, что это не так эффективно, как могло бы быть.Я также играл с идеей регулярных выражений, но я недостаточно знаком с тем, как это делается в C ++.Для этого проекта у меня тоже установлены библиотеки boost, если это поможет.

Спасибо!RR

Ответы [ 2 ]

0 голосов
/ 04 апреля 2011

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

/**
 * Split a line into separate words.
 */
static void splitLine(char *pLine, char **pArgs) {
    char *pTmp = strchr(pLine, ' ');

    if (pTmp) {
        *pTmp = '\0';
        pTmp++;
        while ((*pTmp) && (*pTmp == ' ')) {
            pTmp++;
        }
        if (*pTmp == '\0') {
            pTmp = NULL;
        }
    }
    *pArgs = pTmp;
}



/**
 * Breaks up a line into multiple arguments.
 *
 * @param io_pLine Line to be broken up.
 * @param o_pArgc Number of components found.
 * @param io_pargc Array of individual components
 */
static void parseArguments(char *io_pLine, int *o_pArgc, char **o_pArgv) {
    char *pNext = io_pLine;
    size_t i;
    int j;
    int quoted = 0;
    size_t len = strlen(io_pLine);

    // Protect spaces inside quotes, but lose the quotes
    for(i = 0; i < len; i++) {
        if ((!quoted) && ('"' == io_pLine[i])) {
            quoted = 1;
            io_pLine[i] = ' ';
        } else if ((quoted) && ('"' == io_pLine[i])) {
            quoted = 0;
            io_pLine[i] = ' ';
        } else if ((quoted) && (' ' == io_pLine[i])) {
            io_pLine[i] = '\1';
        }
    }

    // init
    MY_memset(o_pArgv, 0x00, sizeof(char*) * C_MAXARGS);
    *o_pArgc = 1;
    o_pArgv[0] = io_pLine;

    while ((NULL != pNext) && (*o_pArgc < C_MAXARGS)) {
        splitLine(pNext, &(o_pArgv[*o_pArgc]));
        pNext = o_pArgv[*o_pArgc];

        if (NULL != o_pArgv[*o_pArgc]) {
            *o_pArgc += 1;
        }
    }

    for(j = 0; j < *o_pArgc; j++) {
        len = strlen(o_pArgv[j]);
        for(i = 0; i < len; i++) {
            if('\1' == o_pArgv[j][i]) {
                o_pArgv[j][i] = ' ';
            }
        }
    }
}
0 голосов
/ 04 апреля 2011

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

например:

System("./foo some arguments");

Это не лучшее решение .

Лучше всего написать парсер , чтобы найти каждый аргумент и передать его функции стиля exec .

...