Использование strtok()
, чтобы разбить строку на отдельные токены.Это изменит исходную строку, но ее довольно просто реализовать:
int main(void)
{
char line[] = "John,100;passed";
char *name, *score, *status;
/* Detach the initial part of the line,
up to the first comma, and set name
to point to that part. */
name = strtok(line, ",");
/* Detach the next part of the line,
up to the next comma or semicolon,
setting score to point to that part. */
score = strtok(NULL, ",;");
/* Detach the final part of the line,
setting status to point to it. */
status = strtok(NULL, "");
Обратите внимание, что если вы измените char line[] = "John,100";
, тогда status
будет NULL
, но код в противном случае безопасен для запуска.
Итак, на практике, если требуется , чтобы все три поля существовали в line
, было бы достаточно убедиться, что последнее не было NULL
:
if (!status) {
fprintf(stderr, "line[] did not have three fields!\n");
return EXIT_FAILURE;
}
Используйте sscanf()
для преобразования строки.Например,
char line[] = "John,100";
char name[20];
int score;
if (sscanf(line, "%19[^,],%d", name, &score) != 2) {
fprintf(stderr, "Cannot parse line[] correctly.\n");
return EXIT_FAILURE;
}
Здесь 19
относится к числу символов в name
(один всегда зарезервирован для nul char в конце строки, '\0'
) и [^,]
- это преобразование строк, потребляющее все, кроме запятой.%d
конвертирует int
.Возвращаемое значение - это число успешных преобразований.
Этот подход не изменяет исходную строку и позволяет попробовать несколько различных шаблонов синтаксического анализа;если сначала вы попробуете их как можно более сложными, вы можете использовать несколько форматов ввода с небольшим количеством добавленного кода.Я делаю это регулярно, когда принимаю 2D или 3D векторы в качестве входных данных.
Недостатком является то, что sscanf()
(все функции семейства scanf) игнорирует переполнение.Например, в 32-разрядных архитектурах наибольшее значение int
равно 2147483647
, но функции scanf будут успешно преобразовывать, например, 9999999999
в 1410065407
(или какое-либо другое значение!), Не возвращая ошибку.Вы можете только предполагать, что числовые данные вменяются и находятся в определенных пределах;вы не можете проверить.
Используйте вспомогательные функции для токенизации и / или анализа строки.
Как правило, вспомогательные функции имеют вид, подобный
char *parse_string(char *source, char **to);
char *parse_long(char *source, long *to);
где source
- указатель на следующий символ в анализируемой строке, а to
- указатель, где будет сохранено проанализированное значение;или
char *next_string(char **source);
long next_long(char **source);
, где source
- указатель на указатель на следующий символ в анализируемой строке, а возвращаемое значение - это значение извлеченного токена.
Этикак правило, длиннее, чем выше, и если я написал, то довольно параноидально о входах, которые они принимают.(Я хочу, чтобы мои программы жаловались, если их входные данные не могут быть надежно проанализированы, вместо того, чтобы молча производить мусор.)
Единственный "трюк"запомнить символ разделителя, который завершил токен (для этого можно использовать ungetc()
), и использовать другую функцию для (чтения и игнорирования остальных токенов в текущей записи и) использования разделителя новой строки.