Luen syötteen stdiniltä

Luin käyttäjän syötteen sivulta stdin tavallisella C. Ongelmana on, että haluan järkevän toteutuksen, joka on vankka virheille ja rajoittaa käyttäjän tiettyyn tuloon ja ei ime monimutkaisuuden kannalta. Funktio get_strings() lukee syötemerkin char niin kauan kuin uutta riviä ei ole (\n), ei EOF ja kaikki merkit ohittavat isalpha() testi. Mutta haluan pitää välilyönnit.

Jotkut (mielestäni) ansaitsevat erityistä huomiota tarkastelun aikana:

    – Haluaisin päästä eroon ulompi taas-silmukka, joka testaa periaatteessa sitä, onko käyttäjä juuri painanut Enter-näppäintä ilman mitään merkityksellistä syötettä.
    – Täytyykö minun edes käyttää ferror ()? fgetc palauttaa jo EOF: n, kun jokin meni pieleen, mutta aion vain lopeta lukeminen virrasta sen sijaan, että kerroit käyttäjälle jotain pieleen.
    – Sitä ei tapahdu aina, mutta se tapahtuu: A \ n pysyy stdin-virrassa ja ensi kerralla haluan saada mielekästä tietoa fgetc () vain ohitetaan. Sillä ei ole merkitystä tässä, mutta sillä on kysymys, kun kysyn yhden merkin kyllä / ei kysymyksiä. Voin päästä eroon tästä ongelmasta rakenteella, joka poistaa kaikki aikaisemmat stdiniin jääneet asiat. Katso tämän toinen koodilohko .

Joten käsittelen tätä täysin väärin? Onko olemassa parempia käytäntöjä omaksua? Se näyttää minulle todella kömpelöltä ja kömpelö on aina huono.

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

Toinen koodilukko yksinkertaisemmalla tapaus:

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

Kommentit

  • Nopea Huomaa: Käytä mieluummin boolenia kohteesta < stdbool.h > tai määrittelee sijasta 1 ja 0. Se ’ selkeämmin mitä tarkoitat
  • @Zorgatone Olen puoliksi samaa mieltä kanssasi; käytä aina stdbool.h, mutta don ’ älä yritä kääntää omia boolejasi.

Vastaa

arkkitehtuuri

stdin on yleensä -rivi puskuroitu . Joten mitään ei anneta fgetc(): lle, ennen kuin käyttäjä osuu Enter . OP-koodi antaa useita virheilmoituksia syötteellä, kuten ”Hei 123”. Parempi erottaa käyttäjän syöttö syötteen vahvistuksesta. Lue käyttäjän syöttämä rivi fgets() tai jollakin omalla versiollasi, sillä fgets(): llä on joitain heikkouksia. Sitten vahvista tulo.

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

Koskee ”Haluaisin päästä eroon ulkoisesta while-silmukasta”. Kyseinen silmukka kuluttaa hiljaa "\n". Jos haluat silmukan tekemään niin, edelsi vain sisäistä silmukkaa sanalla

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

char ch

ch ei ole paras tyyppi. fgetc() palauttaa tyypillisesti 257 erilaista arvoa [0-255] ja EOF. Jos haluat erottaa ne oikein, tallenna tulos int -kenttään.

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

Sama char tmp;

realloc()

Suoratoisto ei ole tarpeen.
Muodosta muisti loppuun vapauttamiseksi string – ei tarvita, jos koodi yksinkertaisesti poistuu, mutta hyvä käytäntö lelujen asettamiseen (koodi ”s-osoitin” poispäin.

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

Alla olevan sizeof(*strings) -osan hyvä käyttö. Suosittele yksinkertaistamista.

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

size_t len

size_t: n hyvä käyttö taulukon koon esittämiseen. Kummallista koodia ei tehdä samoin kuin int index;. Suosittele size_t index;

is...()

Kun käytetään int ch, suoratoistoa ei tarvita. Koska on looginen testi, suosittele ! käyttöä aritmeettisen sijasta == 0.

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

Seuraava voi olla helpommin ymmärrettävissä – vähemmän negatiivisia. (Tyyliongelma)

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

ferror()

Hieno tarkistus if (ferror(in_stream))

Muuttujien nimet

string, strings on yhtä hyödyllinen kuin kutsumalla kokonaisluku integer. Ehkä phrase, dictionary.

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

get_strings() on nimetty väärin. Se kuulostaa yleiseltä, mutta koodi rajoittaa syötteen kirjaimiin ja välilyöntiin.Ehkä get_words()?

Kommentit

  • Minusta tuntuu, että lähetit saman vastauksen kahdesti? Joka tapauksessa tämä on vastaus, jota etsin! Keskityin täysin fgetc sind -käyttöön. ’ nt toimi alussa (stdinissä olevien roistojen vuoksi). Tämä näyttää paljon paremmalta, ja sisällytän tämän koodiini. Kiitos!
  • @Haini tiedät, että hyväksynnän lisäksi voit myös äänestää vastausta, jos pidit siitä niin paljon 😉

Vastaa

Huono realloc-strategia

Tällä hetkellä soitat realloc() jokaiseen luettuasi merkkiin. Tästä seuraa \ $ O (n ^ 2) \ $ aika merkkijonon lukemiseen, koska joka kerta kun soitat realloc(), sen on ehkä kopioitava nykyinen sisältö uuteen puskuriin . Joko sinun tulisi joko allokoida puskuri, jonka koko on MAX_DATA, ja käyttää sitten realloc kutistamaan allokointi lopussa, tai vaihda uudelleenjako-strategiaksi missä uudelleenjaon kokoa kasvatetaan kerrannaiskertoimella joka kerta (kuten 2x).

Tämä koskee myös merkkijonojasi, jossa teet saman asian.

Outo sisennys

Sisääntulosi on outo, koska sisäkkäinen while -silmukka on samalla sisennystasolla kuin ulompi while -silmukka.

Käytetäänkö fittejä ()?

Käytän henkilökohtaisesti fgets() (tai muuta kirjastofunktiota, kuten readline()) merkkijonon lukemiseksi. fgets() tekee melkein mitä silmukka tekee ilman kaikkea käsin koodattua logiikkaa.

Vastaa

Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *