>> Inapoi <<

==========
Capitolul 14
==========
================
Intrari/iesiri si fisiere
================
In acest capitol vom explica folosirea unor functii de intrare/iesire (printre care si "printf()" si "scanf()"). De asemenea vom arata modurile de deschidere a fisierelor (pentru procesarea lor) si cum se foloseste un pointer catre fisier.

-----------------------------------
Functia de iesire "printf()"
-----------------------------------
Are doua proprietati importante care permit o folosire flexibila la un nivel inalt:
  - poate fi tiparita o lista de argumente de lungime arbitrara;
  - afisarea este controlata de specificari de conversie simple (sau formate). 

Functia "printf()" primeste sirul de caractere din fisierul standard de iesire (stdout), care este normal conectat la ecran. Lista de argumente a lui "printf()" are doua parti:

      sirul_de_control      si     celelalte_argumente
-----------
Exemplu: In cazul apelului:
-----------      
          printf("Produsul %d %s $%f\n", 2, "costa", 3.77);
avem 
         sirul_de_control: "Produsul %d %s $%f\n"
    celelalte_argumente: 2, "costa", 3.77

Expresiile din 'celelalte_argumente' sunt evaluate si convertite conform cu 
formatele din sirul de control si apoi plasate in sirul de iesire. Caracterele
din sirul de control care nu fac parte dintr-un format sunt plasate direct in 
sirul de iesire. Simbolul % introduce o specificare de conversie sau format. 
 O specificare de conversie este un sir care incepe cu % si se termina cu un 
caracter de conversie.

Tabelul de mai jos reprezinta caracterele de conversie (si modul lor de afisare) pentru functia "printf()":

---------------------------------------------------------------------
   Caracter               Cum sunt afisate 
      de             argumentele corespunzatoare ?
   conversie
---------------------------------------------------------------------
     c              ca un caracter
     d, i           ca un intreg zecimal 
     u              ca un intreg zecimal fara semn
     o              ca un intreg octal fara semn
     x, X           ca un intreg hexazecimal fara semn
     e              ca un numar in virgula mobila; (ex: 7.123000e+00)
     E              ca un numar in virgula mobila; (ex: 7.123000E+00)
     f              ca un numar in virgula mobila; (ex: 7.123000)
     g              in formatul cel mai scurt dintre "e" sau "f"
     G              in formatul cel mai scurt dintre "E" sau "f"
     s              ca un sir
     p              argumentul corespunzator este un pointer catre void; valoarea sa se va tipari ca un intreg hexazecimal
     n              argumentul corespunzator este un pointer catre un intreg; argumentul nu este convertit
     %              cu formatul %% se va afisa un singur % catre sirul de iesire; nu avem argumente ce trebuie convertite 
---------------------------------------------------------------------
-----------
Exemplu: Iata un exemplu de folosire a formatului "%n":
-----------
                #include 

                void main()
                 {
                  int * pi; 
                  printf("Mai multe caractere %n.\n", pi);
                  printf("Nr.caractere = %d", *pi);
                 }
 
Pe ecran se va afisa numarul de caractere afisate pana la aparitia formatului 
"%n" (in cadrul instructiunii de afisare "printf()" curente), adica 20.  
Functia "printf()" intoarce un "int" ce reprezinta numarul de caractere 
tiparite dupa inlocuirea corespunzatoare a specificatorilor de conversie.
-----------
Exemplu: 
-----------
     #include 

     void main()
      {
       int * pi; 
       int a = printf("Mai mult de %d caractere %n.\n", 10, pi);
       printf("Numarul de caractere intors de functia printf() este %d\n", a);
       printf("Nr.caractere = %d", *pi);
      }

In specificarile de conversie pot fi incluse informatii de formatare explicite.
Daca ele nu apar, atunci sunt folosite cele implicite. De exemplu, formatul %f
cu argumentul 3.77 va afisa 3.770000. Numarul este afisat pe 6 caractere la 
dreapta punctului zecimal (implicit). Intre % (care specifica inceputul unei 
specificari de conversie) si caracterul de conversie de la sfarsit, pot apare 
in ordine:
   - zero sau mai multe caractere (flag) care modifica intelesul specificarii 
     de conversie;
   - un intreg pozitiv optional care specifica minimul lungimii campului a 
     argumentului convertit. Locul unde un argument este 
tiparit se numeste "campul sau", iar numarul de spatii folosit pentru afisarea
sa se numeste "lungimea campului". Daca argumentul convertit are mai putine 
caractere decat lungimea campului specificat, atunci acesta se va completa cu 
spatii (la stanga sau dreapta). Daca argumentul convertit are mai multe 
caractere decat lungimea campului specificat, atunci lungimea campului se va 
mari cu cat este necesar. Daca intregul care defineste lungimea campului incepe
cu zero si argumentul ce se tipareste este ajustat dreapta in campul sau, 
atunci pentru umplerea sa se vor folosi zerouri in loc de spatii;
   - o "precizie" optionala, care este specificata de un punct urmat de un 
intreg nenegativ. Pentru conversii d, i, o, u, x si X aceasta specifica numarul
minim de cifre ce trebuie afisate. Pentru conversii e, E si f aceasta specifica
numarul de cifre de la dreapta punctului zecimal. Pentru conversii g si G aceasta specifica numarul maxim de cifre semnificative. Pentru conversie cu s, aceasta 
specifica numarul maxim de caractere ce trebuie tiparite dintr-un sir;
   - un h sau l optional, care este un modificator "short" sau "long", 
respectiv. Daca h este urmat de un d, i, o, u, x sau X, atunci aceasta este o 
specificare de conversie relativ la "short int" sau "unsigned short int". Daca
un h este urmat de n, atunci argumentul corespunzator este un pointer catre 
"short int" sau "unsigned short int". Daca l este urmat de d, i, o, u, x sau X,
atunci specificarea de conversie se aplica unui argument "long int" sau 
"unsigned long int". Daca l este urmat de caracterul n, atunci argumentul
corespunzator este un pointer catre "long int" sau "unsigned long int"; 
   - L optional, care este un modificator "lung". Daca L este urmat de e, E, f,
g sau G, specificarea de conversie se aplica unui argument "long double".

Caracterele "flag" sunt:

   - semnul "-" va implica alinierea spre stanga a argumentului convertit in 
campul sau. Daca nu gasim nici un semn "-", atunci alinierea argumentului 
convertit se face la dreapta campului.
   - semnul "+" va implica afisarea semnului "+" in cazul numerelor pozitive 
(functioneaza pentru d, i, e, E, f, g si G). Toate numerele negative incep cu semnul minus.
   - un spatiu, care semnifica ca un numar pozitiv (ce provine dintr-o 
conversie unsigned) are la inceput un spatiu;
   - un "#", care semnifica ca rezultatul trebuie convertit la o forma 
alternativa ce depinde de caracterul de conversie. Daca 
caracterul de conversie este "o", atunci "#" cauzeaza afisarea unui zero in 
fata numarului octal. Intr-o conversie x sau X, "#" cauzeaza 0x, respectiv 0X,
in fata numarului hexazecimal. In conversiile g si G, acesta cauzeaza tiparirea 
unor zerouri la coada (pana la un anumit numar de zecimale, de obicei 5). In 
conversiile e, E, f, g sau G, aceasta cauzeaza tiparirea punctului zecimal, 
chiar si cu precizia 0. Pentru alte conversii, comportarea lui "#" este 
nedefinita.
   - un "0" (zero), care inseamna ca in loc de spatii sunt folosite zerouri. Cu
caracterele de conversie d, i, o, u, x, X, e, E, f, g si G, aceasta poate 
produce zerouri nesemnificative (in fata numarului).

Intr-un format, lungimea campului sau precizia (sau ambele), pot fi specificate folosind *, in loc de un intreg, lucru care indica ca o valoare se obtine dintr-o lista de argumente.
-----------
Exemplu:
-----------
             int m, n;
             double x = 333.7777777;
             . . . . . . . . . . . 
             /* citeste m si n (sau le calculam cumva) */
             . . . . . . . . . . . 
             printf("x = %*.*f\n", m, n, x);
             
 Daca argumentul corespunzator lungimii campului are lungime negativa, atunci se
