Professional Documents
Culture Documents
Stringovisafafasfafa
Stringovisafafasfafa
sam malo detaljnije upute za 10. tutorijal. Nadam se da ce vam biti od pomoci i
nemojte zamjeriti pravopisne i razne druge greske, jer sam jedva gledao dok sam
ovo kucao. smile emoticon
U programskim jezicima viseg nivoa najcesce postoji tip podataka koji se naziva
string i sluzi za obradu teksta. U C-u ne postoji takav tip podataka, ali se moz
e simulirati koristeci nizove znakova (char array).
Deklaracija takvog C stringa bi bila npr.:
char ime[100];
Ovim smo dobili niz od 100 elemenata tipa char koji su, kao i svaki drugi niz ko
ji na ovaj nacin deklariramo, nasumice inicijalizirani (ne znamo koja vrijednost
se nalazi u kojem elementu niza).
Ukoliko zelimo inicijalizirati C string tokom deklaracije mozemo koristiti sljed
ece konstrukcije:
char ime[100] = "Roland";
Ovim smo inicijalizirali prvih 7 elemenata niza na znakove
ime[0] -> 'R'
ime[1] -> 'o'
ime[2] -> 'l'
ime[3] -> 'a'
ime[4] -> 'n'
ime[5] -> 'd'
ime[6] -> '\0'
Ostali elementi su ponovo nasumice inicijalizirani. Konvencija kod stringova je
da znak '\0' koji se naziva jos null terminator i ima ASCII vrijdnost 0 oznacava
kraj stringa. Tu konvenciju postuju sve C-ovske funkcije koje primaju string ka
o argument, sto cemo vidjeti u nastavku.
Ukoliko zelimo ispisati string mozemo koristiti printf. na sljedeci nacin:
printf("%s", ime); // Ovo ispisuje Roland
Posto je formater %s funkcija printf kao argument ocekuje pokazivac na char. Var
ijabla ime predstavlja pokazivac na prvi element niza ime (&niz[0]), sto znaci d
a je prethodni poziv ispravan. Posto je konvencija da stringovi zavrsavaju sa nu
ll terminatorom, onda funkcija printf iterira kroz clanove niza ime sve dok ne n
aidje na znak '\0'.
Da smo primjera radi dodali jos znakove u niz:
ime[7] = '1';
ime[8] = '2';
ime[9] = '3';
ime[10] = '\0';
I ponovo pozvali:
printf("%s", ime);
Opet bismo dobili Roland, obzirom da printf ispisuje do prvog null terminatora s
tring! Ovaj trick se generalno moze iskoristiti za skracivanje stringova.
Pored se moze koristiti i pokazivacka aritmetika da se preskacu neka slova prili
kom ispisa npr.
printf("%s", ime + 2);
Ce ispisati land, jer radi argumenta ime + 2 funkcija printf misli da ime pocinj
e od 3. slova!
Stringove mozemo unijeti koristeci funkciju scanf kao npr.
scanf("%s", ime);
Primjetno je da ne koristimo adresni operator & nad varijablom ime kao sto smo r
adili sa drugim prostim tipovima podataka poput int, float, double, chard, itd.
Razlog tome je sto ime predstavlja vec pokazivac na prvi element niza (&niz[0]),
pa je adresni operator nepotreban. Nemojte slucajno greskom stavljati &niz, jer
to moze imati nepredivljive posljedice (http://stackoverflow.com/ /5406 /reading-astring-with-scanf).
Ukoliko sada unesemo na tastaturi Roland dobit cemo ponovo sljedece znakove u ni
zu:
ime[0] -> 'R'
ime[1] -> 'o'
printf("%c\n", string[i]);
i++;
}
}
Funkcija strlen iz biblioteke string.h vraca duzinu stringa (ne ukljucujuci null
terminator). Tako za nas string Roland\0 u stringu ime vraca 6 (bez obzira sto
u ovaj niz mozemo staviti 100 charova!).
Na OR LV10 je trebalo implementirati funkciju koja vraca duzinu stringa (u princ
ipu funkcija koja oponasa strlen iz string.h). Nju je lako implementirati ako ko
ristimo bilo koju od prethodnih implementacija funkcije ispis, npr.
int duzina(char* string)
{
int duzina = 0;
while (*string != '\0')
{
duzina++;
string++;
}
return duzina;
}
Funkcija strlen je prakticno jednako implementirana kao prethodna funkcija za du
zinu, sto znaci da je posljednja implementacija za ispis najmanje efikasna, jer
dva puta prolazimo kroz niz - jednom da odredimo duzinu i jednom da ispisemo sve
znakove.
Posto su pojedinacni elementi stringa obicni znakovi, to znaci da mozemo samogla
snike izbrojati ako prodjemo kroz string i u svakoj iteraciji provjerimo da li j
e trenutni znak 'a' ili 'A' ili'e' ili 'E' ili 'i' ili 'I' ili 'o' ili 'O' ili '
u' ili 'U'. Medjutim, ono sto je lijepo uraditi je tu provjeru izdvojiti u poseb
nu funkciju kao npr.:
int samoglasnik(char c)
{
// Moze i preko if-a
switch (c)
{
case 'a':
case 'A':
case 'e':
case 'E':
case 'i':
case 'I':
case 'o':
case 'O':
case 'u':
case 'U':
return 1;
}
return 0;
}
Ili npr.:
int samoglasnik(char c)
{
char samoglasnici[10] = "aAeEiIoOuU";
int i;
for (i = 0; i < 10; i++)
{
if (c == samoglasnici[i])
return 1;
}
return 0;
}
Da bismo pretvorili mala slova u velika ponovo trebamo iterirati preko niza i za
svaki znak provjeriti da li je malo slovo, ukoliko jeste pretvorimo ga u veliko
. Za detalje je korisno pogledati ASCII tabelu:
http://www.asciitable.com/index/asciifull.gif
U ASCII tabeli su velika slova prije malih slova i slova su abecedno poredana, s
to znaci da mozemo praviti provjere tipa
if ((string[i] >= 'a') && (string[i] <= 'z'))
Ili
if ((*string >= 'a') && (*string <= 'z))
Da bismo zakljucili da je tekuci znak malo slovo.
Razlika izmedju velikih i malih slova je 32, npr. slovo 'A' ima vrijednost 65, a
slovo 'a' ima vrijednost 97, isto tako slovo 'B' ima vrijednost 66, a slovo 'b'
ima vrijednost 98, itd. Tako da malo slovo mozemo pretvoriti u veliko
if ((string[i] >= 'a') && (string[i] <= 'z'))
{
string[i] -= 32;
}
ili
if ((*string >= 'a') && (*string <= 'z))
{
*string -= 32;
}
Medjutim, razliku od 32 nije potrebno pamtiti, jer se moze dobiti kao 'a' - 'A'
ili 'b' - 'B', itd.
Tako da bi neka implementacija mogla ovako izgledati:
void samoglasnici(char* string)
{
int razlika = 'a' - 'A';
while (*string != 0)
{
if ((*string >= 'a') && (*string <= 'z))
{
*string -= razlika;
}
string++;
}
}
Prethodna funkcija ce sva mala slova iz stringa pretvoriti u velika slova. Medju
tim, u postavci tutorijala prototip treba da izgleda ovako:
char* samoglasnici(char* string);
Zasto bi povratni tip trebao biti char*? To je stvar konvencije u C-u, sve funkc
ije koje modificiraju niz vracaju kao rezultat pokazivac na prvi element niza da
su moguci lancani pozivi poput:
printf("%s", velika(ime));
Ukoliko smo unijeli string "Roland" u varijablu ime prvo ce se izvrsiti funkcija
velika i pretvoriti to u "ROLAND", zatim ce kao rezultat vratiti pokazivac na p
rvi element stringa ime da bi funkcija printf odmah mogla ispisati novu vrijedno
st (ovo se inace naziva lancani poziv funkcija). Tako da cemo implementaciju pre
praviti na sljedeci nacin:
char* samoglasnici(char* string)
{
char* pocetak = string;
int razlika = 'a' - 'A';
while (*string != 0)
{
if ((*string >= 'a') && (*string <= 'z))
{
*string -= razlika;
}
string++;
}
return pocetak;
}
Morali smo uvesti novu varijablu obzirom da iteriranjem po petlji pomjeramo poka
zivac desno, tako da smo npr. vratili string printf ne bi ispisao nista jer stri
ng pokazuje na null terminator.
Ovaj zadatak sa brojanjem rijeci podrazumijeva da su recenice gramaticki praviln
o napisane tako da se ovo svodi na brojanje razmaka. Tako tesni string "Danas je
lijep dan." ima 3 razmaka, odnosno 4 rijeci. Implementacije takve funkcije je d
osta trivijalna.
Ovaj zadnji zadatak zahtjeva da implementirate svoju funkciju koja oponasa funkc
iju strcmp iz biblioteke string.h. Ja bih preporucio da joj date drugo ime da bi
ste mogli porediti rezultate sa pravom strcmp funkcijom. Ideja je da u isto vrij
eme iterirate kroz dva stringa - znaci u prvoj iteraciji gledate prvi znak oba s
tringa, u drugoj iteraciji drugi znak oba stringa, itd. Prilikom dizajna ASCII t
abele je vec vodjeno racuna o tome, radi toga su velika slova prije malih slova,
a isto tako su pojedina slova poredana abecedno.
if (*prvi < *drugi) // prvi je abecedno prije drugog
if (*drugi < *prvi) // drugi je abecedno prije prvog
Ako se zavrsi petlja ova petlja bez returna onda provjeravate
if (duzina(prvi) < duzina(drugi)) // prvi je abecedno prije drugog
if (duzina(drugi) < duzina(prvi)) // drugi je abecedno prije prvog
Ako ni ovi if-ovi nisu zadovoljeni vratiti nulu, jer su stringovi jednaki. Ovo j
e sasvim okay implementacija, mada se ovo moze dodatno skratiti ako se uslov u p
etlji fino postavi.