Hi,
beim Einlesen in Strings und Strings per Benutzereingabe bin ich auf
eine Frage gestoßen (die ich aber erst am Ende stellen werde :) ).
Ziel ist es, dass der User über die Tastatur einen String eingeben soll.
Das char Array beträgt 12 Elemente. Wenn der String > 12 Zeichen hat,
sollen nur die ersten 12 gespeichert werden - das ist OK (dynamische
Arraygröße etc wurde noch nicht behandelt).
Ich weiß, dass scanf() idR. nur ein einziges Wort einliest ("Gerd Gerd"
geht zB nicht, sondern hört beim ersten Gerd auf).
Daher gets(), der einen ganzen String einliest.
Sowohl scanf() und gets() haben das Risiko mit dem Bufferüberlauf, wenn
das char Array nicht groß genug für den String ist, und der Zeiger
anschließend im Nirvana landet.
Jetzt gibt es die Möglichkeit, fgets() zu verwenden, bei der man die
Arraygröße mit angeben kann. Es wird also nur bis zur Arraygröße
eingelesen. Weil im Input Line aber immer noch Zeichen vorhanden sein
können (wenn die Stringeingabe größer als das Array ist), kann man nicht
unmittelbar danach fgets() erneut verwenden für eine neue Eingabe.
Wie löst man das am geschicktesten/elegantesten?
Ich habe bin mit Hilfe des Buches (C Primer Plus) auf folgenden Ansatz
gekommen, und würde mich über Feedback freuen:
foobar schrieb:> return (c == EOF && i == 0) ? NULL : buf;
Warum i==0? Was passiert bei einem I/O-Error oder wenn die letzte Zeile
nicht mit '\n' aufhört?
Außerdem fehlen jede Menge { }. Das if am Ende könnte ich notfalls noch
durchgehen lassen, aber sowas macht nur ein Gefahrensucher:
> Warum i==0? Was passiert bei einem I/O-Error oder wenn die letzte Zeile> nicht mit '\n' aufhört?
Die Routine verhält sich wie ANSI gets: Wenn schon Zeichen gelesen
wurden, werden die auf jeden Fall zurückgegeben; NULL gibt's nur bei EOF
als erstem Zeichen.
foobar schrieb:> Die Routine verhält sich wie ANSI gets
Nichts ist so unbrauchbar, dass es nicht als schlechtes Beispiel dienen
könnte... Die Steigerung davon sind Funktionen, die mal und mal nicht
die '\0' mitliefern. Eigentlich sollte es ja längst (seit C11) gets_s()
geben, was ist denn daraus geworden?
>> Die Routine verhält sich wie ANSI gets>> Nichts ist so unbrauchbar, dass es nicht als schlechtes Beispiel dienen> könnte...
Nicht das ich die stdio-Routine gerne verteidige, aber dieses Verhalten
bzgl EOF ist in Ordnung. Es stellt sicher, dass keine Zeichen verloren
gehen (z.B. die von dir genannte "letzte Zeile ohne \n" wird geliefert).
Erkauft wird das damit, dass ein EOF oder I/O-Error evtl erst beim
nächsten Aufruf gemeldet wird.
...
Oh, gerade mal im avr-libc-1.8.0 nachgeschaut: da wird es falsch
behandelt, ein EOF macht augenblicklich ein "return NULL;" - die letzte
Zeile ohne \n geht verloren.
Bauform B. schrieb:> Nichts ist so unbrauchbar, dass es nicht als schlechtes Beispiel dienen> könnte... Die Steigerung davon sind Funktionen, die mal und mal nicht> die '\0' mitliefern.
Unter welchen Umständen wird denn kein '\0' angehängt?
LG, Sebastian
wangnick schrieb:> Bauform B. schrieb:>> Nichts ist so unbrauchbar, dass es nicht als schlechtes Beispiel dienen>> könnte... Die Steigerung davon sind Funktionen, die mal und mal nicht>> die '\0' mitliefern.>> Unter welchen Umständen wird denn kein '\0' angehängt?>> LG, Sebastian
Das ging erstmal gegen das alte gets() und dann, weil ich gerade am
meckern war, allgemein gegen Funktionen, die mal keine '\0' liefern.
foobar hätte halt gets() nicht erwähnen sollen ;) Sein ngets() ist ja
deutlich braver.
foobar schrieb:> Das "if" schützt vor size<=0
ach so, klar ;-)
foobar schrieb:> ohne das c==EOF würde auch eine> Leerzeile ein NULL generieren.
Ja, kompiziert.
Dieses "EOF" hat ja was mit Files zu tun. Und die Eingabe kann ja per
Tastatur aber auch per File erfolgen.
1
./my_program<input.txt
Und falls "input.txt" leer ist, dann gibt es Probleme. Dann werden die
Ausgaben
1
ngets(name,NAME_LEN);
2
printf("%s\n",name);
und
1
printf("%s\n",ngets(name,NAME_LEN));
untershiedlich sein. Wegen der letzten drei Zeilen.
zitter_ned_aso schrieb:> Dieses "EOF" hat ja was mit Files zu tun.
Du kannst EOF auch mit der Tastatur erzeugen Ctrl-D auf Linux und Ctrl-Z
auf Windows.
zitter_ned_aso schrieb:> Und falls "input.txt" leer ist, dann gibt es Probleme. Dann werden die> Ausgaben> ngets(name, NAME_LEN);> printf("%s\n", name);>> und printf("%s\n", ngets(name, NAME_LEN));>> untershiedlich sein.
Was soll da unterschiedlich sein?
> Dieses "EOF" hat ja was mit Files zu tun.
Kann evtl aber auch über die Tastatur simuliert werden, bei Linux z.B.
default-mäßig per CTRL-D.
> Dann werden die Ausgaben [...] untershiedlich sein.
Korrekt. Und zwar gewollt, um ein Ende der Datei überhaupt erkennen zu
können. Was sollte er sonst liefern? Unendlich viele Leerzeilen?
Die typische Nutzung sähe z.B. so aus:
1
charbuf[32];// short lines ...
2
inti=0;
3
4
printf("Anfang\n");
5
6
while(ngets(buf,sizeof(buf)))
7
printf("Zeile %d: '%s'\n",++i,buf);
8
9
printf("Ende\n");
> Wegen der letzten drei Zeilen.
Nur die letzte. Die beiden davor kümmern sich darum, dass der Buffer
immer schön 0-terminiert ist.
zitter_ned_aso schrieb:> Und falls "input.txt" leer ist, dann gibt es Probleme. Dann werden die> Ausgaben> ngets(name, NAME_LEN);> printf("%s\n", name);>> und printf("%s\n", ngets(name, NAME_LEN));>> untershiedlich sein.
Man könnte das auch als Feature betrachten und eher das
printf("%s\n", ngets(name, NAME_LEN));
als Problem. Nicht jedes printf() druckt "(null)" und es ist auch nicht
klar, ob das ein Feature ist.