considera "-" ca fiind un "flag" urmat de o valoare pozitiva. Daca argumentul 
corespunzator preciziei are valoare negativa, atunci acesta se considera ca 
lipseste.
 Tabelul de mai jos contine formate de caractere si siruri (folosim ghilimele pentru delimitarea vizuala a campului, ele nefiind 
afisate).
------------------------------------------------------------------------
Declaratii si initializari
------------------------------------------------------------------------
char c = 'A', s[] = "Luna rosie!";
------------------------------------------------------------------------
Format  Argumentul   Cum este afisat        Observatii
       corespunzator in campul sau ?
------------------------------------------------------------------------
%c           c        "A"             Lungimea campului 1 implicit
%2c          c        " A"            Lungimea campului 2, aliniat dreapta
%-3c         c        "A  "           Lungimea campului 3, aliniat stanga
%s           s        "Luna rosie!"   Lungimea campului 11 implicit
%3s          s        "Luna rosie!"   Spatii suplimentare
%.6s         s        "Luna r"        Precizia 6
%-11.8s      s        "Luna ros   "   Precizie 8, aliniere stanga
------------------------------------------------------------------------
In tabelul de mai jos vom da exemple de folosire a altor formate (avand aceeasi
 conventie de afisare cu ghilimele).
---------------------------------------------------------------------
Declaratii si initializari
---------------------------------------------------------------------
int i = 123;
double x = 0.123456789;
---------------------------------------------------------------------
Format  Argumentul   Cum este afisat        Observatii
       corespunzator in campul sau ?
---------------------------------------------------------------------
%d         i        "123"            Lungimea campului 3 implicit
%05d       i        "00123"          Adaugat 0-uri nesemnificative
%7o        i        "    173"        Aliniere dreapta, octal
%-9x       i        "7b       "      Aliniere stanga, hexazecimal
%-#9x      i        "0x7b     "      Aliniere stanga, hexazecimal
%10.5f     x        "   0.12346"     Lungimea campului 10, precizie 5
%-12.5e    x        "1.23457e-01  "  Aliniere stanga, format -e
---------------------------------------------------------------------

------------------------------------
Functia de intrare "scanf()"
------------------------------------
   Are doua proprietati importante care permit o folosire flexibila la un nivel inalt:
  - poate citi o lista de argumente de lungime arbitrara;
  - intrarea este controlata de specificari de conversie simple (sau formate).
Functia "scanf()" citeste caractere din fisierul standard "stdin". Lista de argumente a functiei "scanf()" are doua parti:

    sir_de_control     si     celelalte_argumente
    
-----------
Exemplu:  Fie declaratiile si apelul functiei "scanf()":
-----------
              char   a, b, c, s[100];
              int    n;
              double x;
              scanf("%c%c%c%d%s%lf", &a, &b, &c, &n, s, &x);
Avem:
        sir_de_control:      "%c%c%c%d%s%lf"
        celelalte_argumente: &a, &b, &c, &n, s, &x
Celelalte argumente urmate de un sir de control consta dintr-o lista separata 
prin virgule de expresii pointer sau adrese (reamintim ca "s" este insusi o 
adresa).

Directive in sirul de control
------------------------------------
Sirul de control din "scanf()" este compus din trei tipuri de "directive": 

        - caractere ordinare
        - spatii goale
        - specificari de conversie
        
Caractere ordinare
-------------------------
Caracterele din sirul de control (diferite de spatiile goale si caracterele din
specificarile de conversie) sunt numite "caractere ordinare". Caracterele 
ordinare trebuie sa se regaseasca (potriveasca) cu cele din sirul de la 
intrare.
-----------
Exemplu:      float suma;
-----------      scanf("$%f", &suma);

Caracterul $ este ordinar. Deci trebuie sa intalnim $ in sirul de la intrare. 
Daca are loc o potrivire cu succes, atunci spatiile goale (daca exista) se vor
sari, si caracterele care urmeaza se vor potrivi la o valoare (in virgula 
mobila). Valoarea convertita va fi plasata in memorie la adresa variabilei 
"suma".

Caractere "spatii goale"
--------------------------------
Caracterele spatii goale din sirul de control care nu fac parte dintr-o 
specificare de conversie se potrivesc cu orice spatiu liber din sirul de 
intrare. 
-----------
Exemplu:    char c1, c2, c3;
-----------    scanf(" %c %c %c", &c1, &c2, &c3);

Daca sirul de la intrare contine literele "a", "b", si "c", atunci "c1", "c2" 
si "c3" vor avea valorile "a", "b", "c" (a nu se citi ghilimelele). O directiva
spatiu liber implica ca spatiile goale (daca exista) sa fie ignorate din sirul 
de intrare.
-----------
Exemplu:  Urmatoarele instructiuni sunt echivalente:
-----------
                scanf(" %c %c %c", &c1, &c2, &c3);
                scanf("\t%c  \t  %c\n%c", &c1, &c2, &c3);
                
Specificari de conversie
------------------------------
Intr-un sir de control pentru "scanf()", o directiva de specificare de 
conversie incepe cu un "%" si se termina cu un caracter de conversie. Aceasta 
determina modurile de potrivire si de convertire a caracterelor din sirul de 
intrare (cele doua tabele de mai jos contin explicatii pentru functia 
"scanf()"): 
---------------------------------------------------------------------
 Caracter de    Caracterele din sirul de                   Tipul 
 conversie      intrare cu care se potrivesc            argumentului
nemodificabil                                           corespunzator    
---------------------------------------------------------------------
     c         orice caracter, inclusiv spatiu liber      char *
     d         un intreg zecimal (optional cu semn)       int *
     i         zecimal, octal, hexazecimal                int *
               (77, 077, 0x77, optional cu semn)          
     u         un intreg zecimal (optional cu semn)       unsigned *
     o         un intreg octal (optional cu semn)         unsigned * 
               cifra 0 nu mai este necesara         
   x, X        un intreg hexazecimal (optional cu semn)   unsigned *
               0x sau 0X nu mai sunt necesare
e, E, f, g, G  numar in virgula mobila (optional cu semn) float *
     s         o secventa de caractere diferite de spatiu char *
     p         ceea ce produce %p in "printf()"           void * *
               (de obicei intreg hexazecimal fara semn) 
n, %, [ . . .] (vezi urmatorul tabel)
---------------------------------------------------------------------

Observatie. Zecimal inseamna numar intreg scris in baza 10 (nu numar cu zecimale).      

---------------------------------------------------------------------------------------------------------------------------------------------
 Caracter de                       Observatii
 conversie      
nemodificabil   
---------------------------------------------------------------------------------------------------------------------------------------------
     n       Nu se potriveste nici un caracter din sirul de intrare. 
              Argumentul corespunzator este un pointer catre "int", in care se
              memoreaza numarul de caractere citite in acel "scanf()"
     %       Conversia de specificare %% va implica potrivirea cu un caracter %
              din sirul de intrare. Nu are argumente corespunzatoare. 
 [ . . . ]   Multimea caracterelor dintre paranteze se numeste  multime de 
              scanare. Aceasta determina ce se potriveste si face citirile 
              respective. Argumentul corespunzator este un pointer catre baza 
              sirului de caractere ce este suficient de mare pentru a pastra 
              caracterele cu care s-a potrivit, apoi va adauga automat 
              terminatorul '\0'.
---------------------------------------------------------------------------------------------------------------------------------------------
Intre % si caracterul de conversie poate fi:

  - caracterul * optional, care indica o suprascriere, urmata de un intreg 
    optional care defineste lungimea maxima a sirului de intrare (care va fi 
    deci ignorat), urmat optional de h, l, L care modifica caracterul de 
    conversie;
  - modificatorul h, care poate precede caracterele de conversie d, i, o, u, x
    sau X. Acesta precizeaza ca valoarea convertita trebuie memorata ca un 
    "short int" sau "unsigned short int";
  - modificatorul l, care poate precede caracterele de conversie d, i, o, u, x,
    X sau e, E, f, g, G. In primul caz, acesta precizeaza ca valoarea trebuie 
    memorata ca un "long int" sau "unsigned long int".
