Čtení vstupu ze stdin

Čtení uživatelského vstupu z stdin v prostém C. Problém je v tom, že chci rozumnou implementaci, která je robustní vůči chybám a omezuje uživatele na určitý vstup a nesaje z hlediska složitosti. Funkce get_strings() čte vstupní znak char, pokud neexistuje žádný nový řádek (\n), žádný EOF a všechny znaky předávají isalpha() test. Ale chci zachovat mezery.

Některé body, které (myslím) si během kontroly zaslouží zvláštní pozornost:

    – Rád bych se zbavil vnější while smyčka, která v podstatě testuje, zda uživatel právě stiskl klávesu Enter bez jakéhokoli smysluplného vstupu.
    – Musím dokonce použít ferror ()? fgetc již vrátí EOF, když se něco pokazilo, ale já jen přestat číst ze streamu místo toho, abys uživateli sdělil, že se něco pokazilo.
    – Nestane se to pokaždé, ale stane se to: \ n zůstane ve stdin streamu a příště chci získat smysluplný vstup fgetc () je právě přeskočeno. Tady na tom nezáleží, ale záleží na tom, když kladu otázky s jedním znakem Ano / Ne. Tohoto problému se můžu zbavit pouze pomocí konstruktu, který vymaže všechny předchozí věci, které zůstaly v stdin. Viz druhý kódový blok .

Takže s tím zacházím naprosto špatně? Existují lepší postupy, jak je přijmout? Vypadá to pro mě opravdu neohrabaně a neohrabaně vždycky špatně.

/** @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; } 

Druhý CodeBlock s jednodušším případem:

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; } } 

Komentáře

  • Rychlé poznámka: raději použijte boolens z < stdbool.h > nebo definuje spíše než 1 a 0. ‚ je jasnější, co máte na mysli
  • @Zorgatone s vámi napůl souhlasím; vždy používejte stdbool.h, ale ne ‚ nezkoušejte své vlastní booly.

odpověď

Architektura

stdin je obvykle řádek ve vyrovnávací paměti . fgetc() se tedy nic nedává, dokud uživatel stiskne Enter . OP kód poskytne více chybových zpráv se vstupem jako „Hello 123“. Lepší je oddělit vstup uživatele od ověření vstupu. Přečtěte si řádek vstupu uživatele s fgets() nebo s nějakou vlastní verzí, protože fgets() má některé slabiny. Potom ověřte vstup.

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

Pokud jde o „Rád bych se zbavil vnější smyčky while“. Tato smyčka existuje, aby tiše spotřebovala "\n". Pokud chcete, aby to smyčka udělala, předcházela vnitřní smyčka pomocí

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

char ch

ch není nejlepší typ. fgetc() vrací obvykle 257 různých hodnot [0-255] a EOF. Chcete-li je správně rozlišit, uložte výsledek do int.

// 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") { 

Totéž pro char tmp;

realloc()

Cast není potřeba.
Upravte nedostatek paměti a uvolněte string – není potřeba, pokud se kód jednoduše ukončí, přesto je dobré umístit své hračky (kód „s ukazatel) pryč.

// 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; 

Dobré využití sizeof(*strings) níže. Doporučte zjednodušení.

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

size_t len

Dobré použití size_t k reprezentaci velikosti pole. Kupodivu kód nedělá totéž s int index;. Doporučit size_t index;

is...()

Při použití int ch není potřeba obsazení. is je logický test, doporučujeme použít ! místo aritmetického == 0.

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

Následující text může být srozumitelnější – méně negací. (Vydání stylu)

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

ferror()

Pěkná kontrola if (ferror(in_stream))

Názvy proměnných

string, strings jsou stejně užitečné jako volání celého čísla integer. Možná místo toho phrase, dictionary.

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

get_strings() je nesprávně pojmenován. Zní to obecně, ale kód omezuje vstup na písmena a mezeru.Možná get_words()?

Komentáře

  • Mám pocit, že jste zveřejnili stejnou odpověď dvakrát? Každopádně, toto je odpověď, kterou jsem hledal! Byl jsem úplně zaměřen na používání fgetc sind fgets nepracoval ‚ na začátku (kvůli nepoctivým ve stdin). To se zdá být mnohem lepší a zahrnu to do svého kódu. Děkujeme!
  • @Haini víte, že kromě přijetí můžete také hlasovat pro odpověď, pokud se vám moc líbila 😉

odpověď

Špatná strategie realloc

V současné době voláte realloc() na každý znak, který přečtete. To má za následek \ $ O (n ^ 2) \ $ čas na přečtení řetězce, protože pokaždé, když zavoláte realloc(), bude možná nutné zkopírovat aktuální obsah do nové vyrovnávací paměti . Měli byste buď jen přidělit vyrovnávací paměť velikosti MAX_DATA a poté pomocí realloc na konci přidělení zmenšit, nebo změnit strategii přerozdělení kde velikost realokace se pokaždé zvětší o multiplikativní faktor (například 2x).

To platí také pro vaše pole řetězců, kde děláte totéž.

Zvláštní odsazení

Vaše odsazení je zvláštní, protože vaše vnořená while smyčka je na stejné úrovni odsazení jako vnější while smyčka.

Použít fgets ()?

Osobně bych použil fgets() (nebo nějakou jinou knihovní funkci, například readline()) pro čtení řetězce. fgets() dělá téměř to, co vaše smyčka, bez veškeré ručně kódované logiky.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *