LABORATORUL 03

Operații de intrare/ieșire pentru consolă

Durată: 2 ore Bibliotecă: stdio.h Precedent: Laboratorul 2 PDF îndrumar EN English version

În C, operațiile de intrare/ieșire nu fac parte din limbaj: nu există instrucțiuni dedicate, ci doar funcții adăugate prin biblioteca standard. Această separare oferă flexibilitate, dar mută pe umerii programatorului responsabilitatea de a preciza corect formatul datelor.

1Obiectivele lucrării

  • Înțelegerea conceptului de stream și a celor cinci dispozitive logice predefinite
  • Folosirea funcțiilor de citire/scriere la nivel de caracter
  • Citirea și afișarea șirurilor de caractere
  • Utilizarea corectă a specificatorilor de format în printf și scanf
  • Recunoașterea și evitarea capcanelor clasice ale funcției scanf

2Streamuri standard

tastaturaflux de caracterecintampon de intrarevariabilatip cunoscutapasarioperatorul >>ecrantext afisatcouttampon de iesirevariabilaoperatorul <<golire tampon
Fig. - Cele doua fluxuri standard. Datele nu trec direct de la tastatura la variabila: intre ele sta un tampon, care explica de ce caracterele ramase necitite afecteaza citirea urmatoare.

Transferurile cu dispozitivele fizice se fac prin dispozitive logice numite stream (flux). Există două categorii: streamuri de tip text, care transferă caractere organizate pe linii, și streamuri binare, care transferă octeți fără nicio structură impusă.

StreamTipAsociat cuPoate fi redirectat?
stdintexttastatura - intrarea standardda
stdouttextmonitorul - ieșirea standardda
stderrtextmonitorul - ieșirea pentru erorida
stdprnbinarimprimanta (dispozitiv DOS PRN)nu
stdauxbinarintrare/ieșire auxiliară (DOS AUX)nu
De ce există stderr separat de stdoutMesajele de eroare merg pe un flux distinct, astfel încât la redirectarea rezultatelor în fișier (program > rezultat.txt) erorile să apară în continuare pe ecran, nu să fie ascunse în fișier.

3Operații la nivel de caracter

FuncțieRolObservații
int getchar(void)citește un caracter din stdinașteaptă Enter; returnează EOF la sfârșit de fișier
int getch(void)citește un caracter fără ecounu așteaptă Enter; specific conio.h, neportabil
int getche(void)citește un caracter cu ecou pe ecrannu așteaptă Enter; neportabil
int putchar(int c)scrie un caracter în stdoutreturnează caracterul scris
De ce returnează int, nu chargetchar() trebuie să poată semnala sfârșitul fluxului prin valoarea EOF (de regulă −1), care nu încape în domeniul unsigned char. Dacă rezultatul este stocat într-un char, testul c != EOF poate să nu funcționeze niciodată.

4Operații cu șiruri de caractere

FuncțieRolRisc
gets(s)citește o linie întreagă, inclusiv spațiilepericuloasă - nu limitează lungimea; eliminată din standard
fgets(s, n, stdin)citește cel mult n−1 caracteresigură; păstrează caracterul '\n' la final
puts(s)afișează un șir și adaugă linie nouă-
scanf("%s", s)citește un cuvânt (se oprește la spațiu)nu poate citi propoziții cu spații
Regulă practicăFolosiți întotdeauna fgets în locul lui gets. Pentru a elimina caracterul de linie nouă rămas: s[strcspn(s, "\n")] = '\0';

5Intrare/ieșire cu format

Structura unui specificator

forma generală
%[flag][latime][.precizie][modificator]tip

   %-8.3f
   │││ │ └── tip: f = real
   ││└─┴──── precizie: 3 zecimale
   │└─────── latime minima: 8 caractere
   └──────── flag: - = aliniere la stanga
SpecificatorTip de dateExemplu de afișare
%d, %iint-42
%uunsigned int4294967254
%ffloat / double3.141593
%.2freal cu 2 zecimale3.14
%enotație științifică3.141593e+00
%ccharA
%sșir de caracteretext
%x, %Xhexazecimalff / FF
%ooctal377
%padresă (pointer)0x7ffd3a2c
%%caracterul procent literal%

6Explorator de specificatori

Alegeți un specificator și o valoare, apoi urmăriți exact ce afișează printf. Încercați combinații nepotrivite - de exemplu %c cu valoarea 65 - pentru a înțelege de ce compilatorul nu vă avertizează.

printf - specificator, valoare, rezultat

7Capcane la citire

1. Lipsa operatorului & la scanf scanf("%d", n); în loc de scanf("%d", &n); transmite valoarea variabilei în loc de adresa ei. Programul se compilează, dar se oprește brutal la execuție.
2. Caracterul Enter rămas în buffer După scanf("%d", &n);, tasta Enter rămâne necitită în stdin. Un scanf("%c", &c); imediat următor va prelua acel '\n', nu caracterul dorit. Soluție: scanf(" %c", &c); - spațiul din format sare peste caracterele albe.
3. Specificator nepotrivit printf("%d", 3.14); nu produce eroare de compilare, dar afișează o valoare fără sens, pentru că interpretează biții unui double ca pe un int.
4. Date de intrare nevalide Dacă utilizatorul tastează litere când se așteaptă un număr, scanf returnează 0 și lasă textul în buffer, ceea ce poate duce la buclă infinită. Verificați întotdeauna valoarea returnată: if (scanf("%d", &n) != 1) { ... }

