Разделить массив символов по разделителю, а затем сохранить результат?

Мне нужно иметь возможность анализировать следующие две строки в моей программе:

cat myfile || sort
more myfile || grep DeKalb

Строка сохраняется в char buffer[1024]. В итоге мне нужно получить указатель на массив символов для левой стороны и указатель на массив символов для правой стороны, чтобы я мог использовать их для вызова следующего для каждой стороны:

int execvp(const char *file, char *const argv[]); 

У кого-нибудь есть какие-либо идеи относительно того, как я могу получить правильные аргументы для команды execvp, если две приведенные выше строки сохранены в буфере символов char buffer[1024]; ?

Мне нужно char *left для хранения первого слова с левой стороны, затем char *const leftArgv[] для хранения обоих слов с левой стороны. Тогда мне нужно то же самое для права. Я возился с strtok уже около двух часов, и я врезался в стену. У кого-нибудь есть идеи?


person KateMak    schedule 20.03.2013    source источник


Ответы (3)


Я рекомендую вам узнать больше о регулярных выражениях. А для безболезненного решения вашей проблемы вы можете использовать Boost.Regex, которая предоставляет мощный механизм регулярных выражений. Решением будет всего несколько строк кода, но я рекомендую вам сделать это самостоятельно — это будет хорошим упражнением. Если у вас все еще есть проблемы, вернитесь с некоторыми результатами и четко укажите, где вы застряли.

person Alexander Shukaev    schedule 20.03.2013

Вы можете использовать std::getline(stream, stringToReadInto, delimeter).

Я лично использую свою собственную функцию, в которую встроены некоторые дополнительные функции, которая выглядит так:

StringList Seperate(const std::string &str, char divider, SeperationFlags seperationFlags, CharValidatorFunc whitespaceFunc)
{
    return Seperate(str, CV_IS(divider), seperationFlags, whitespaceFunc);
}

StringList Seperate(const std::string &str, CharValidatorFunc isDividerFunc, SeperationFlags seperationFlags, CharValidatorFunc whitespaceFunc)
{
    bool keepEmptySegments     = (seperationFlags & String::KeepEmptySegments);
    bool keepWhitespacePadding = (seperationFlags & String::KeepWhitespacePadding);

    StringList stringList;

    size_t startOfSegment = 0;
    for(size_t pos = 0; pos < str.size(); pos++)
    {
        if(isDividerFunc(str[pos]))
        {
            //Grab the past segment.
            std::string segment = str.substr(startOfSegment, (pos - startOfSegment));
            if(!keepWhitespacePadding)
            {
                segment = String::RemovePadding(segment);
            }

            if(keepEmptySegments || !segment.empty())
            {
                stringList.push_back(segment);
            }

            //If we aren't keeping empty segments, speedily check for multiple seperators in a row.
            if(!keepEmptySegments)
            {
                //Keep looping until we don't find a divider.
                do
                {
                    //Increment and mark this as the (potential) beginning of a new segment.
                    startOfSegment = ++pos;

                    //Check if we've reached the end of the string.
                    if(pos >= str.size())
                    {
                        break;
                    }
                }
                while(isDividerFunc(str[pos]));
            }
            else
            {
                //Mark the beginning of a new segment.
                startOfSegment = (pos + 1);
            }
        }
    }

    //The final segment.
    std::string lastSegment = str.substr(startOfSegment, (str.size() - startOfSegment));
    if(keepEmptySegments || !lastSegment.empty())
    {
        stringList.push_back(lastSegment);
    }

    return stringList;
}

Где «StringList» — это определение типа std::vector, а CharValidatorFunc — это указатель на функцию (на самом деле, std::function для поддержки функтора и лямбда-выражения) для функции берет один символ и возвращает логическое значение. его можно использовать так:

StringList results = String::Seperate(" Meow meow , Green, \t\t\nblue\n   \n, Kitties!", ',' /* delimeter */, DefaultFlags, is_whitespace);

И вернет результаты: {"Мяу-мяу", "Зеленый", "Синий", "Котята!"}

Сохранение внутреннего пробела «Мяу-мяу», но удаление пробелов, табуляций и новых строк, окружающих переменные, и разделение запятыми.

(CV_IS — это функторный объект для сопоставления определенного символа или определенного набора символов, взятых как строковый литерал. У меня также есть CV_AND и CV_OR для объединения функций проверки символов)

Для строкового литерала я бы просто бросил его в std::string(), а затем передал его функции, если только не требуется экстремальная производительность. Нарушение разделителей довольно легко сделать самостоятельно — вышеприведенная функция просто настроена в соответствии с типичным использованием и требованиями моих проектов, но не стесняйтесь изменять ее и требовать ее для себя.

person Jamin Grey    schedule 20.03.2013

На случай, если это огорчит кого-то еще, вот как я решил проблему:

//variables for the input and arguments
char *command[2];
char *ptr;
char *LeftArg[3];
char *RightArg[3];

char buf[1024]; //input buffer

//parse left and right of the ||
number = 0;
command[0] = strtok(buf, "||");

//split left and right
while((ptr=strtok(NULL, "||")) != NULL)
{
    number++;
    command[number]=ptr;
}

//parse the spaces out of the left side
number = 0;
LeftArg[0] = strtok(command[0], " ");

//split the arguments
while((ptr=strtok(NULL, " ")) != NULL)
{
    number++;
    LeftArg[number]=ptr;
}

//put null at the end of the array
number++;
LeftArg[number] = NULL;

//parse the spaces out of the right side
number = 0;
RightArg[0] = strtok(command[1], " ");

//split the arguments
while((ptr=strtok(NULL, " ")) != NULL)
{
        number++;
        RightArg[number]=ptr;
}

//put null at the end of the array
number++;
RightArg[number] = NULL;

Теперь вы можете использовать LeftArg и RightArg в команде, после того, как правильно настроите трубопровод.

execvp(LeftArg[0], LeftArg);//execute left side of the command

Затем перейдите к правой стороне команды и выполните

execvp(RightArg[0], RightArg);//execute right side of command
person KateMak    schedule 20.03.2013
comment
Обратите внимание, что это не полный рабочий код моей программы. Это просто здесь, чтобы показать вам, как сделать часть в моем вопросе. - person KateMak; 20.03.2013
comment
Отчасти это было, да. Другой частью было любопытство. - person KateMak; 24.03.2013