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
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.
stdbool.h
szót, de ne használd ‘ ne próbálja meggördíteni a saját booljait.