In ihrem Buch The Practice of Programming (das es wert ist, gelesen zu werden) diskutieren Kernighan und Pike dieses Problem und lösen es, indem sie snprintf()
den String mit der richtigen Puffergröße für die Übergabe an die scanf()
Funktionsfamilie erstellen . In der Tat:
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);
}
Beachten Sie, dass dies die Eingabe weiterhin auf die als "Puffer" angegebene Größe beschränkt. Wenn Sie mehr Speicherplatz benötigen, müssen Sie die Speicherzuweisung durchführen oder eine nicht standardmäßige Bibliotheksfunktion verwenden, die die Speicherzuweisung für Sie übernimmt.
Beachten Sie, dass die POSIX 2008 (2013) Version der scanf()
Familie von Funktionen ein Format Modifikator unterstützt m
(eine Zuweisung Zuweisungszeichen) für String - Eingänge ( %s
, %c
, %[
). Anstatt ein char *
Argument zu verwenden, wird ein char **
Argument verwendet und der erforderliche Speicherplatz für den gelesenen Wert zugewiesen:
char *buffer = 0;
if (sscanf(data, "%ms", &buffer) == 1)
{
printf("String is: <<%s>>\n", buffer);
free(buffer);
}
Wenn die sscanf()
Funktion nicht alle Konvertierungsspezifikationen erfüllt, wird der gesamte Speicher, den sie für %ms
ähnliche Konvertierungen zugewiesen hat, freigegeben, bevor die Funktion zurückkehrt.
buflen-1
- Danke. Sie müssen sich dann um einen nicht signierten Unterlauf (Umhüllung auf eine ziemlich große Anzahl) kümmern, daher derif
Test. Ich wäre sehr versucht, dies durch ein zu ersetzenassert()
oder es durch einassert()
vor demif
während der Entwicklung ausgelöstes zu sichern, wenn jemand nachlässig genug ist, um 0 als Größe zu übergeben. Ich habe die Dokumentation nicht sorgfältig geprüft, was dies%0s
bedeutetsscanf()
- der Test könnte besser sein alsif (buflen < 2)
.snprintf
schreibt einige Daten in einem String - Puffer undsscanf
liest von diesem erstellten Zeichenfolge. Wo genau ersetzt dies,scanf
indem es von stdin liest?snprintf
aber es ist nicht der eigentliche Formatparameter.data
und dahersscanf()
angemessen sind. Wenn Sie stattdessen von der Standardeingabe lesen möchten, löschen Sie dendata
Parameter und rufen Siescanf()
stattdessen auf. Bei der Auswahl des Namensformat
für die Variable, die die Formatzeichenfolge im Aufruf von wirdsscanf()
, können Sie diese bei Bedarf umbenennen, der Name ist jedoch nicht ungenau. Ich bin mir nicht sicher, welche Alternative Sinn macht. würdein_format
es klarer machen? Ich habe nicht vor, es in diesem Code zu ändern. Sie können, wenn Sie diese Idee in Ihrem eigenen Code verwenden.scanf()
auf macOS ist nicht als unterstützend dokumentiert%ms
, obwohl es nützlich wäre.Wenn Sie gcc verwenden, können Sie den GNU-Erweiterungsspezifizierer verwenden
a
, damit scanf () Speicher zuweist, damit Sie die Eingabe halten können:int main() { char *str = NULL; scanf ("%as", &str); if (str) { printf("\"%s\"\n", str); free(str); } return 0; }
Bearbeiten: Wie Jonathan betonte, sollten Sie die
scanf
Manpages konsultieren, da der Bezeichner möglicherweise anders ist (%m
) und Sie möglicherweise bestimmte Definitionen beim Kompilieren aktivieren müssen.quelle
m
Modifikator bereitstellt , um denselben Job auszuführen. Siehescanf()
. Sie müssen überprüfen, ob die von Ihnen verwendeten Systeme diesen Modifikator unterstützen.%ms
. Die Notation%a
ist ein Synonym für%f
(bei der Ausgabe werden hexadezimale Gleitkommadaten angefordert). Die GNU-Manpage fürscanf()
lautet: _ Es ist nicht verfügbar, wenn das Programm mitgcc -std=c99
oder gcc -D_ISOC99_SOURCE kompiliert wurde (sofern nicht anders_GNU_SOURCE
angegeben). In diesem Falla
wird das als Bezeichner für Gleitkommazahlen interpretiert (siehe oben) ._Meistens eine Kombination aus
fgets
undsscanf
erledigt den Job. Die andere Sache wäre, einen eigenen Parser zu schreiben, wenn die Eingabe gut formatiert ist. Beachten Sie auch, dass Ihr zweites Beispiel einige Änderungen erfordert, um sicher verwendet zu werden:#define LENGTH 42 #define str(x) # x #define xstr(x) str(x) /* ... */ int nc = scanf("%"xstr(LENGTH)"[^\n]%*[^\n]", array);
Mit dem obigen Befehl wird der Eingabestream bis zum Zeichen newline (
\n
) verworfen, jedoch nicht . Sie müssen ein hinzufügengetchar()
, um dies zu verbrauchen. Überprüfen Sie auch, ob Sie das Ende des Streams erreicht haben:if (!feof(stdin)) { ...
und das war's auch schon.
quelle
feof
Code in einen größeren Kontext stellen? Ich frage, da diese Funktion oft falsch verwendet wird.array
muss seinchar array[LENGTH+1];
Die direkte Verwendung
scanf(3)
und ihre Varianten werfen eine Reihe von Problemen auf. In der Regel werden Benutzer und nicht interaktive Anwendungsfälle in Form von Eingabezeilen definiert. Es kommt selten vor, dass mehr Zeilen das Problem lösen, wenn nicht genügend Objekte gefunden werden. Dies ist jedoch der Standardmodus für scanf. (Wenn ein Benutzer nicht wusste, dass er eine Nummer in die erste Zeile eingeben soll, helfen eine zweite und eine dritte Zeile wahrscheinlich nicht.)Zumindest wenn Sie
fgets(3)
wissen, wie viele Eingabezeilen Ihr Programm benötigt und Sie keine Pufferüberläufe haben ...quelle
Das Begrenzen der Länge der Eingabe ist definitiv einfacher. Sie können eine beliebig lange Eingabe akzeptieren, indem Sie eine Schleife verwenden, jeweils ein Stück einlesen und den Speicherplatz für die Zeichenfolge nach Bedarf neu zuweisen ...
Aber das ist eine Menge Arbeit, so dass die meisten C-Programmierer die Eingabe einfach in beliebiger Länge abschneiden. Ich nehme an, Sie wissen das bereits, aber mit fgets () können Sie keine beliebigen Textmengen akzeptieren - Sie müssen immer noch ein Limit festlegen.
quelle
realloc()
Sie einfach Ihren Puffer.Es ist nicht so viel Arbeit, eine Funktion zu erstellen, die den benötigten Speicher für Ihre Zeichenfolge zuweist. Das ist eine kleine C-Funktion, die ich vor einiger Zeit geschrieben habe. Ich benutze sie immer, um Strings einzulesen.
Es wird die gelesene Zeichenfolge zurückgegeben oder wenn ein Speicherfehler auftritt, NULL. Beachten Sie jedoch, dass Sie Ihre Zeichenfolge freigeben () und immer auf den Rückgabewert prüfen müssen.
#define BUFFER 32 char *readString() { char *str = malloc(sizeof(char) * BUFFER), *err; int pos; for(pos = 0; str != NULL && (str[pos] = getchar()) != '\n'; pos++) { if(pos % BUFFER == BUFFER - 1) { if((err = realloc(str, sizeof(char) * (BUFFER + pos + 1))) == NULL) free(str); str = err; } } if(str != NULL) str[pos] = '\0'; return str; }
quelle
sizeof (char)
ist per Definition1
. Du brauchst es hier nicht.strerror(3)
) zurückgeben oder eine vorab zugewiesene Zeichenfolge erwarten (wie (strerror_r(3)
- oderscanf(3)
) ...