int n = sscanf("string", "%s %[^, ]%*[, ]%s", word1, word2, word3);
Возвращаемое значение в n
говорит вам, сколько назначений было выполнено успешно.%[^, ]
- это сопоставление класса символов с отрицанием, которое находит слово, не содержащее запятых или пробелов (добавьте вкладки, если хотите).%*[, ]
- это совпадение, которое находит запятую или пробел, но подавляет присвоение.
Я не уверен, что использовал бы это на практике, но оно должно работать.Это, однако, не проверено.
Возможно, более строгая спецификация:
int n = sscanf("string", "%s %[^, ]%*[,]%s", word1, word2, word3);
Разница в том, что класс не присваивающих символов принимает только запятую.sscanf()
останавливается в любом пробеле (или EOS, конец строки) после word2
и пропускает пробелы перед присвоением word3
.Предыдущее издание допускало пробел между вторым и третьим словами вместо запятой, чего вопрос строго не допускает.
Как указывает pmg в комментарии, присваивающие спецификации преобразования должныдать длину, чтобы предотвратить переполнение буфера.Обратите внимание, что длина не включает нулевой терминатор, поэтому значение в строке формата должно быть на единицу меньше размера массивов в байтах.Также обратите внимание, что, хотя printf()
позволяет динамически указывать размеры с помощью *
, sscanf()
и др. Используют *
для подавления назначения.Это означает, что вы должны создать строку специально для поставленной задачи:
char word1[20], word2[32], word3[64];
int n = sscanf("string", "%19s %31[^, ]%*[,]%63s", word1, word2, word3);
(Kernighan & Pike предлагает динамически форматировать строку формата в их (превосходной) книге 'Практика программирования' или Amazon Практика программирования 1999.)
Только что обнаружил проблему: учитывая "word1 word2 ,word3"
, он не читает word3
.Есть ли лекарство?
Да, есть лекарство, и оно тоже тривиально.Добавьте пробел в строке формата перед спецификацией преобразования без присвоения, с запятой.Таким образом:
#include <stdio.h>
static void tester(const char *data)
{
char word1[20], word2[32], word3[64];
int n = sscanf(data, "%19s %31[^, ] %*[,]%63s", word1, word2, word3);
printf("Test data: <<%s>>\n", data);
printf("n = %d; w1 = <<%s>>, w2 = <<%s>>, w3 = <<%s>>\n", n, word1, word2, word3);
}
int main(void)
{
const char *data[] =
{
"word1 word2 , word3",
"word1 word2 ,word3",
"word1 word2, word3",
"word1 word2,word3",
"word1 word2 , word3",
};
enum { DATA_SIZE = sizeof(data)/sizeof(data[0]) };
size_t i;
for (i = 0; i < DATA_SIZE; i++)
tester(data[i]);
return(0);
}
Пример вывода:
Test data: <<word1 word2 , word3>>
n = 3; w1 = <<word1>>, w2 = <<word2>>, w3 = <<word3>>
Test data: <<word1 word2 ,word3>>
n = 3; w1 = <<word1>>, w2 = <<word2>>, w3 = <<word3>>
Test data: <<word1 word2, word3>>
n = 3; w1 = <<word1>>, w2 = <<word2>>, w3 = <<word3>>
Test data: <<word1 word2,word3>>
n = 3; w1 = <<word1>>, w2 = <<word2>>, w3 = <<word3>>
Test data: <<word1 word2 , word3>>
n = 3; w1 = <<word1>>, w2 = <<word2>>, w3 = <<word3>>
После того, как «класс неназванного символа» принимает только запятую, вы можете сократить его до буквальной запятой встрока формата:
int n = sscanf(data, "%19s %31[^, ] , %63s", word1, word2, word3);
Подключение этого к тестовому жгуту дает тот же результат, что и раньше.Обратите внимание, что весь код выигрывает от просмотра;его часто (по сути, всегда) можно улучшить даже после того, как он заработает.