In cel de-al doilea caz, acesta precizeaza ca valoarea convertita trebuie 
 memorata ca un "double";
  - modificatorul L, care poate precede caracterele de conversie e, E, f,g sau
    G. Acesta precizeaza ca valoarea convertita trebuie memorata ca un "long 
    double".

Caracterele din sirul de intrare sunt convertite la valori in concordanta cu 
specificarile de conversie din sirul de control si plasate la adresa data prin
expresia pointer corespunzatoare din lista de argumente. Cu exceptia unei 
intrari caracter, un camp de scanare consta dintr-un numar contiguu de 
caractere diferite de spatiu (conforme cu conversia specificata). Campul de 
scanare se termina cand se gaseste un caracter neadecvat, sau s-a depasit 
lungimea scanarii (daca ea este precizata), sau s-a ajuns la EOF (depinde care
vine primul).

Specificarea %s sare spatiile goale si apoi citeste caractere diferite de 
spatiu pana cand se gaseste spatiu sau EOF (depinde care vine primul). In 
schimb, specificarea %5s sare spatiile goale, apoi citeste caracterele diferite
de spatiu, dar cel mult 5 caractere sau pana la EOF (depinde care vine primul).
Cand se citeste un sir de caractere, se presupune ca in memorie este deja 
rezervat suficient spatiu pentru memorarea sa (cu tot cu santinela \0). 

Formatul %nc (unde "n" este o constanta intreaga) foloseste la citirea 
urmatoarelor n caractere, inclusiv spatii goale (se presupune ca s-a rezervat 
suficient spatiu in memorie pentru pastrarea lor, iar caracterul \0 nu se mai 
adauga). 

Numere in virgula mobila din sirul de intrare
----------------------------------------------------------
Numerele in virgula mobila din sirul de intrare sunt formatate cu un semn 
optional (+ sau -) urmat de un sir de cifre cu un punct zecimal optional, urmat
de parte exponentiala optionala. Partea exponentiala consta din e sau E, urmate de un semn optional (+ sau 
-), urmat de un sir de cifre. 
-----------
Exemple:
-----------
                77     
                +7.7e1
                770.0E-1
                +0.003
                
Nu uitati: Sirul de intrare nu este cod C (se aplica reguli diferite).
----------

Folosirea multimii de scanare
--------------------------------------
O specificare de conversie de forma %[sir] indica ca un sir special poate fi 
citit. Multimea de caractere dintre parantezele patrate se numeste "multime de
scanare". Daca primul caracter din multimea de scanare nu este caracterul 
circumflex "^", atunci sirul trebuie sa fie construit numai din caractere ce 
apartin multimii de scanare. 
-----------
Exemple: 1. Formatul %[abc] va citi orice sir care contine literele "a", "b" si
----------- "c" si se va opri daca orice alt caracter va apare in sirul de 
             intrare, inclusiv un spatiu (ex. scanf(“%[abc]”, m)).
               2. In contrast, formatul %[^abc] va citi orice ce se va termina
                  cu "a", "b" sau "c", dar nu si spatiu. 
               3. Fie codul
                        char m[30];
                        scanf("%29[AB \t\n]", m);
                        
                  Aceasta va produce citirea in vectorul de caractere "m" a 
                  unui sir de cel mult 29 caractere. Sirul consta din literele
                  A, B, spatiu, tab, newline. La sfarsit, se va scrie \0.
              4. Programatorii de obicei gandesc o linie ca un sir de caractere
                  inclusiv spatii si taburi, care se termina cu un newline. Un 
                  mod (elegant) de a citi o linie in memorie este folosirea unei
                  multimi de scanare potrivita:
            
                        char linie[256];
                        while (scanf(" %[^\n]", linie) == 1)
                          printf("%s\n", linie);
                          
Valoarea returnata de "scanf()"
-----------------------------------------
Cand "scanf()" este apelata, poate apare o greseala la citire. De exemplu, daca
nu sunt caractere in sirul de intrare, atunci "scanf()" va intoarce -1 (EOF). 
Daca apare o nepotrivire intre formatele din "scanf()" si sirul de la intrare,
atunci "scanf()" va intoarce numarul de conversii cu succes pana in acel 
moment. Numarul este zero daca nu apar conversii. Daca "scanf()" reuseste cu 
succes, atunci este returnat numarul de conversii cu succes. La fel, acest 
numar poate fi zero.
-----------
Exemplu:
-----------
            char c, *sir_control, s[7], m[18];
            int a, contor;
            sir_control = "%d , %*s %% %c %[abc] %*s %5s %s";
            contor = scanf(sir_control, &a, &c, s, m, &m[5]);
                
Consideram ca avem la intrare sirul:

            23 , ignora   %  C  abacus  citeste_aceasta**
            
Atunci:
            "23" este plasat in memorie la adresa lui "a"
            "," se potriveste
            "ignora" este un sir ignorat
            "%" se potriveste
            "C" este plasat in memorie la adresa lui "c"
            "abac" este plasat in s[0],...,s[4]='\0'
            "us" este un sir ignorat
            "cites" este plasat in m[0],...,m[5]='\0'
            "te_aceasta**" este plasat in m[5],...,m[18]='\0'

Din moment ce au avut loc 5 conversii cu succes, rezulta ca functia "scanf()" va intoarce valoarea 5.
-----------
Exemplu:  Tabelul de mai jos contine mai multe exemple de directive de control pentru functia "scanf()":
-----------
---------------------------------------------------------------------
Directive   Tipul argumentului  Continutul        Observatii
in sirul      corespunzator     sirului de
de control                        intrare   
---------------------------------------------------------------------
  ab%2c      char *             abacus     ab se potriveste
                                ac se converteste
---------------------------------------------------------------------
  %3hd       short *            -7733      -77 se converteste
---------------------------------------------------------------------
  %41i       long *             +0x66      +0x6 se converteste
---------------------------------------------------------------------
  -%2u       unsigned *         -123         - se potriveste
                                 12 se converteste
---------------------------------------------------------------------
  + %lu      unsigned long *    +-123      + se potriveste
                                 -123 se converteste
---------------------------------------------------------------------
  + %lu      unsigned long *    + -123     + se potriveste
                                  -123 se converteste
---------------------------------------------------------------------
  + %lu      unsigned long *    +- 123     + se potriveste
                                eroare, (- nu se converteste)
---------------------------------------------------------------------
  %3e        float *            +7e-2      +7e se converteste
---------------------------------------------------------------------
  %4f        float *            7e+22      7e+22 se converteste
---------------------------------------------------------------------
  %51f       double *           -1.2345    -1.23 se converteste  
---------------------------------------------------------------------
  %4Lf       long double *      12345      1234 se converteste
---------------------------------------------------------------------
  %p         void * *           dependent  poate citi ceea ce printf()
                                 de sistem  cu %p scrie la iesire
---------------------------------------------------------------------

-------------------------------------------
Functiile "sprintf()" si "sscanf()"
-------------------------------------------
Functiile "sprintf()" si "sscanf()" sunt versiuni ce folosesc siruri ale functiei "printf()" si "scanf()", respectiv. Prototipurile lor, care se gasesc in "stdio.h", sunt:

        int sprintf(char *s, const char *format, ...);
        int sscanf(const char *s, const char *format, ...);
        
Punctele ... indica compilatorului faptul ca functia poate avea un numar variabil de argumente. O instructiune de forma:

        sprintf(sir, sir_de_control, alte_argumente);
        
scrie rezultatul in sirul de caractere "sir". Intr-o maniera similara, o instructiune de forma:

        sscanf(sir, sir_de_control, alte_argumente);
        
citeste rezultatul din sirul de caractere "sir".
-----------
Exemplu:
-----------
            char * sir_intrare = "1 2 3 ab";
            char sir_iesire[100], temp[100];
            int a, b, c;
            sscanf(sir_intrare, "%d%d%d%s", &a, &b, &c, &temp);
            sprintf(sir_iesire, "%s %s %d%d%d\n", temp, temp, a, b, c);
            printf("%s", sir_iesire);
            
Atunci se va afisa la ecran:

           ab ab 123
           
Atentie ! Este responsabilitatea programatorului sa rezerve spatiu suficient pentru memorarea lui "sir_iesire" din "sprintf()".

------------------------------------------
Functiile "fprintf()" si "fscanf()"
------------------------------------------
 Functiile "fprintf()" si "fscanf()" sunt versiunile pentru fisiere a functiilor
