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
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.
stdbool.h, mutta don ’ älä yritä kääntää omia boolejasi.