Despre mecanismul de utilizare a fgets() și stdin împreună (Programare, C, Stdin, Fgets)

Gareth Lam a intrebat.

Aș dori să înțeleg mai bine cum se utilizează fgets() și stdin.Următorul este codul meu:

int main()
{
    char inputBuff[6];
    while(fgets(inputBuff, 6, stdin))
    {
        printf("%s", inputBuff);
    }
    return 0;
}

Să spunem că intrarea mea este aaaabbbb și că apăs Enter. Prin utilizarea lui a loopcount, înțeleg că de fapt bucla va rula de două ori (inclusiv cea pe care am introdus-o eu aaaabbbb) înainte de următoarea intrare.

Bucla 1: După ce am tastat caracterele, aaaabbbb
vor fi stocate în memoria tampon de stdin fluxul de fișiere. Și fgets() va prelua un anumit număr de date din fluxul de fișiere și le va pune în inputBuff. În acest caz, se vor prelua 5 (6 – 1) caractere la un moment dat. Astfel, când fgets() a fost deja executat o dată, inputBuff va stoca aaaab, iar apoi va fi tipărit.

Bucla 2: Apoi, din moment ce bbb
sunt lăsate în fluxul de fișiere, fgets() se va executa pentru a doua oară, astfel încât inputBuff conține bbb
și apoi va fi tipărit.

Bucla 3: Programul îmi va cere datele de intrare (a doua oară) deoarece fluxul de fișiere a ajuns la sfârșit (EOF).

Întrebare: Se pare că fgets() îmi va cere introducerea de la tastatură numai după ce stdin fluxul nu mai are date în buffer (EOF). Mă întreb de ce nu aș putea să folosesc tastatura pentru a introduce ceva în bucla 2, iar fgets() doar să continui să recuperez 5 caractere din stdin în flux și să las datele în exces în fluxul de fișiere pentru următoarea recuperare. Am vreo neînțelegere cu privire la stdin sau fgets()? Vă mulțumesc pentru timpul acordat!

Comentarii

  • Nu este foarte clar în ce punct realitatea diferă de așteptările dumneavoastră. Poți arăta inputul care produce un output neașteptat? –  > Por n. „pronume” m..
  • Din manualul Fine: fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the buffer. A '' is stored after the last character in the buffer. –  > Por joop.
  • Vă așteptați la "bbb
    "
    să fie aruncată și să se solicite o nouă intrare? Dacă da, cum ar putea un program să citească o linie când lungimea ei nu era cunoscută dinainte? –  > Por Veleta meteo.
  • Ce vreți să spuneți mai exact cu „Mă întreb de ce nu am putut folosi tastatura pentru a introduce ceva în bucla 2, iar fgets() continuă să recupereze 5 caractere din fluxul stdin…”? Ați vrut să interpuneți ceva în mijlocul șirului anterior (înainte de bbb
    ), sau să aveți fgets citit după linia nouă, sau ce?  > Por ilkkachu.
  • Îmi pare rău băieți, am avut niște sarcini de codare de făcut, așa că nu pot să vă răspund imediat la comentarii. Îmi cer scuze că răspund atât de târziu. @n.m. Nu, nu am primit nicio ieșire neașteptată. Vreau doar să știu de ce „uneori” nu mi se cere să introduc ceva ori de câte ori este apelat fgets(). –  > Por Gareth Lam.
3 răspunsuri
chqrlie

Comportamentul programului dvs. este ceva mai subtil decât vă așteptați:

fgets(inputBuff, 6, stdin) citește cel mult 5 octeți din stdin și se oprește din citit atunci când primește un caracter newline, care este stocat în array-ul de destinație.

Prin urmare, așa cum corect diagnostichezi, la primul apel se citesc cei 5 octeți aaab și îi tipărește, iar al doilea apel citește 4 octeți bbb
și îi tipărește, apoi al treilea apel primește un flux de intrare gol și așteaptă intrarea utilizatorului.

Partea complicată este modul în care stdin primește datele de intrare de la utilizator, cunoscute și sub numele de intrare în consolă.