"printf()" si "scanf()". Fisierul "stdio.h" contine un numar de constructii 
referitoare la fisiere. In acest fisier exista si tipul structura FILE a caror
membrii descriu starea curenta a unui fisier. 
 Tot in acest fisier, sunt definiti trei pointeri la fisier. Este vorba despre
 "stdin", "stdout" si "stderr". 
   ------------------------------------------------------------------------
     Denumirea in C       Numele complet         Observatii
   ------------------------------------------------------------------------  
     stdin                standard input file   conectat la tastatura
     stdout               standard output file  conectat la ecran
     stderr               standard error file   conectat la ecran
   ------------------------------------------------------------------------           
In fisierul "stdio.h" exista prototipurile pentru functiile "fprintf()" si 
 "fscanf()":
  
        int fprintf(FILE *ofp, const char *format, ...);
        int fscanf(FILE *ifp, const char *format, ...);

("ofp" - outfile pointer, iar "ifp" - infile pointer)

Punctele ... spun compilatorului ca functia ia un numar variabil de argumente. O instructiune de forma:

   fprintf(pointer_catre_fisier, sir_de_control, alte_argumente);
   
va scrie in fisierul spre care pointeaza "pointer_catre_fisier". In particular, 

   fprintf(stdout, ...);  este echivalent cu   printf(...);
   
Intr-o maniera similara, o instructiune de forma:

   fscanf(pointer_catre_fisier, sir_de_control, alte_argumente);
   
va citi din fisierul spre care pointeaza "pointer_catre_fisier". In particular, 

   fscanf(stdin, ...);  este echivalent cu   scanf(...);

-------------------------
Accesarea fisierelor
-------------------------
Fisierele au cateva proprietati importante:

        - au un nume
        - trebuie inchise si deschise
        - poate fi scris in ele sau citit din ele sau adaugat la ele
        - cand sunt deschise avem acces la ele de la inceput la sfarsitul lor

 Abstract, un fisier poate fi gandit ca un sir de caractere. Dupa ce un fisier 
a fost deschis, sirul poate fi accesat folosind functii din biblioteca standard. 

-----------
Exemplu:
-----------
            #include 
               
            void main()
             {
              int suma = 0, val;
              FILE *ifp, *ofp;
              ifp = fopen("fis_in", "r");  /* deschis pentru citire */
              ofp = fopen("fis_out", "w"); /* deschis pentru scriere */
              . . . . . .
             }

In acest exemplu, am deschis fisierul "fis_in" pentru citire si "fis_out" 
pentru scriere. Din momentul deschiderii fisierului, pointerul catre fisier 
poate fi folosit exclusiv pentru referirea la intregul fisier. Daca, de 
exemplu, presupunem ca fisierul "fis_in" are numere intregi, atunci iata o 
modalitate de a face suma lor:

            while (fscanf(ifp, "%d", &val) == 1)
              sum += val;
            fprintf(ofp, "Suma lor este %d.\n", suma);
            
Ca si "scanf()", functia "fscanf()" intoarce numarul de conversii cu succes. 
Dupa ce terminam de exploatat fisierele, putem sa le inchidem: 

            fclose(ifp);
            fclose(ofp);
            
Un apel de functie de forma "fopen(nume_fisier, mod)" deschide fisierul 
respectiv intr-un mod particular si returneaza un pointer catre fisier. Sunt 
mai multe posibilitati pentru modul de accesare a fisierului:

      ------------------------------------------------------------
      Mod de acces               Semnificatie
      ------------------------------------------------------------
         "r"             deschide fisier text pentru citire    
         "w"             deschide fisier text pentru scriere
         "a"             deschide fisier text pentru adaugare
         "rb"            deschide fisier binar pentru citire
         "wb"            deschide fisier binar pentru scriere
         "ab"            deschide fisier binar pentru adaugare
      -------------------------------------------------------------   

Fiecare dintre aceste moduri se poate termina cu "a+". Asta inseamna ca fisierul poate fi deschis si pentru citire si pentru scriere.

   ---------------------------------------------------------------
    Mod de acces               Semnificatie
   ---------------------------------------------------------------
    "r+"            deschide fisier text pentru citire si scriere   
    "w+"            deschide fisier text pentru scriere si citire
        . . . . . 
   ---------------------------------------------------------------

