Az input beolvasása a stdin-ről

A (z) stdin felhasználó bemenetét sima C. A probléma az, hogy olyan épelméjű megvalósítást szeretnék, amely robusztus a hibákra, és korlátozza a felhasználót egy bizonyos bemenetre, és nem bonyolult. A get_strings() függvény beolvassa a bemeneti karaktert char, amíg nincs új sor (\n), nincs EOF, és minden karakter áthalad a isalpha() teszt. De szeretnék helyet hagyni.

Néhány olyan pont, amelyre (szerintem) külön figyelmet érdemel a felülvizsgálat:

    – Szeretnék megszabadulni a A külső while hurok, amely alapvetően azt teszteli, hogy a felhasználó csak nyomja meg az Enter billentyűt érdemi bevitel nélkül.
    – Szükségem van-e még a ferror () használatára? Az fgetc már visszaadja az EOF-et, ha valami nem stimmelt, de én csak hagyja abba az olvasást a folyamról, ahelyett, hogy közölné a felhasználóval, hogy valami rosszul esett.
    – Ez nem történik meg mindig, de megtörténik: A \ n a stdin adatfolyamban marad, és amikor legközelebb értelmes információt szeretnék kapni Az fgetc () csak kihagyásra kerül. Itt nincs jelentősége, de igen, ha egyetlen karakterre teszek fel Igen / Nem kérdéseket. Csak egy konstrukcióval tudok megszabadulni ettől a problémától, amely kitisztítja az összes korábbi dolgot, ami maradt a stdin-ben. Lásd erről a második kódblokkot. .

Szóval, ezt teljesen rosszul kezelem? Vannak-e jobb gyakorlatok, amelyeket befogadhatok? Számomra ez nagyon ügyetlen, és mindig rossz.

/** @brief Contains the dictionary */ static char **strings = NULL; /** @brief Helps with containing the dicionary */ static char *string; /* Reads input char by char with fgetc() */ static char *get_strings() { char *string = NULL; char ch; size_t len = 0; while (string == NULL && ch != EOF) { while (EOF != (ch = fgetc(in_stream)) && ch != "\n") { if (ch != " " && isalpha((int)ch) == 0) { fprintf(stderr, "Only [a-z] is a valid input. | \t" "| Input another or end with CTRL+D: "); continue; } string = (char*) realloc(string, len+2); if (string == NULL) { bail_out(EXIT_FAILURE, "realloc(3) failed"); } string[len++] = toupper(ch); if (len >= MAX_DATA) { bail_out(EXIT_FAILURE, "Input too long\n"); } } if (ferror(in_stream)) { bail_out(EXIT_FAILURE, "Error while reading from stream"); } } if(string) { string[len] = "\0"; } else { printf("\nFinished dictionary...\n"); } printf("Added string: %s | Input another or end with CTRL+D: ", string); return string; } /* Saves the returned strings from get_strings() in a linked list */ static void read_dict() { int index; for (index = 0; (string = get_strings()); ++index) { if (string[0] == "\0") continue; strings = (char**) realloc(strings, (index+1)*sizeof(*strings)); if (strings == NULL) { bail_out(EXIT_FAILURE, "realloc(3) failed"); } strings[index] = string; } /* Take a note of how many entries we have yet. */ dict_size = index; } 

Második kódblokk egyszerűbb esettel:

while(1) { char tmp; printf("Please enter your guess [a-z]: "); guess = fgetc(stdin); /* Jump back to start of loop */ if (guess == "\n") { continue; } /* HERE IS THE CLEAR FOR STDIN This part really just eats all remaining \ns from the user, so that later inputs can start uninterrupted. Can I get rid of it in some better way? */ while((tmp = getchar()) != "\n" && tmp != EOF); if(!isalpha(guess)) { fprintf(stderr, "Enter a valid letter [a-z]!\n"); continue; } } 

Megjegyzések

  • Gyors megjegyzés: inkább használjon < stdbool.h > booleneket, vagy 1 és 0 helyett definiálja. ‘ érthetőbb, mire gondolsz
  • @Zorgatone Félig egyetértek veled; mindig használd a stdbool.h szót, de ne használd ‘ ne próbálja meggördíteni a saját booljait.

Válasz

Építészet

stdin általában sor pufferelt . Tehát semmit nem kap a fgetc(), amíg a felhasználó el nem nyomja az Enter gombot. Az OP kód több hibaüzenetet fog megadni, például “Hello 123” bemenettel. Jobb elkülöníteni a felhasználói bemenetet a bemeneti hitelesítéstől. Olvassa el a fgets() vagy a saját verziójának felhasználói beviteli sorát, mivel a fgets() tartalmaz néhány gyengeséget. Ezután ellenőrizze a bemenetet.

char *input; while ((input = my_gets()) != NULL) { if (valid_phrase(input)) { foo(input); } else { fprintf(stderr, "Invalid input\n"); } free(input); } 

A “Szeretnék megszabadulni a külső while ciklustól”. Ez a hurok azért létezik, hogy némán elfogyassza a "\n" -t. Ha azt szeretné, hogy egy hurok ezt megtegye, akkor csak a belső hurkot előzte meg a következővel:

int ch; while ((ch = fgetc()) == "\n") ; ungetc(ch, stdin); 

char ch

A ch nem a legjobb típus. A fgetc() általában 257 különböző értéket ad vissza [0-255] és EOF. A megfelelő megkülönböztetés érdekében mentse az eredményt egy int fájlba.

// bad char ch; .. while (string == NULL && ch != EOF) { while (EOF != (ch = fgetc(in_stream)) && ch != "\n") { // better int ch; .. while (string == NULL && ch != EOF) { while (EOF != (ch = fgetc(in_stream)) && ch != "\n") { 

Ugyanaz a char tmp;

realloc()

Az átküldésre nincs szükség.
A memória hiánya érdekében módosítsa, hogy szabaddá válhasson string – nem szükséges, ha a kód egyszerűen kilép, mégis jó gyakorlat a játékok (kód “s pointer” away.

// string = (char*) realloc(string, len+2); char * new_string = realloc(string, len+2); if (new_string == NULL) { free(string); bail_out(EXIT_FAILURE, "Out of memory"); } string = new_string; 

Az alábbi sizeof(*strings) megfelelő használata. Egyszerűsítés ajánlása.

strings = (char**) realloc(strings, (index+1)*sizeof(*strings)); strings = realloc(strings, sizeof *strings * (index+1)); 

size_t len

A size_t megfelelő használata egy tömb méretének képviseletében. Érdekes módon a kód nem ugyanezt teszi a int index; -vel. size_t index;

is...()

A int ch használatával nincs szükség leadásra. Mivel th Ez egy logikai teszt, javasoljuk a ! használatát az aritmetikai helyett == 0.

// if (ch != " " && isalpha((int)ch) == 0) { if (ch != " " && !isalpha(ch)) { 

A következők könnyebben érthetők – kevesebb tagadás. (Stílusprobléma)

if (!(ch == " " || isalpha(ch))) { 

ferror()

A if (ferror(in_stream))

szép ellenőrzése A változónevek

string, strings ugyanolyan hasznos, mint egy egész szám hívása integer. Talán phrase, dictionary.

// OK /** @brief Contains the dictionary */ static char **strings = NULL; // Better // comment not truly needed static char **dictionary = NULL; 

get_strings() elnevezése rossz. Úgy hangzik, hogy általános, de a kód betűkre és szóközre korlátozza a bevitelt.Lehet, hogy get_words()?

Megjegyzések

  • Úgy érzem, kétszer is ugyanazt a választ adta? Egyébként ez a válasz, amit kerestem! Teljesen az fgetc sind használatára összpontosítottam. A fgets ‘ nt az elején dolgozott (a stdinben lévő gazemberek miatt). Ez sokkal jobbnak tűnik, és ezt beépítem a kódomba. Köszönöm!
  • @Haini tudod, hogy az elfogadás mellett válaszon felül is szavazhatsz, ha annyira tetszett 😉

Válasz

Rossz átcsoportosítási stratégia

Jelenleg minden elolvasott karakternél felhívod az realloc() -t. Ez \ $ O (n ^ 2) \ $ időt eredményez egy karakterlánc elolvasásához, mert minden alkalommal, amikor az realloc() hívást kezdeményezi, előfordulhat, hogy az aktuális tartalmat át kell másolnia az új pufferbe . Vagy el kell osztania egy MAX_DATA méretű puffert, majd a realloc használatával csökkenteni kell a kiosztást a végén, vagy át kell állítani egy átcsoportosítási stratégiára ahol az átcsoportosítás méretét minden alkalommal multiplikatív tényező növeli (például 2x).

Ez vonatkozik a karakterláncok tömbjére is, ahol ugyanazt csinálja.

Furcsa behúzás

A behúzásod furcsa, mert a beágyazott while hurok ugyanazon a behúzási szinten van, mint a külső while hurok.

Használja a fgets () alkalmazást?

Személyesen használnám a fgets() -et (vagy valamilyen más könyvtárfunkciót, például readline()) karakterlánc elolvasásához. A fgets() nagyjából azt csinálja, amit a hurok minden kézzel kódolt logika nélkül végez.

Vélemény, hozzászólás?

Az email címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük