?
rednaZ schrieb:
Jürgen Wolf empfiehlt mir folgendes:
...und ich empfehle dir, dir ein anderes Buch zuzulegen. Von jemandem, der C kann. Der Code, den du da hast, wird, wenn die Zeile kurz genug ist, um vollständig in den Buffer zu gehen (also im Normalfall), die nächste Zeile ignorieren.
Was die Problemstellung angeht, so gibt es da verschiedene Herangehensweisen, abhängig davon, was für ein Verhalten du willst und wie wichtig Performance/Speicher sind. Wenn du den Rest der Zeile ignorieren willst und Performance keine Rolle spielt (das ist bei Benutzereingaben der Fall -- es steht nicht zu erwarten, dass man dir da gigabyteweise Daten in einem Tempo unterschiebt, dass du nicht mehr hinterherkommst), ist
if(fgets(buf, BUFSIZE, stdin) &&
buf[strlen(buf - 1)] != '\n')
{
while(fgetc(stdin) != '\n')
;
}
eine Möglichkeit. Wobei du dann in einem Fall einen String hast, der auf '\n' endet und einmal nicht, und wobei du natürlich Benutzereingaben teilweise verlierst. Performancemäßig ist das vor allem deshalb nicht so prall, weil fgetc jedes mal den Stream lockt, aber das dürfte in deinem speziellen Fall nicht so das Problem sein.
Wenn du GNU-Funktionen verwenden kannst, gibt es in der glibc eine Funktion getline, die das Problem ziemlich hübsch mit dynamischem Speicher löst. Das sieht etwa so aus:
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char *line = NULL;
size_t buflen = 0;
while(getline(&line, &buflen, stdin) && 0 != strcmp(line, "exit")) {
puts(line);
}
free(line);
}
getline fordert ausreichend Speicher an, um die Zeile aufzunehmen, und verwendet bei zukünftigen Durchläufen den Buffer wieder bzw. realloct sich einen größeren. Wenn du unter Linux unterwegs bist und dein Code vorläufig nicht anderswo laufen muss, ist das der bequemste Ansatz.
Ansonsten hab ich hier noch etwas alten Code rumliegen, der für den Zweck auch gehen sollte:
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *get_line(FILE *stream) {
size_t const CHUNKSIZE = 128;
size_t n = 0;
char *buf = NULL, *pos = NULL;
if(feof(stream)) return NULL;
do {
char *p;
n += CHUNKSIZE;
p = realloc(buf, n + 1);
if(p == NULL) {
free(buf);
return NULL;
}
buf = p;
pos = buf + n - CHUNKSIZE;
if(fgets(pos, CHUNKSIZE + 1, stream) == NULL) {
if(n > CHUNKSIZE && feof(stream)) {
return buf;
} else {
free(buf);
return NULL;
}
}
} while(pos[strlen(pos) - 1] != '\n');
return buf;
}
Diese Funktion erhöht nach und nach die Länge des Buffers, bis die Zeile in ihn hineinpasst. Im Gegensatz zu getline benutzt sie einen alten Buffer aber nicht wieder und lässt (wie fgets) das \n am Ende des Strings, so dass die benutzende Schleife in dem Fall beispielsweise so aussähe:
char *line;
int keep_going = 1;
while(keep_going && (line = get_line(stdin)) != NULL) {
if(strcmp(line, "exit\n") == 0) {
keep_going = 0;
} else {
puts(line);
}
free(line); // jede Zeile muss freigegeben werden, nicht nur einmal
// der dauernd wiederbenutzte Buffer.
}
Das ist im Vergleich zu getline suboptimal, aber wenn du keinen Zugriff auf GNU hast vertretbar.