CATEGORII DOCUMENTE |
DOCUMENTE SIMILARE |
|
TERMENI importanti pentru acest document |
|
. O colectie de programe utile
Vom considera in cele ce urmeaza o familie de programe inrudite pentru efectuarea de operatii simple asupra datelor alcatuite din caractere. Vom vedea ca multe programe sint doar versiuni extinse ale prototipurilor pe care le vom discuta aici.
Introducere si extragere de caractere
Biblioteca standard poseda functii pentru citirea si scrierea unui caracter la un moment dat. 'getchar()' aduce urmatorul caracter de intrare de fiecare data cind este apelata si returneaza acel caracter ca si valoare a ei. Adica, dupa
c=getchar()
variabila 'c' contine urmatorul caracter de intrare. Caracterele vin in mod normal de la terminal, dar aceasta nu ne intereseaza pina in Capitolul 7.
Functia 'putchar(c)' este complementara lui 'getchar()':
putchar(c)
tipareste continutul variabilei 'c' pe un mediu de iesire, in mod normal, tot pe terminal. Apelurile la 'putchar' si 'printf' pot fi intercalate; iesirea va apare in ordinea in care s-au facut apelurile.
Ca si in cazul lui 'printf', nu exista nimic special relativ la 'getchar' si 'putchar'. Ele nu sint parti ale limbajului C, dar sint universal disponibile.
Copiere de fisiere
Date 'getchar' si 'putchar', veti putea scrie o cantitate surprinzatoare de cod util, fara a sti nimic despre operatiile de I/O. Cel mai simplu program este acela care-si copiaza intrarea in iesire, caracter cu caracter. Schitindu-l:
citeste_un_caracter
while (caracterul_nu_este_semnal_de_sfirsit_de_fisier)
tipareste_caracterul_chiar_citit
citeste_un_nou_caracter
Convertind aceasta in limbajul C, obtinem:
main() /* copy input to output; 1st version */
}
Operatorul relational '!=' inseamna 'diferit de'.
Principala problema este detectarea sfirsitului de intrare. Prin conventie, 'getchar' returneaza o valoare care nu este un caracter valid atunci cind intilneste sfirsitul intrarii; in acest mod, programele pot detecta cind s-au terminat intrarile. Singura complicatie, o neplacere serioasa de fapt este aceea ca exista doua conventii ce se folosesc uzual pentru valoarea sfirsitului de fisier. Noi am evitat aceasta folosind deobicei numele simbolic de EOF pentru aceasta valoare, oricare ar fi fost ea. In practica, EOF poate fi sau -1 sau 0, asa ca programul trebuie sa fie precedat de una din declaratiile de mai jos:
#define EOF -1
sau
#define EOF 0
pentru ca el sa lucreze corect. Folosind constanta simbolica EOF pentru a reprezenta valoarea pe care o ret/neaza getchar cind intilneste sfirsitul de fisier, ne asiguram ca numai un singur lucru din program depinde de valoarea numerica specificata.
De asemenea il declaram pe 'c' ca fiind 'int', nu 'char', pentru ca el sa poata pastra valoarea pe care o returneaza 'getchar'. Cum vom vedea in Capitolul 2, aceasta valoare este normal un 'int', deoarece ea trebuie sa fie capabila sa-l reprezinte si pe EOF in plus fata de toate char-urile posibile.
Programul de copiere ar putea fi de fapt scris mult mai concis de catre un programator experimentat in limbajul C. In C, o asignare ca
c = getchar()
poate fi folosita intr-o expresie; valoarea sa este pur si simplu valoarea ce se asigneaza partii stingi. Daca asignarea unui caracter lui c se pune in partea de test a unui 'while', programul de copiat fisiere poate fi scris astfel:
main() /* copy input to output; 2nd version */
Programul citeste un caracter, il asigneaza lui 'c' si apoi testeaza daca acesta a fost semnalul de sfirsit de fisier. Daca nu a fost, corpul buclei 'while' este executat, tiparindu-se caracterul si bucla se repeta. Cind semnalul de sfirsit de fisier este atins in fine, bucla 'while' se termina, terminindu-se totodata si programul 'main'.
Aceasta versiune centralizeaza intrarile - nu mai apare decit un singur apel la 'getchar'- si restringe programul. Plasarea unei asignari intr-un test constituie unul din locurile unde C permite o concizie uluitoare. (Este posibil sa mergeti si mai departe, creind un cod impenetrabil, tendinta pe care noi incercam sa nu o incurajam).
Este important sa recunoastem ca parantezele ce includ asignarea sint absolut necesare. Ponderea lui '!=' este mai mare decit aceea a lui '=' ceea ce inseamna ca, in absenta parantezelor, testul relational '!=' va fi facut inaintea asignarii '='. Asa ca instructiunea
c = getchar() != EOF
este echivalenta cu
c = (getchar() != EOF)
Aceasta are un efect nedorit, prin setarea lui 'c' pe 0 sau 1, dupa cum apelul lui 'getchar ' a intilnit sau nu sfirsitul de fisier. (Mai multe despre acestea se vor vedea in Capitolul 2).
Contorizarea caracterelor
Urmatorul program va contoriza caracterele; el este o mica elaborare a programului de copiere.
main() /* count characters in input */
Instructiunea
++nc;
ne introduce un nou operator '++' care inseamna, increment cu 1. Se putea scrie si 'nc = nc+1', dar '++nc' este mai concisa si adesea mai eficienta. Exista un operator corespunzator, '--' pentru decrementare cu 1. Operatorii '++' si '--' pot fi atit operatori prefix, cit si sufix ('nc++'); aceste doua forme au valori diferite in expresii asa cum se va arata in Capitolul 2, dar '++nc' si 'nc++' il incrementeaza amindoi pe 'nc'.
Programul de contorizare de caractere acumuleaza numarul de caractere intr-o variabila 'long' in loc de 'int'. La PDP-11, valoarea maxima pentru un intreg este 32767 si s-ar putea ca sa dam peste o depasire de contor daca-l declaram intreg; pe
Honeywell si pe IBM, 'long' si 'int' sint sinonime si mult mai mari. Specificatorul de conversie '%ld' semnaleaza lui 'printf' ca argumentul corespunzator este un intreg 'long'.
Pentru a face fata la numere chiar si mai mari, se poate folosi o declaratie de 'double' ('float' de lungime dubla). Vom folosi, deasemenea, instructiunea 'for' in locul lui 'while' pentru a ilustra un alt mod in scrierea buclei.
main() /* count characters in input */
'printf' foloseste '%f' atit pentru 'float' cit si pentru 'double'; '%.0f' suprima tiparirea partii fractionare inexistente.
Corpul buclei 'for' este in cazul acesta vid, deoarece toata munca este facuta in partile de test si reinitializare. Dar regulile gramaticale ale limbajului C pretind ca o instructiune 'for' sa aiba un corp. Punctul si virgula ce apare izolat pe o linie, in mod tehnic o instructiune nula, este pus tocmai pentru a satisface aceasta cerere. Noi l-am pus pe o linie separata tocmai pentru a-l face mai vizibil.
Inainte de a parasi programul de contorizare caractere, sa observam ca daca intrarea nu contine nici un caracter, testul din 'while' sau 'for' esueaza la primul apel la getchar si deci rezultatul programului este zero, ceea ce este corect. Aceasta este o observatie importanta. Unul din lucrurile frumoase care se pot spune despre 'while' si despre 'for' este cela ca ele testeaza la inceputul buclei, inainte de a prelucra corpul buclei. Daca nu este nimic de facut, nimic nu se face, chiar daca aceasta inseamna ca nu se va parcurge corpul buclei niciodata. Programele vor actiona inteligent atunci cind vor minui intrari de tipul 'nici un caracter'. Instructiunile 'while' si 'for' ne asigura ca vor face lucruri rezonabile si in conditii la limita.
Contorizarea liniilor
Urmatorul program contorizeaza liniile pe care le primeste ca intrare. Liniile de intrare se presupun a fi terminate cu un caracter 'linie noua' n adaugat cu sfintenie la fiecare linie scrisa.
main() /* contorizarea liniilor in intrare */
Corpul buclei 'while' consta acum dintr-un 'if',care la rindul ei controleaza incrementarea ++nl. Instructiunea 'if' testeaza conditia din paranteza si, daca este adevarata, se executa instructiunea (sau grupul de instructiuni dintre acolade) care
urmeaza. Am aliniat iarasi, ca sa aratam ce este controlat de cine (ce).
Semnul dublu de egal '==' este in C notatia pentru 'este egal cu' (ca si .EQ. din FORTRAN). Acest simbol este folosit pentru a distinge testul de egalitate de egal simplu (=) folosit pentru asignare. Deoarece asignarea este cam de doua ori mai frecventa in C decit testul de egalitate, este normal ca si operatorul de asignare sa fie jumatate din cel de egalitate, ca lungime.
Orice caracter singur poate fi scris intre apostrofuri, pentru a produce valoarea numerica a caracterului in codul de carctere al calculatorului; acesta se numeste constanta de caracter. Asa de exemplu, 'A' este o constanta de caracter; in setul de caractere ASCII, valoarea sa este 65, reprezentarea interna a caracterului A. Desigur 'A' este de preferat lui 65: semnificatia lui este evidenta si independenta de orice set particular de caractere.
Secventele escape folosite in sirurile de caractere sint si ele legale in constantele de caracter, asa ca in teste si in expresii aritmetice 'n' tine locul caracterului 'linie noua'. Sa notam ca 'n' este un singur caracter si, in expresii, este echivalent cu un singur intreg; pe de alta parte, 'n' este un sir de caractere care, intimplator, contine un singur caracter. Subiectul comparatiei intre siruri si caractere este discutat mai departe in Capitolul 2.
Exercitiul 1.6. Scrieti un program care sa numere blankurile, taburile si new-line-urile.
Exercitiul 1.7. Scrieti un program care sa copieze intrarea in iesire,inlocuind fiecare sir de unul sau mai multe blankuri cu un singur blank.
Exercitiul 1.8. Scrieti un program care sa inlocuiasca fiecare tab printr-o secventa >,backspace,- care se va tipari ca '->' s fiecare backspace prin secventa similara '<-'.
Aceasta face taburile si backspace-urile vizibile.
Contorizarea de cuvinte
Al patrulea program din seria de programe utile va contoriza linii, cuvinte si caractere, un singur cuvint fiind definit ca orice secventa de caractere care nu contine blanc, tab sau linie noua (acesta este de fapt un schelet al programului utilitar 'wc' din UNIX).
#define YES 1
#define NO 0
main() /*contorizare linii, cuvinte si caractere la intrare*/
}
printf('%d %d %d'n', nl, nw, nc);
De fiecare data cind programul intilneste primul caracter al unui cuvint, il contorizeaza. Variabila 'inword' inregistreaza de cite ori programul este intr-un cuvint sau nu ; initial el 'nu este intr-un cuvint ' si variabilei i s-a asignat valoarea
NO. Preferam constantele simbolice YES si NO valorilor literale 1 si 0 deoarece ele fac programul mai usor citibil. Desigur ca intr-un program mic ca acesta diferenta este mica, dar intr-un program mai mare cresterea in claritate merita micul efort suplimenar de a-l scrie in acest mod de la inceput. Veti vedea deasemenea ca este mai usor sa efectuati modificari masive in programe in care numerele apar numai ca si constante simbolice.
Linia
nl = nw = nc = 0;
seteaza toate cele trei variabile pe zero. Acesta nu este un caz special ci doar o consecinta a faptului ca o asignare asociaza de la dreapta spre stinga. Este ca si cind am fi scris;
nc = (nl = (nw = 0));
Operatorul || inseamna SAU, asa ca linia
if(c == ' ' || c == 'n' || c == 't');
spune ca 'daca c este un blanc sau c este o linie noua sau c este un tab'. (Secventa escape t este reprezentarea vizibila a caracterului tab).Exista un operator corespunzator && pentru SI. Expresiile conectate prin && sau || sunt evaluate de la stinga la dreapta si evaluarea se opreste atunci cind se cunoaste adevarul sau falsul expresiei. Astfel daca c contine un blanc, nu mai este nevoie sa testam daca el contine o line noua sau un tab, asa ca testele acestea nu se mai fac. In particular, aceasta nu este important aici, dar este foarte semnificativ in multe situatii complicate, asa cum vom vedea in curind.
Exemplul nostru foloseste deasemenea instructiunea 'else', care specifica o actiune alternativa ce trebuie executata daca partea de conditie unei instructiuni 'if' este falsa.
Forma generala este:
if (expresie)
instructiune1
else
instructiune2
Una si numai una din instructiunile asociate cu if-else se executa. Daca 'expresia' este adevarata, se executa 'instructiunea-1'; daca nu, se executa 'instructiunea-2'. Fiecare 'instructiune' poate fi, de fapt, mult mai complicata. In exemplul nostru instructiuea de dupa 'else' este un 'if' care controleaza doua instructiuni in paranteze.
Exercitiul 1.9. Cum veti testa programul de contorizare cuvinte? Care sint unele dintre limitele lui ?
Exercitiul 1.10. Scrieti un program care sa tipareasca cuvin tele introduse,cite unul pe linie.
Exercitiul 1.11. Revizuiti programul de contorizare cuvinte pentru a folosi o mai buna definitie a 'cuvintului', de exemplu o secventa de litere, cifre si apostrofuri care incepe cu o litera.
1.6. Tablouri
Vom scrie acum un program care va contoriza aparitiile fiecarei cifre, a fiecarui caracter de spatiere (blanc, tab, linie noua) si a tuturor celorlalte caractere. Desigur, este un program artificial, dar ne va permite sa ilustram mai multe aspecte ale lui C intr-un singur program.
Exista 12 categorii de intrari, asa ca ne este mai convenabil sa folosim un tablou pentru a tine numarul de aparitii a fiecarei cifre, decit sa folosim 10 variabile individuale. Iata acum o versiune a acestui program:
main() /* contorizeaza cifre, spatii albe, alte caractere */
Declaratia
int ndigit[10];
spune ca ndigit este un tablou de 10 intregi. Indicii de tablou intodeauna incep de la zero in C (spre deosebire de FORTRAN sau PL/1 unde incepe de la unu), asa ca elementele tabloului sint ndigit[0], ndigit[1] , ,ndigit[9]. Acestea se reflecta in buclele 'for', care initializeaza si tiparesc tabloul.
Un indice poate sa fie orice expresie intreaga, inclusiv desigur variabilele intregi ca 'i', sau constantele intregi.
Acest program particular se bazeaza mult pe proprietatile reprezentarii drept caractere a cifrelor. De exemplu, testul
if (c >= '0' && c <= '9')
determina daca un caracter din c este cifra. Daca el este cifra, valoare numerica a acelei cifre este
c - '0'
Acest algoritm functioneaza bine numai daca '0', '1', etc sint pozitive si in ordine crescatoare, si intre '0' si '9' nu se gaseste altceva decit cifre. Din fericire, aceasta este adevarul pentru toate seturile de caractere conventionale.
Prin definitie, calculele aritmetice care implica tipuri 'char' si 'int', convertesc totul in tipul 'int' inainte de prelucrare, asa ca variabilele si constantele de tip 'char' sint esential identice cu tipul 'int' in contexte aritmetice. Acest fapt este aproape natural si convenabil; de exemplu: c-'0' este o expresie intreaga cu o valoare intre 0 si 9 corespunzatoare caracteruluidintre '0' si '9' depus in c, si deci este un indice valid pentru tabloul ndigit.
Decizia se ia asupra caracterului (daca el este o cifra, un caracter de spatiere sau altceva) in secventa:
if (c >= '0' && c <= '9')
++ndigit[c-'0'];
else if (c == ' ' || c == 'n' || c == 't')
++nwhite;
else
++nother;
Constructia de tipul
if (conditie)
instructiune
else if (conditie)
instructiune
else
instructiune
apare frecvent in programe ca o modalitate de a exprima deciziile multiple. Codul se citeste simplu de sus in jos pina cind o conditie este indeplinita; in acest punct, se executa partea corespunzatoare de 'instructiune' si intreaga constructie este terminata. (Desigur ca 'instructiune' pot fi mai multe instructiuni incluse intre paranteze). Daca nici una din conditii nu este indeplinita, instructiunea care urmeaza dupa ultimul 'else' este executata daca este prezenta. Daca 'else' final si 'instructiune' lipsesc (ca in programul nostru), nu are loc nici o actiune. Pot exista un numar arbitrar de constructii de tipul
else if (conditie)
instructiune
grupate intre 'if'-ul initial si 'else'-ul final. Ca o chestiune de stil, va sfatuim sa formati aceste constructii asa cum le-am facut si noi, astfel incit deciziile lungi sa nu ajunga pe marginea din dreapta a paginii.
Instructiunea 'switch', care va fi prezentata in Capitolul 3, reprezinta un alt mod de a scrie o decizie multipla si este potrivita, in particular, cind conditia care se testeaza este simpla sau cind o expresie de caractere sau de intregi se potriveste cu o constanta dintr-un sir dat. Prin contrast, vom prezenta o versiune 'switch' a acestui program in Capitolul 3.
Exercitiul 1-12. Scrieti un program pentru a tipari histograma lungimilor cuvintelor care apar la intrare. Este cel mai usor sa desenati histograma orizontal; o orientare verticala este mai laborioasa.
Politica de confidentialitate | Termeni si conditii de utilizare |
Vizualizari: 1088
Importanta:
Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved