У своїй книзі "Практика програмування" (яку варто прочитати) Керніган та Пайк обговорюють цю проблему і вирішують її, використовуючи snprintf()
для створення рядка з правильним розміром буфера для передачі до scanf()
сімейства функцій. В ефекті:
int scanner(const char *data, char *buffer, size_t buflen)
{
char format[32];
if (buflen == 0)
return 0;
snprintf(format, sizeof(format), "%%%ds", (int)(buflen-1));
return sscanf(data, format, buffer);
}
Зауважте, це все ще обмежує вхідні дані розміром, що надається як "буфер". Якщо вам потрібно більше місця, то вам доведеться розподілити пам’ять або скористатися нестандартною функцією бібліотеки, яка виконує розподіл пам’яті за вас.
Зверніть увагу , що версія POSIX 2008 (2013) з scanf()
сімейства функцій підтримує модифікатор формату m
(характер присвоєння розподілу) для строкових входів ( %s
, %c
, %[
). Замість того, щоб приймати char *
аргумент, він бере char **
аргумент і виділяє необхідний простір для значення, яке читає:
char *buffer = 0;
if (sscanf(data, "%ms", &buffer) == 1)
{
printf("String is: <<%s>>\n", buffer);
free(buffer);
}
Якщо sscanf()
функція не може задовольнити всі специфікації перетворення, то вся пам'ять, яку вона виділила для %ms
подібних перетворень, звільняється до повернення функції.
buflen-1
- Дякую. Тоді вам доведеться турбуватися про неповноцінне заповнення (обгортання до досить великої кількості), отже, іif
тест. Я б дуже спокусився замінити це наassert()
або, або підкріпити його наassert()
перед тим,if
що спрацює під час розробки, якщо хтось досить необережний, щоб передати 0 як розмір. Я не ретельно переглядав документацію щодо того, що%0s
означаєsscanf()
- тест може бути кращимif (buflen < 2)
.