C-keel on programmeerimise aluste õppimisel väga oluline keel. Nende hulgas on „stringi sisestamine“ hädavajalik funktsioon, kui on vaja kasutajalt andmeid vastu võtta. Selles artiklis selgitame üksikasjalikult, kuidas stringisisestust C-keeles kasutada, ja tutvustame tehnikaid ning ettevaatusabinõusid, et seda turvaliselt hallata. Eriti algajatel tekivad sageli probleemid vigade ja turvariskidega, mis võivad ilmneda sisestatud stringe töödelda püüdes. Seetõttu katab see artikkel laia spektrit, alates põhilistest funktsioonidest kuni edasijõudnute koodinäideteni, eesmärgiga aidata teil omandada praktilisi oskusi. Kui mõistate õigesti C-keele stringi sisestamise põhimõtteid ja suudate neid turvaliselt rakendada, saate astuda esimese sammu keerukamate programmide loomise poole. Alustame konkreetse sisuga.
2. Mis on stringisisestus C-keeles? Põhimõisted
Mis on string?
C-keeles esitatakse stringid tähemärkide massiivina. Stringi lõpus peab alati olema lõpetav sümbol „\0“, mis näitab stringi lõppu. Tänu sellele ei pea C-keeles stringi pikkust alati otseselt määrama.
Stringide ja massiivide seos
C-keeles on string tegelikult tähemärgimassiiv (char tüüpi). Näiteks saab stringi deklareerida järgmiselt:
char str[10]; // 10 tähemärgi pikkune puhver stringi jaoks
Selles näites reserveeritakse mälu kuni 10 tähemärgi jaoks. Kuid üks koht on reserveeritud lõpetavale märgile „\0“, seega saab tegelikult salvestada kuni 9 tähemärki.
Stringi literaali näide
Stringi literaal on tekst, mis on ümbritsetud jutumärkidega (“”). Näiteks:
char greeting[] = "Hello";
Sellisel juhul käsitletakse greeting-i automaatselt 6 elemendiga massiivina („Hello“ + lõpetav sümbol).
Miks on stringi sisestamine vajalik?
Programmides on sageli vaja kasutajalt andmeid sisestada. Näiteks nime või aadressi registreerimine, otsingusõna sisestamine jne. Seetõttu on oluline mõista, kuidas stringe turvaliselt ja tõhusalt käsitleda.
3. Stringi sisestamise põhifunktsioonid ja kasutusnäited
3-1. scanf funktsioon
scanf funktsiooni põhikasutus
scanf funktsiooni kasutatakse andmete saamiseks standardsisendist (klaviatuurilt). Stringi sisestamisel kasutatakse vormingumäärangut %s. Koodinäide:
#include <stdio.h>
int main() {
char str[50]; // Puhver kuni 50 tähemärgi jaoks
printf("Sisesta string: ");
scanf("%s", str); // Loe string standardsisendist
printf("Sisestatud string: %s\n", str);
return 0;
}
See programm võtab kasutajalt stringi sisendi ja kuvab selle ekraanile.
scanf funktsiooni tähelepanekud
Tühikuid ei töödelda:scanf käsitleb tühikuid, tabulaatoreid ja reavahetusi eraldajatena. Seetõttu katkeb string sisestamisel esimese tühiku juures.
Näide: Sisend:
Hello World
Väljund:
Hello
Puhvri ületäitumise oht: Kui sisestatud string ületab puhvri suuruse, võib see mälu rikkuda. Selle tagajärjeks võib olla programmi kokkujooksmine või turvanõrkuste ärakasutamine.
Lahendus: Soovitatav on kasutada turvalisemaid funktsioone (näiteks fgets).
3-2. fgets funktsioon
fgets funktsiooni põhikasutus
fgets võimaldab turvaliselt lugeda stringi kuni määratud pikkuseni. See loeb kaasa ka reavahetuse märgi, kuid väldib puhvri ületäitumist. Koodinäide:
#include <stdio.h>
int main() {
char str[50]; // Puhver kuni 50 tähemärgi jaoks
printf("Sisesta string: ");
fgets(str, sizeof(str), stdin); // Turvaline sisestus
printf("Sisestatud string: %s", str);
return 0;
}
See programm loeb stringi kuni 50 tähemärki ja väljastab selle turvaliselt.
fgets funktsiooni eelised
Puhvri ületäitumise vältimine: Kasutaja sisestus piiratakse määratud suurusega.
Tühikute töötlemine: Sisestatud string võib sisaldada ka tühikuid ja tabulaatoreid.
fgets funktsiooni tähelepanekud
Reavahetuse töötlemine: Sisestatud stringi lõpus võib olla reavahetuse märk, mis võib väljundis põhjustada ootamatu tühja rea.
Näide reavahetuse eemaldamiseks:
str[strcspn(str, "\n")] = '\0';
Sisendpuhvrisse jäävad andmed: Kui pärast fgets-i kasutatakse muid sisendmeetodeid, võib puhvris alles olla üleliigne sisend. Selle vältimiseks kasutatakse sageli fflush(stdin) või getchar().
3-3. Millist kasutada?
Funktsiooni nimi
Kasutus
Tähelepanekud
scanf
Lihtne stringi sisestus (ilma tühikuteta lühikesed stringid)
Oht puhvri ületäitumisele, ei tööta tühikutega.
fgets
Turvaline, sobib ka tühikuid sisaldava sisendi jaoks
Tuleb eemaldada reavahetus ja vajadusel puhverdada sisend.
Algajatele ja praktiliste programmide puhul on soovitatav eelistada fgets-i turvalisuse tõttu.
Puhvri ületäitumine tekib siis, kui sisend ületab ette nähtud mäluvahemiku suuruse ja kirjutab andmeid teistesse mälupiirkondadesse. Selle tagajärjeks võib olla programmi krahh või turvaaukude tekkimine. Näide: Järgmine kood on ohtlik:
char str[10];
scanf("%s", str); // Ei piira sisendi pikkust
Kui kasutaja sisestab üle 10 tähemärgi, tekib puhvri ületäitumine.
Lahendus 1: Kasuta fgets funktsiooni
fgets võimaldab määrata puhvri suuruse ja piirata sisendi pikkust, mis suurendab turvalisust. Turvaline näide:
scanf-i saab samuti kasutada stringide sisestamiseks, kuid nagu eelnevalt mainitud, ei suuda see töödelda tühikuid. Keerukamate stringide ja turvalisuse jaoks on parem kasutada fgets-i.
getline funktsiooni kasutamine
getline on POSIX-standardiga funktsioon (mitte C11 osa), mis võimaldab dünaamiliselt mälu eraldada ja töödelda pikki stringe ilma puhvri suurust muretsemata. Näide:
#include <stdio.h>
#include <stdlib.h>
int main() {
char *line = NULL;
size_t len = 0;
ssize_t read;
printf("Sisesta string: ");
read = getline(&line, &len, stdin); // Dünaamiline mälu eraldamine
if (read != -1) {
printf("Sisestatud string: %s", line);
}
free(line); // Vabasta mälu
return 0;
}
Miks vältida vananenud funktsioone?
C-keeles on mitmed vanad funktsioonid turvalisuse kaalutlustel märgitud mitte-soovitatavaks. Kui sisend pärineb väljastpoolt (nt kasutajalt), suureneb turvarisk. Seetõttu tuleb alati kasutada turvalisi alternatiive.
Mitte-soovitatav funktsioon
Alternatiiv
Kasutus ja eelised
gets
fgets
Turvaline sisestus fikseeritud puhvri suurusega.
gets
getline
Sobib pikkade stringide jaoks, kasutab dünaamilist mälu.
scanf("%s")
fgets
Töötleb turvaliselt ka tühikuid sisaldavaid stringe.
6. Praktilised näited ja laiendatud kasutus|mitmerealise stringi sisestamine ja töötlemine
6-1. Mitme rea sisestuse vastuvõtmine
Mitmerealise sisendi ülevaade
Mõnes programmis on vaja kasutajalt võtta mitmerealine tekst ja töödelda seda tervikuna. Näiteks märkmiku rakendustes on mitmerealine sisend hädavajalik.
Näide 1: 3 rea sisendi vastuvõtmine
#include <stdio.h>
#include <string.h>
#define MAX_LINES 3 // Reakogus
#define MAX_LENGTH 100 // Maksimaalne pikkus rea kohta
int main() {
char lines[MAX_LINES][MAX_LENGTH];
printf("Palun sisesta %d rida:\n", MAX_LINES);
for (int i = 0; i < MAX_LINES; i++) {
printf("%d. rida: ", i + 1);
fgets(lines[i], sizeof(lines[i]), stdin);
lines[i][strcspn(lines[i], "\n")] = '\0'; // Eemalda reavahetus
}
printf("\nSisestatud read:\n");
for (int i = 0; i < MAX_LINES; i++) {
printf("%d. rida: %s\n", i + 1, lines[i]);
}
return 0;
}
Põhipunktid:
Kahemõõtmeline massiiv: Iga rida salvestatakse eraldi massiivi.
Turvaline sisestus fgets-iga: Piirab maksimaalset pikkust ja eemaldab reavahetuse.
for-tsükkel: Võimaldab mugavalt mitut sisendit töödelda.
Kasutusnäide:
Palun sisesta 3 rida:
1. rida: Hello
2. rida: World
3. rida: C Language
Sisestatud read:
1. rida: Hello
2. rida: World
3. rida: C Language
6-2. Tühikute ja erimärkidega stringide töötlemine
Näide tühikutega sisendist
Tühikuid sisaldavate stringide sisestamiseks tuleks kasutada fgets-i. Näide 2: Tühikuid sisaldava sisendi töötlemine
Lõpetuseks toome näite programmist, mis kombineerib mitmerealise sisendi ja faili salvestamise. Näide 4: Märkmiku programm
#include <stdio.h>
#include <string.h>
#define MAX_LINES 5
#define MAX_LENGTH 100
int main() {
char lines[MAX_LINES][MAX_LENGTH];
printf("Saad sisestada kuni %d rida.\n", MAX_LINES);
for (int i = 0; i < MAX_LINES; i++) {
printf("%d. rida: ", i + 1);
fgets(lines[i], sizeof(lines[i]), stdin);
lines[i][strcspn(lines[i], "\n")] = '\0';
}
FILE *file = fopen("memo.txt", "w");
if (file == NULL) {
printf("Faili ei õnnestunud avada.\n");
return 1;
}
for (int i = 0; i < MAX_LINES; i++) {
fprintf(file, "%s\n", lines[i]);
}
fclose(file);
printf("Märge salvestatud.\n");
return 0;
}
Põhipunktid:
Kasutaja sisend salvestatakse faili.
Praktiline näide failitöötluse õppimiseks.
7. Korduma kippuvad küsimused (K&V vormis)
K1: Miks ei tohiks kasutada gets funktsiooni?
V:gets loeb stringi ilma pikkust piiramata, mis tekitab puhvri ületäitumise riski. See turvanõrkus võib põhjustada tõsiseid probleeme, mistõttu eemaldati see C11 standardist. Soovitatav on kasutada turvalist fgets-i. Näide (turvaline alternatiiv):
K5: Kuidas puhastada ülejäänud sisendit, kui fgets ei mahuta kogu stringi?
V: Kui sisend ületab puhvri, jäävad ülejäänud tähemärgid sisendpuhvrisse. Selle saab puhastada getchar-iga. Näide:
char str[10];
fgets(str, sizeof(str), stdin);
if (strchr(str, '\n') == NULL) {
int c;
while ((c = getchar()) != '\n' && c != EOF);
}
printf("Sisestatud string: %s\n", str);
K6: Kuidas lubada ainult tähed ja numbrid?
V: Rakenda sisendi filtreerimine ja luba ainult sobivad märgid. Näide:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int isValidInput(const char *str) {
for (int i = 0; str[i] != '\0'; i++) {
if (!isalnum(str[i])) {
return 0;
}
}
return 1;
}
int main() {
char str[50];
printf("Sisesta ainult tähed ja numbrid: ");
fgets(str, sizeof(str), stdin);
str[strcspn(str, "\n")] = '\0';
if (isValidInput(str)) {
printf("Kehtiv sisend: %s\n", str);
} else {
printf("Vigane sisend.\n");
}
return 0;
}
K7: Kuidas töödelda väga pikki stringe, mis ületavad puhvri suuruse?
V:fgets puhul tuleb string jagada mitmeks osaks või kasutada getline-i, mis eraldab mälu dünaamiliselt ja sobib pikkade sisendite jaoks. Näide:getline võimaldab lugeda suurt hulka andmeid ilma mälupiiranguteta.
8. Kokkuvõte
Selles artiklis selgitasime C-keele stringi sisestamist alates põhialustest kuni edasijõudnud kasutuseni. Õppisime, kuidas valida sobivaid funktsioone ja rakendada praktilisi tehnikaid, et hallata stringe turvaliselt ja tõhusalt.
1. Stringi sisestamise põhialused
C-keeles esitatakse string tähemärgi massiivina ja see lõpeb lõpetava märgiga \0. Selle põhimõtte mõistmine on aluseks stringide töötlemisele.
2. Sisestusfunktsioonide omadused ja valik
scanf: Ei toeta tühikuid ja võib põhjustada puhvri ületäitumise riski.
fgets: Võimaldab määrata sisendi suuruse ja töödelda turvaliselt ka tühikuid. Vajalik on reavahetuse eemaldamine.
getline: Dünaamilise mälu kasutus, sobib suurte sisendite jaoks (ainult POSIX keskkonnas).
3. Turvalise sisendi praktika
Turvalisuse suurendamiseks õppisime:
Puhvri suuruse kontroll: Väldi liiga suuri sisendeid.
Reavahetuse eemaldamine: Hoia sisend puhtana.
Sisendi valideerimine: Kontrolli andmeid ja rakenda vigade käsitlemist.
Tänu sellele saab kirjutada töökindlamaid ja turvalisemaid programme.
4. Praktiliste näidete kaudu oskuste tugevdamine
Mitmerealine sisend ja failide kirjutamine andsid kogemuse praktiliste programmide loomiseks. Kasutades kahemõõtmelisi massiive või dünaamilist mälu, saab ehitada keerukamaid rakendusi.
5. Korduvate probleemide lahendamine
K&V jaotis käsitles sagedasi küsimusi, sealhulgas:
gets funktsiooni ohtlikkus ja alternatiivid (fgets, getline).
Tühikute ja erimärkide käsitlemine.
Puhvri ületäitumise vältimine ja reavahetuse eemaldamine.
6. Järgmised sammud ja laiendatud õppimine
Stringi sisestamise mõistmine loob aluse järgmistele teemadele:
Stringi teekide kasutamine: Funktsioonid nagu strlen, strcmp, strcpy.
Dünaamiline mälu haldus:malloc, realloc paindlikuks stringide töötlemiseks.
Failide sisend/väljund: Suurte andmekoguste töötlemine failides.
Andmestruktuurid ja algoritmid: Otsingu- ja sorteerimisalgoritmide rakendamine stringidega.