Deschiderea pentru citire a unui fisier care nu exista, sau care nu poate fi 
citit, va esua si functia "fopen()" va intoarce pointerul NULL. Deschiderea 
unui fisier pentru scriere va avea ca efect crearea unui fisier (daca acesta 
nu exista) sau se va suprascrie peste unul existent. Deschiderea unui fisier 
pentru adaugare va avea ca efect crearea unui fisier (daca acesta nu exista) 
sau se va scrie la sfarsitul sau (daca acesta exista). 

Daca scriem modul "r+" sau "w+" atunci fisierul se considera ca a fost deschis
pentru citire si scriere. Cu toate acestea citirile nu pot fi urmate de scrieri
decat daca s-a ajuns la EOF sau au intervenit apeluri ale functiilor "fseek()",
"fsetpos()" sau "rewind()". De asemeni, scrierile nu pot fi urmate de citiri 
decat daca s-a ajuns la EOF sau au intervenit apeluri ale functiilor "fflush()"
, "fseek()", "fsetpos()" sau "rewind()". 

------------------------------------------
Accesarea aleatoare a unui fisier
------------------------------------------
 In plus fata de accesarea unui caracter unul dupa altul intr-un fisier (acces
secvential), noi putem accesa caractere in locuri diferite (acces aleator). 
 In biblioteca C, functiile "fseek()" si "ftell()" sunt folosite pentru accesarea aleatoare a unui fisier. O expresie de forma

        ftell(pointer_catre_fisier)
        
returneaza valoarea curenta a indicatorului de pozitie in fisier. Valoarea 
reprezinta numarul de octeti pornind de la inceputul fisierului, numarand de la
0. Cand un caracter este citit dintr-un fisier, sistemul incrementeaza 
indicatorul de pozitie cu 1. Tehnic vorbind, indicatorul de pozitie in fisier 
este un membru a structurii catre care pointeaza "pointer_catre_fisier". 
Pointerul catre fisier nu pointeaza catre caractere individuale din fisier 
(aceasta este o eroare de conceptie pe care o fac programatorii incepatori).  

Functia "fseek()" are trei argumente:

        - pointer catre fisier
        - offset (intreg)
        - un intreg care arata locul fata de care se calculeaza offset-ul
        
O instructiune de forma 

        fseek(pointer_catre_fisier, offset, mod);
        
seteaza indicatorul de pozitie la o valoare care reprezinta "offset" octeti 
pornind de la "mod". Valoarea lui "mod" poate fi 0, 1 sau 2, insemnand ca ne 
referim la inceputul fisierului, pozitia curenta sau sfarsitul fisierului, 
respectiv. 

Atentie ! Functiile "fseek()" si "ftell()" sunt garantate sa lucreze numai 
pentru fisiere binare. In MS-DOS, daca dorim sa lucram cu aceste functii, 
trebuie sa deschidem acest fisier in acces binar. In UNIX, din moment ce exista
doar un singur mecanism de lucru cu fisierele, orice mod de deschidere pentru 
fisier este bun. 

------------------------
Stil de programare
------------------------
Un stil bun de programare va verifica daca functia "fopen()" lucreaza asa cum 
ne asteptam (in orice program serios, acest lucru trebuie facut). Iata cum ar 
trebui sa se faca deschiderea fisierului "fis1" in acces de citire:

        if ((ifp = fopen("fis1", "r")) == NULL)
         {
          printf("\nNu putem deschide fisierul fis1. Pa!\n\n");
          exit(1);
         }

-----------------------------------------------
Exercitii propuse spre implementare
-----------------------------------------------
1. Folosind "argc" si "argv" ca argumente ale functiei "main()", scrieti un 
  program care copie "fisier1" in "fisier2" dubland liniile  (cu exceptia lui 
  ).
2. Scrieti un program C care citeste in variabile C dintr-un fisier text 
  numarul de linii, respectiv coloane, si elementele unei matrice, apoi afisati
  matricea citita din fisier.