Работа со строками с использованием strtok/sscanf в C

Я пытаюсь разделить следующую строку на три отдельные переменные, то есть a, b и c.:

"   mov/1/1/1,0 STR{7}, r7" 

каждый должен содержать другой сегмент строки, например:

a = "mov/1/1/1,0"
b = "STR{7}"
c = "r7"

Между каждой командой может быть пробел или табуляция; это то, что делает эту часть кода более сложной.

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

char command[50] = "    mov/1/1/1,0 STR{7}, r7";
char a[10], b[10], c[10];
char * ptr = strtok(command, "\t");
strcpy(a, ptr);
ptr = strtok(NULL, "\t");
strcpy(b, ptr);
ptr = strtok(NULL, ", ");
strcpy(c, ptr);

но это делает вещи действительно беспорядочными, поскольку переменные a, b и c получают больше значений, чем должны, что приводит к сбою программы.

Ввод может варьироваться от:

"   mov/1/1/1,0 STR{7}, r7"
"jsr /0,0            PRTSTR"
"mov/1/1/0,0         STRADD{5}, LASTCHAR {r3} "

в котором значения a, b и c изменяются на другую часть данной строки.

Мне сказали, что для таких манер безопаснее использовать sscanf, чем strtok, но я не уверен, почему и как это может мне помочь.

Буду более чем рад услышать ваше мнение!


person Ron Nuni    schedule 17.08.2013    source источник
comment
mov/1/1/0,0 и LASTCHAR {r3} Это будет длиннее девяти символов (a[10], c[10]).   -  person BLUEPIXY    schedule 18.08.2013
comment
Вы говорите, что хотите, чтобы вещи были разделены пробелом или табуляцией, но затем вы используете strtok только с "\t" в качестве разделителя. Возможно, вам просто нужны " \t" (пробелы или табуляции, как вы говорите), хотя в качестве разделителя будет использоваться любая последовательность из одного или нескольких пробелов или табуляции. Если вы хотите, чтобы два пробела означали «пустое» поле, это не сработает.   -  person Chris Dodd    schedule 18.08.2013
comment
Ваша примерная строка "jsr /0,0 PRTSTR" бросает гаечный ключ в работу; у него во втором поле есть значащая запятая, тогда как в двух других строках примеров запятая во втором поле незначительна. Если вам нужно удалить конечные запятые, вы можете сделать это после разделения на основе пробелов.   -  person Jonathan Leffler    schedule 18.08.2013


Ответы (3)


Это должно помочь:

sscanf(command, "%s,%s,%s", &a, &b, &c)

На странице руководства scanf %s съедает пробелы, будь то пробелы или табуляции:

s : соответствует последовательности непробельных символов; следующий указатель должен быть указателем на массив символов, длина которого достаточна для хранения входной последовательности и завершающего нулевого байта ('\0'), который добавляется автоматически. Входная строка останавливается на пробеле или на максимальной ширине поля, в зависимости от того, что произойдет раньше.

person ydache    schedule 17.08.2013
comment
Это включает запятую во второй строке, которая, по-видимому, не является желаемой целью OP. Возможно, обновите спецификацию формата, чтобы учесть это. - person WhozCraig; 18.08.2013
comment
Спасибо, в некоторых случаях это помогло, но в других могут быть табуляции или пробелы между каждой фразой, из-за чего sscanf не читает ее должным образом. кроме того, во второй строке он оставляет запятую в конце строки, которая не должна быть ее частью - person Ron Nuni; 18.08.2013
comment
Ребята, пробелы между каждой строкой могут меняться в разных командах, они варьируются от одного пробела до вкладок, это действительно усложняет задачу.. p.s. Я просто добавил несколько примеров заданных значений. - person Ron Nuni; 18.08.2013
comment
@RonNuni Если вы ищете универсальный спецификатор формата sscanf для всех этих форматов, возможно, вам придется потрудиться. Что касается того, что пытался передать этот конкретный ответ, попробуйте "%s %[^,], %s", но я бы серьезно рассмотрел наиболее надежный подход. - person WhozCraig; 18.08.2013
comment
@WhozCraig, спасибо, это действительно работает в большинстве случаев, но тот факт, что количество пробелов меняется при каждом запуске, доставляет мне трудности, потому что переменные a, b и c не инициализируются должным образом. - person Ron Nuni; 18.08.2013
comment
@RonNuni Тогда найдите что-то, что поможет вам приблизиться ко всем трем переменным, а затем испортите каждую из них. Со всеми вашими потенциальными форматами я не думаю, что существует серебряная пуля. Возможно, вам придется разобрать это трудным путем (иногда это просто нужно сделать). - person WhozCraig; 18.08.2013
comment
@WhozCraig, да, вы были правы в том, что мне действительно пришлось создавать свои собственные функции и обрабатывать большинство «событий» самостоятельно. Просто небольшой вопрос, как я могу взять небольшой кусок строки и инициализировать им переменную. например, из строки: mov/1/1/0,0 STRADD{5}, LASTCHAR {r3}... как я могу инициализировать переменную a с помощью mov? (бежать, пока я не достигну /)? - person Ron Nuni; 18.08.2013

