W swojej książce The Practice of Programming (która jest warta przeczytania), Kernighan i Pike omawiają ten problem i rozwiązują go, używając snprintf()
do stworzenia łańcucha z odpowiednim rozmiarem bufora do przekazania do scanf()
rodziny funkcji. W efekcie:
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);
}
Uwaga, to nadal ogranicza dane wejściowe do rozmiaru podanego jako „bufor”. Jeśli potrzebujesz więcej miejsca, musisz dokonać alokacji pamięci lub użyć niestandardowej funkcji bibliotecznej, która alokuje pamięć za Ciebie.
Należy zauważyć, że POSIX 2008 (2013) wersja scanf()
rodziny funkcji obsługuje modyfikator formatu m
(o charakterze przydział alokacji) dla wejść strunowych ( %s
, %c
, %[
). Zamiast pobierać char *
argument, pobiera char **
argument i przydziela niezbędną przestrzeń dla odczytywanej wartości:
char *buffer = 0;
if (sscanf(data, "%ms", &buffer) == 1)
{
printf("String is: <<%s>>\n", buffer);
free(buffer);
}
Jeśli sscanf()
funkcja nie spełnia wszystkich specyfikacji konwersji, cała pamięć, którą zaalokowała na potrzeby %ms
konwersji podobnych do tych , jest zwalniana, zanim funkcja zwróci.
buflen-1
- dziękuję. Następnie musisz się martwić o niepodpisany niedomiar (zawijanie do dość dużej liczby), stądif
test. Byłbym bardzo kuszony, aby zastąpić to znakiemassert()
lub zarchiwizować goassert()
przed tym,if
który uruchomi się podczas programowania, jeśli ktoś jest na tyle nieostrożny, aby podać 0 jako rozmiar. Nie przejrzałem dokładnie dokumentacji, co%0s
oznaczasscanf()
- test może być lepszy, jakif (buflen < 2)
.