Atât intrarea în consolă, cât și stdin sunt, de obicei, stocate în mod implicit în memoria tampon de linie, astfel încât puteți tasta o linie completă de intrare, indiferent de dimensiunea memoriei tampon transmisă către fgets(). Cu toate acestea, dacă puteți seta stdin ca netamponată și intrarea de consolă ca netamponată, prima fgets() ar citi într-adevăr primii 5 octeți imediat ce îi tastați.

Intrarea în consolă este un subiect complicat. Iată un articol aprofundat despre mecanismele sale interne: https://www.linusakesson.net/programming/tty/

Comentarii

  • Informații foarte utile și detaliate! Vă mulțumim pentru răspuns! –  > Por Gareth Lam.
Achal

Totul este acolo în pagina de manual a fgets() ceea ce întrebi. Trebuie doar să o citiți corect, spune

char *fgets(char *s, int size, FILE *stream);

fgets() citește cel mult unul mai puțin decât sizecharacters de la stream și le stochează în bufferul indicat de s. Citirea se oprește după ce se produce o eroare de citire. EOF sau o linie nouă. Dacă se citește o linie nouă, aceasta este stocată în buffer. După ultimul caracter din buffer se stochează un octet nul de sfârșit (aqaq).

În cazul în care intrarea este aaaabbbb și în fgets() al doilea argument ați specificat dimensiunea ca 6 adică se va citi cu unul mai puțin 5 caracter și un caracter de terminare va fi adăugată, astfel încât prima dată inputBuff se păstrează aaaab și din moment ce încă EOF sau
nu a avut loc, deci data viitoare inputBuff ține bbb
ca linie nouă, de asemenea, este stocată în cele din urmă.

De asemenea, ar trebui să verificați tipul de returnare al fgets() și să verificați dacă
apare, apoi întrerupeți bucla. De exemplu

char *ptr = NULL;

while( (ptr = fgets(inputBuff, 6, stdin))!= NULL){
          if(*ptr == '
')
                   break;
          printf("%s", inputBuff);
}

Comentarii

  • Thx pentru că mi-ai reamintit că trebuie să adaug o condiție pentru a întrerupe bucla. „fgets() citește cel mult un caracter mai mic decât sizecharacters din stream și le stochează în bufferul indicat de s. Citirea se oprește după un EOF sau un newline.” Înseamnă că, de fapt, fgets() nu a terminat „primul” proces de citire a datelor de intrare după ce a stocat „aaaab” în buffer? Imediat după ce „bbb
    ” este citit, fgets() începe a doua cerere de intrare a utilizatorului. În acest caz, de ce următorul printf() poate fi executat chiar dacă fgets() nu s-a oprit din citit (ajungând la EOF sau la newline char)? –  > Por Gareth Lam.
  • @GarethLam fgets nu a terminat de citit întregul linie, dar a citit o parte din replică și are oprit în acest punct. Puteți printf partea citită până acum. În momentul următor, va începe să citească de unde a rămas. Dacă linia este mai lungă de 5 caractere, atunci programul dvs. nu poate fi în starea în care întreaga linie este citită și gata de a fi tipărită, deoarece pur și simplu nu există spațiu pentru a reține întreaga linie. Tot ce poate face cel mai bine, și chiar o face, este să citească liniile mai lungi bucată cu bucată. –  > Por n. „pronume” m..
  • @n.m. Se pare că nu am o bună înțelegere a fgets() și a stdin. Nu știam că trebuie să aștept după ce întreaga linie este citită de fgets() pentru a introduce noi caractere. Vă mulțumesc pentru comentariile dumneavoastră. Chiar am o înțelegere mai profundă în aceste 2 funcții. –  > Por Gareth Lam.
Aligator artistic

fgets() nu citește decât până când fie '
'
fie EOF. Tot ceea ce urmează după aceea va fi lăsat în stdin și, prin urmare, va fi citit atunci când veți apela fgets() din nou. Cu toate acestea, puteți elimina caracterele în exces din stdin de exemplu, folosind getc() până când ajungeți la ''. Ar fi bine să vă uitați în paginile de manual pentru asta.

Tags:, ,