Как вы, возможно, знаете, вы можете использовать sscanf() так же, как scanf(), разница в том, что sscanf сканирует из строки, а scanf из стандартного ввода.
В этой задаче вы можете указать scanf с набором символов, чтобы «всегда пропускать». ", как это сделано в этой ссылке.
Поскольку вы имеют различный набор ограничений для сканирования всех трех строк, вы можете указать, используя %*[^...], эти ограничения перед каждым %s внутри sscanf().

person Don't You Worry Child    schedule 17.08.2013

У меня есть сомнения по поводу использования strtok(), но этот код, похоже, делает то, что вам нужно. Как я отметил в комментарии, образец строки "jsr /0,0 PRTSTR" бросает вызов работе; у него во втором поле есть значащая запятая, тогда как в двух других строках примеров запятая во втором поле незначительна. Если вам нужно удалить конечные запятые, вы можете сделать это после разделения на основе пробелов, как показано в этом коде. Второй цикл проверяет функцию zap_trailing_commas(), чтобы убедиться, что она ведет себя в вырожденных случаях, удаляя конечные запятые, но не занижая начало буфера или что-то ужасное.

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

static void zap_trailing_commas(char *str)
{
    size_t len = strlen(str);
    while (len-- > 0 && str[len] == ',')
        str[len] = '\0';
}

static void splitter(char *command)
{
    char a[20], b[20], c[20];
    char *ptr = strtok(command, " \t");
    strcpy(a, ptr);
    zap_trailing_commas(a);
    ptr = strtok(NULL, " \t");
    strcpy(b, ptr);
    zap_trailing_commas(b);
    ptr = strtok(NULL, " \t");
    strcpy(c, ptr);
    zap_trailing_commas(c);
    printf("<<%s>> <<%s>> <<%s>>\n", a, b, c);
}

int main(void)
{
    char data[][50] =
    {
        "   mov/1/1/1,0 STR{7}, r7",
        "jsr /0,0            PRTSTR",
        "mov/1/1/0,0         STRADD{5}, LASTCHAR {r3} ",
    };

    for (size_t i = 0; i < sizeof(data)/sizeof(data[0]); i++)
        splitter(data[i]);

    char commas[][10] = { "X,,,", "X,,", "X,", "X" };
    for (size_t i = 0; i < sizeof(commas)/sizeof(commas[0]); i++)
    {
        printf("<<%s>> ", commas[i]);
        zap_trailing_commas(&commas[i][1]);
        printf("<<%s>>\n", commas[i]);
    }

    return 0;
}

Пример вывода:

<<mov/1/1/1,0>> <<STR{7}>> <<r7>>
<<jsr>> <</0,0>> <<PRTSTR>>
<<mov/1/1/0,0>> <<STRADD{5}>> <<LASTCHAR>>
<<X,,,>> <<X>>
<<X,,>> <<X>>
<<X,>> <<X>>
<<X>> <<X>>

Я также протестировал вариант с запятыми вместо крестиков, в котором осталась одна запятая.

person Jonathan Leffler    schedule 18.08.2013