8Cod sursă

caractere.c - numărarea caracterelor citite
#include <stdio.h>

int main(void)
{
    int c;                  // int, NU char, pentru a putea compara cu EOF
    int litere = 0, cifre = 0, spatii = 0, altele = 0;

    printf("Introduceti text (Ctrl+Z / Ctrl+D pentru final):\n");

    while ((c = getchar()) != EOF) {
        if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) litere++;
        else if (c >= '0' && c <= '9')                        cifre++;
        else if (c == ' ' || c == '\t' || c == '\n')          spatii++;
        else                                                  altele++;
    }

    printf("\nLitere: %d  Cifre: %d  Spatii: %d  Altele: %d\n",
           litere, cifre, spatii, altele);
    return 0;
}
citire_sigura.c - citire robustă
#include <stdio.h>
#include <string.h>

int main(void)
{
    char nume[50];
    int varsta;

    printf("Numele complet: ");
    fgets(nume, sizeof(nume), stdin);           // sigur: limiteaza lungimea
    nume[strcspn(nume, "\n")] = '\0';           // elimina linia noua

    printf("Varsta: ");
    while (scanf("%d", &varsta) != 1) {         // verifica reusita citirii
        printf("Valoare invalida. Reincercati: ");
        while (getchar() != '\n');              // goleste bufferul
    }

    printf("\n%-20s %3d ani\n", nume, varsta);
    return 0;
}
formatare.c - tabel aliniat
#include <stdio.h>

int main(void)
{
    printf("%-12s %8s %10s\n", "Produs", "Bucati", "Pret");
    printf("--------------------------------\n");
    printf("%-12s %8d %10.2f\n", "Rezistor",  120,  0.15);
    printf("%-12s %8d %10.2f\n", "Condensator", 45,  0.80);
    printf("%-12s %8d %10.2f\n", "Microcontroler", 8, 24.50);

    // acelasi numar, in baze diferite
    int v = 255;
    printf("\n%d in zecimal = %o in octal = %X in hexa\n", v, v, v);

    // afisarea unui procent literal
    printf("Progres: %d%%\n", 75);

    return 0;
}

9Atelier de cod

Formatarea afișării se învață experimentând. Schimbați numerele din specificatori (%8.3f, %-12s) și priviți cum se mișcă coloanele.

Specificatorii de format, văzuți la lucru
#include <stdio.h>

int main(void)
{
    int    n = 42;
    double x = 3.14159265;
    char   c = 'Q';
    char   s[] = "text";

    printf("[%d]      intreg simplu\n", n);
    printf("[%8d]  latime 8, aliniat la dreapta\n", n);
    printf("[%-8d]  latime 8, aliniat la stanga\n", n);
    printf("[%08d]  completat cu zerouri\n", n);

    printf("\n[%f]      implicit: 6 zecimale\n", x);
    printf("[%.2f]        doua zecimale\n", x);
    printf("[%10.3f]  latime 10, trei zecimale\n", x);

    printf("\n[%c] [%s] [%x in hexa] [%o in octal]\n", c, s, n, n);
    return 0;
}
Citire de la tastatură - exercițiu
#include <stdio.h>

int main(void)
{
    char   nume[30];
    int    varsta;
    double medie;

    printf("Nume: ");
    scanf("%s", nume);

    /* Cititi si varsta (intreg) si media (double).
       Atentie: la scanf, variabilele numerice au nevoie de & */

    printf("\n%-12s %3d ani   media %.2f\n", nume, varsta, medie);
    return 0;
}
De încercatCâmpul date de intrare de mai sus ține locul tastaturii: fiecare valoare de acolo este preluată de câte un scanf, în ordine. Într-un program real, utilizatorul le-ar tasta pe rând.

10Sarcini de lucru

  • Scrieți un program care citește caractere până la EOF și numără literele, cifrele și spațiile.
  • Citiți un nume complet (cu spații) folosind fgets și afișați-l cu puts.
  • Reproduceți capcana cu Enter rămas în buffer, apoi corectați-o cu " %c".
  • Afișați numărul 255 în zecimal, octal și hexazecimal, într-un singur printf.
  • Construiți un tabel aliniat cu trei coloane, folosind lățimi și alinieri explicite.
  • Implementați citirea robustă a unui întreg, cu reîncercare la date nevalide.
  • Testați ce afișează printf("%d", 3.14) și explicați rezultatul.

11Aplicație de aprofundare

ExtindereScrieți un mic „analizor de text": programul citește o propoziție și afișează numărul de cuvinte, cuvântul cel mai lung, numărul de vocale și de consoane, plus frecvența fiecărei litere sub formă de histogramă cu asteriscuri. Folosiți doar funcții din stdio.h și ctype.h.

12Întrebări de verificare

13Resurse