Как токенизировать предложения с символами в C - PullRequest
1 голос
/ 09 февраля 2020

Я пытаюсь понять, как токенизировать команды Unix, но я не знаю, как обойти тот факт, что strtok() разделяет любой символ, который у вас есть в качестве разделителя. Например, strtok(string, ". ") удалит точку И пробел.

Строка, которую я пытаюсь токенизировать, может быть чем-то вроде ps aux( sort ( more, и есть пробелы до и после скобок.

Даже если я наберу strtok(string, "("), до или после слов все еще есть пробелы, и, очевидно, execvp() не распознает эти токены. Например,

ps aux 
 sort
 more

Ожидаемый вывод:

ps aux
sort
more

Существуют ли какие-либо другие функции, которые позволяют указывать c входные данные, например " ( ", для его разделения на токены?

Ответы [ 3 ]

2 голосов
/ 09 февраля 2020

Не используйте strtok для этого, это не правильный инструмент для точного разбора.

Вы можете использовать strspn() и strcspn() для сканирования строки на разделители без изменения строки.

Вот упрощенный пример c:

#include <stdio.h>
#include <string.h>

void parse_line(const char *buf) {
    int pos, len;

    for (pos = 0; buf[pos]; pos += len) {
        len = strspn(buf + pos, " \t\r\n");     // skip blanks
        if (len > 0) {
            continue;
        }
        len = strspn(buf + pos, "<>|&[]()");
        if (len > 0) {
            printf("operator %.*s\n", len, buf + pos);
            continue;
        }
        if (buf[pos] == '\'') {
            len = 1 + strcspn(buf + pos + 1, "'");
            if (buf[pos + len] != '\'') {
                printf("unterminated string: %.*s\n", len, buf + pos);
                break;
            }
            len += 1;
            printf("string: %.*s\n", len, buf + pos);
            continue;
        }
        if (buf[pos] == '\"') {
            len = 1 + strcspn(buf + pos + 1, "\"");
            if (buf[pos + len] != '\"') {
                printf("unterminated string: %.*s\n", len, buf + pos);
                break;
            }
            len += 1;
            printf("string: %.*s\n", len, buf + pos);
            continue;
        }
        len = strcspn(buf + pos, "\'\" \t\r\n<>|&[]()");
        printf("token: %.*s\n", len, buf + pos);
    }
}

int main() {
    char buf[128];

    while (fgets(buf, sizeof buf, stdin)) {
        parse_line(buf);
    }
    return 0;
}
0 голосов
/ 09 февраля 2020

Насколько мне известно, (ANSI) C не имеет более мощных инструментов, чем это, но если вы должны использовать его, вы можете попробовать его с библиотекой регулярных выражений, только вам, возможно, придется выполнить часть работы себя (я не знаю, например, имеет ли GNULib функциональность regex_replace_all).

Возможно, вы захотите взглянуть на this .

Инвентаризация библиотек regex. и больше об этом topi c можно также найти здесь .

PS: Это скорее комментарий, но у меня нет прав на его написание

0 голосов
/ 09 февраля 2020

Предполагается:

  • Вы хотите разбить линию на левой части (.
  • Левой части может предшествовать и / или следовать пробел. (Судя по вашим данным, между aux и следующей левой парой нет пробелов)

Тогда как насчет awk решения:

str="ps aux( sort ( more"
awk -F ' *\\( *' '{ for (i=1; i<=NF; i++) print $i}' <<< "$str"

Вывод:

ps aux
sort
more
  • Опция -F определяет разделитель поля ввода.
  • Шаблон ' *\\( *' - это регулярное выражение, которое сопоставляет левую часть с 0 или более пробелами перед и / или после него.

Если мое предположение неверно, пожалуйста, сообщите мне.

[РЕДАКТИРОВАТЬ]

Если вы предпочитаете C решение, следующий код поможет при запуске:

#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int
main(void)
{
    regex_t    preg;
    char       *string = "ps aux( sort ( more";
    char       *pattern = " *\( *";    // regex of the delimiter
    char       out[256];               // output buffer
    int        rc;
    size_t     nmatch = 1;
    regmatch_t pmatch[1];

    // compile the regex
    if (0 != (rc = regcomp(&preg, pattern, 0))) {
        printf("regcomp() failed, returning nonzero (%d)\n", rc);
        exit(EXIT_FAILURE);
    }

    // loop while the regex of delimiter is found
    while (0 == (rc = regexec(&preg, string, nmatch, pmatch, 0))) {
        strncpy(out, string, pmatch[0].rm_so);  // copy the substring to print
        out[pmatch[0].rm_so] = 0;       // terminate the string
        printf("%s\n", out);
        string += pmatch[0].rm_eo;      // seek the pointer to the start of the next token
    }
    // print the last remaining portion
    if (strlen(string) > 0) {
        printf("%s\n", string);
    }
    regfree(&preg);
    return 0;
}

[Объяснение]
Если regexec() завершится успешно, он возвращает "начальную позицию соответствующей подстроки" в pmatch[0].rm_so и «рядом с конечной позицией совпадающей подстроки» в pmatch[0].rm_eo следующим образом:

1st call of regexec()
string:  ps aux( sort ( more
               ^ ^
           rm_so rm_eo

Мы можем интерпретировать их следующим образом: pmatch[0].rm_so содержит длину 1-го токена, а pmatch[0].rm_eo указывает начальную позицию следующего токена Затем мы обновляем переменные и вызываем 2-е regexec():

2nd call of regexec()
string:  sort ( more
             ^  ^
         rm_so  rm_eo

Мы повторяем l oop до тех пор, пока regexec() не вернет ненулевое значение, что означает отсутствие соответствия. Тогда последний токен останется в string.

...