Имитация парсера аргументов оболочки в C++

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

$> ./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); or string[] splitArgs(string allArgs);

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

Спасибо! РР


person Roadrunner-EX    schedule 04.04.2011    source источник
comment
но я чувствую, что это не так эффективно, как могло бы быть... действительно, вам лучше просто сделать это и получить работающую оболочку. В любом случае, поскольку вы спросили, проверьте stackoverflow.com/questions/541561/ для решения, использующего токенизатор повышения.   -  person Tony Delroy    schedule 04.04.2011
comment
Просто пройдитесь по каждому персонажу и посмотрите, что у вас есть. Вот я сделал это на C#. Я не уверен, что RegEx даст вам то, что вам здесь нужно.   -  person Jonathan Wood    schedule 04.04.2011
comment
Отлично, спасибо ребята. Я думаю, это то, что я хотел знать.   -  person Roadrunner-EX    schedule 04.04.2011
comment
Реализация — это шаг 2. Шаг 1 — это определение этого. Проверьте документацию по вашей любимой оболочке, чтобы найти определение, с которым вы можете работать. Что следует учитывать: несколько видов кавычек; круглые скобки; перенаправление ввода/вывода; обратная косая черта. (Кроме того, вы действительно хотите удалить первые два символа из первого токена? Почему?)   -  person Rob Kennedy    schedule 04.04.2011
comment
Миграция связана: stackoverflow.com/q/21959706/544721   -  person Grzegorz Wierzowiecki    schedule 30.04.2016


Ответы (2)


Я иногда все еще использую для этого эту простую служебную функцию C. Я в основном использую это во встроенных системах, где есть очень ограниченная стандартная библиотека, поэтому большую часть кода можно изменить, чтобы сделать его более эффективным, используя стандартные элементы управления 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] = ' ';
            }
        }
    }
}
person Pieter-Bas    schedule 04.04.2011

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

eg:

System("./foo some arguments");

Однако это не лучшее решение.

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

person mikek3332002    schedule 04.04.2011