C-keel standardne sisend: scanf ja fgets kasutamine algajatele

目次

1. Sissejuhatus

C-keelt õppides on kasutaja sisendi käsitlemine ehk “standardne sisend” vältimatu ja väga oluline funktsioon. Standardse sisendi õige mõistmine ja turvaline kasutamine võimaldab oluliselt parandada programmi paindlikkust ja töökindlust.

Selles artiklis selgitame süsteemselt standardse sisendi kasutamist C-keeles – alustades põhialustest ja lõpetades rakenduslike näidetega. Ka algajatele arusaadavuse tagamiseks lisame näidiskoodi, et saaksite õpitut kohe oma programmide arenduses rakendada.

Standardse sisendi olulisus

Standardne sisend on programmide jaoks põhiline mehhanism andmete vastuvõtmiseks väljastpoolt. Seda kasutatakse näiteks järgmistes olukordades:

  • Arvutusäpp, mis kasutab kasutaja sisestatud numbreid
  • Otsingufunktsioon, mis kasutab tekstistringe
  • Dünaamilised programmid, mis reageerivad käsurealt antud käskudele

Sellistes olukordades võimaldab standardse sisendi õige käsitlemine luua tõhusaid ja turvalisi programme.

Mida selles artiklis õpid

Selles artiklis käsitleme järgmisi teemasid:

  1. Standardse sisendi põhimõte ja selle teostus C-keeles
  2. scanf ja fgets funktsioonide kasutamine
  3. Kuidas rakendada turvalist ja töökindlat sisendi töötlemist
  4. Edasijõudnutele mõeldud andmetöötluse tehnikad
  5. Levinumad probleemid ja nende lahendused

Kellele see artikkel on mõeldud

  • Algajad, kes õpivad C-keelt esimest korda
  • Programmeerijad, kes ei tunne end kindlalt standardse sisendi kasutamisel
  • Need, kes soovivad luua turvalist ja tõhusat sisenditöötlust

2. Mis on standardne sisend

Standardne sisend C-keeles on põhiline mehhanism, mille kaudu programm saab andmeid välisest allikast. See on osa “standardsest sisendist ja väljundist” ning võimaldab kasutajal anda programmile andmeid terminali või käsurea kaudu. Selles jaotises selgitame standardse sisendi põhikontseptsiooni ja rolli.

Standardse sisendi määratlus

Standardne sisend (Standard Input, stdin) viitab andmevoole, mida programm kasutab andmete vastuvõtmiseks väljastpoolt. C-keeles saab standardset sisendit hõlpsasti kasutada stdio.h teegi abil.

  • Andmeid sisestatakse tavaliselt klaviatuuri kaudu.
  • Sisestatud andmeid töödeldakse programmis ning tulemused väljastatakse standardväljundisse.

Standardse sisendi ja väljundi mehhanism

C-keeles on saadaval kolm standardvoogu:

  1. Standardne sisend (stdin): andmete vastuvõtmiseks väljastpoolt.
  2. Standardne väljund (stdout): programmide tulemuste väljastamiseks.
  3. Standardne veaväljund (stderr): veateadete väljastamiseks.

Näide praktilisest toimimisest

Järgmine näide võtab standardse sisendi kaudu arvu ja väljastab selle standardväljundisse.

#include <stdio.h>

int main() {
    int number;
    printf("Sisesta arv: ");
    scanf("%d", &number); // loe täisarv standardse sisendi kaudu
    printf("Sisestatud arv on: %d\n", number); // väljasta standardväljundisse
    return 0;
}

Standardse sisendi roll ja olulisus

Standardset sisendit kasutatakse järgmistes programmides:

  • Andmetöötlusprogrammid: numbrite või tekstistringide sisestamine ja nende põhjal arvutuste või analüüside tegemine.
  • Interaktiivsed rakendused: programmi töö muutmine vastavalt kasutaja käskudele.
  • Dünaamiline andmete töötlemine: kasutaja sisestatud andmete reaalajas töötlemine.

Standardse sisendi eelised

  • Paindlikkus: toetab erinevaid sisendiallikaid, nagu klaviatuur, torud ja ümbersuunamine.
  • Lihtsus: sisseehitatud funktsioonide scanf ja fgets abil on sisendi töötlemine lihtne.

3. Standardse sisendi põhitõed C-keeles

C-keeles on kasutajal andmete sisestamiseks saadaval mitu funktsiooni. Kõige tavalisemad on scanf ja fgets. Selles jaotises selgitame, kuidas kumbagi kasutada koos näidetega.

scanf funktsiooni kasutamine

scanf on funktsioon, mis analüüsib sisestatud andmeid vormingutunnuste abil ja salvestab need muutujatesse.

Põhisüntaks

int scanf(const char *format, ...);
  • format: määrab andmete vormingu stringina.
  • ...: muutujate aadressid, kuhu sisestatud väärtused salvestatakse.

Peamised vormingutunnused

TunnusSelgitusNäide
%dTäisarv42
%fKoma-arv (ujukomaarv)3.14
%cÜks märkA
%sSõnestringHello

Kasutusnäide

#include <stdio.h>

int main() {
    int age;
    printf("Sisesta oma vanus: ");
    scanf("%d", &age); // loeb täisarvu
    printf("Sisestatud vanus on: %d\n", age);
    return 0;
}

Tähelepanekud

  • Kui andmeid ei sisestata õiges vormingus, võib tekkida vigu.
  • Oluline on piirata sisendi suurust, et vältida puhvermäluga seotud ületäitumisi (nt %s kasutamisel).

fgets funktsiooni kasutamine

fgets loeb sisendi ridade kaupa ja seda peetakse turvalisemaks kui scanf.

Põhisüntaks

char *fgets(char *str, int n, FILE *stream);
  • str: massiiv, kuhu sisend salvestatakse.
  • n: maksimaalne loetavate märkide arv (massiivi suurus).
  • stream: sisendi allikas (tavaliselt stdin).

Kasutusnäide

#include <stdio.h>

int main() {
    char name[50];
    printf("Sisesta oma nimi: ");
    fgets(name, 50, stdin); // loeb kuni 49 märki
    printf("Sisestatud nimi on: %s", name);
    return 0;
}

Tähelepanekud

  • Kui sisestatud tekst ületab puhversuuruse, siis ülejäänud osa kärbitakse.
  • Kui sisendis on reavahetus, tuleb see käsitsi eemaldada.

Kuidas eemaldada reavahetusmärk

name[strcspn(name, "\n")] = '\0';

scanf ja fgets võrdlus

Omadusscanffgets
KasutusArvude või stringide lugemine kindla vormingu järgiTekstirea lugemine tervikuna
TurvalisusVõib põhjustada puhvrimälu ületäitumistVõimaldab kontrollida puhversuurust
PaindlikkusToetab vormingutunnuseidLoeb kogu sisendi stringina

Kumb valida?

  • Kui kasutada scanf: sobib, kui on vaja otse lugeda numbreid või kindlat tüüpi andmeid.
  • Kui kasutada fgets: sobib, kui tähtis on turvalisus ja sisend võib olla pikk või keeruline.

4. Turvaline standardse sisendi kasutamine

Standardse sisendi töötlemisel C-keeles on turvalisuse tagamine äärmiselt oluline. Valesti teostatud sisend võib põhjustada puhvrimälu ületäitumisi või määratlemata käitumist ning vähendada programmi töökindlust. Selles jaotises käsitleme, kuidas standardset sisendit turvaliselt rakendada, koos praktiliste näidetega.

Kuidas vältida puhvrimälu ületäitumist

scanf on mugav, kuid ilma sisendi suurust piiramata võib tekkida puhvrimälu ületäitumine. Selle vältimiseks tuleb määrata maksimaalne sisendisuurus vormingutunnuses.

Näide sisendi piiramise kohta

#include <stdio.h>

int main() {
    char input[10];
    printf("Sisesta string (kuni 9 märki): ");
    scanf("%9s", input); // loeb kuni 9 märki
    printf("Sisestatud string: %s\n", input);
    return 0;
}

Olulised punktid

  • Kasutades nt %9s, saab piirata sisendi pikkust ja vältida massiivi ületamist.

Turvaliseks sisendiks kasuta fgets

fgets võimaldab määrata loetavate märkide arvu, vähendades sellega puhvrimälu ületäitumise riski. See sobib eriti hästi pikkade stringide või terve rea lugemiseks.

fgets kasutamise näide

#include <stdio.h>
#include <string.h>

int main() {
    char buffer[20];
    printf("Sisesta string: ");
    fgets(buffer, sizeof(buffer), stdin); // loeb kuni 19 märki
    buffer[strcspn(buffer, "\n")] = '\0'; // eemaldab reavahetuse
    printf("Sisestatud string: %s\n", buffer);
    return 0;
}

Olulised punktid

  • sizeof(buffer) abil saab puhversuuruse määrata automaatselt.
  • strcspn funktsiooni abil eemaldatakse sisendist reavahetusmärk.

Sisendandmete kontrollimine

Et tagada turvaline sisendi töötlemine, tuleb kasutaja sisestatud andmeid alati kontrollida. Näiteks kui eeldatakse täisarvu sisestamist, tasub sisend esmalt lugeda stringina ning seejärel teha teisendus ja vigade kontroll.

Näide täisarvu kontrollimise kohta

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

int main() {
    char buffer[20];
    long number;
    char *endptr;

    printf("Sisesta täisarv: ");
    fgets(buffer, sizeof(buffer), stdin);

    errno = 0; // lähtesta veaseisund
    number = strtol(buffer, &endptr, 10); // teisenda string täisarvuks

    if (errno != 0 || endptr == buffer || *endptr != '\0') {
        printf("Palun sisesta kehtiv täisarv.\n");
    } else {
        printf("Sisestatud täisarv: %ld\n", number);
    }

    return 0;
}

Olulised punktid

  • strtol võimaldab avastada teisenduse käigus tekkivaid vigu.
  • errno ja endptr koos annavad teada, kas teisendustulemus oli korrektne.

Vigade käsitlemise parimad tavad

Standardse sisendi töötlemisel on korrektne veakontroll võtmetähtsusega töökindluse tagamiseks.

Näide veakontrollist

#include <stdio.h>

int main() {
    int value;
    printf("Sisesta täisarv: ");

    if (scanf("%d", &value) != 1) { // kui sisend ebaõnnestub
        printf("Sisend on vigane.\n");
        return 1; // lõpetab veaga
    }

    printf("Sisestatud täisarv: %d\n", value);
    return 0;
}

Olulised punktid

  • Tuleb kontrollida scanf tagastusväärtust ja teha veakäsitlus, kui sisend ei vasta oodatud vormingule.

Turvalise sisenditöötluse kokkuvõte

  • scanf kasutamisel tuleb alati piirata sisendi suurust vormingutunnusega.
  • Pikemate stringide või rea kaupa sisendi jaoks kasuta fgets.
  • Ära usalda pimesi kasutaja sisendit – tee alati kontroll ja veakäsitlus.

5. Standardse sisendi rakenduslik kasutamine

Kui oled õppinud standardse sisendi põhitõed, tasub edasi liikuda keerukamate kasutusjuhtude juurde. See võimaldab töödelda mitmekesisemaid sisendandmeid ja olukordi. Selles jaotises käsitleme näiteks mitme väärtuse korraga sisestamist ning reavahetuste ja tühikute käsitlemist.

Mitme andme sisestamine korraga

Sageli tuleb töödelda mitut väärtust, mis sisestatakse standardse sisendi kaudu. Allpool on toodud näited, kuidas lugeda andmeid, mis on eraldatud tühikute või komadega.

Näide tühikuga eraldatud sisendist

#include <stdio.h>

int main() {
    int numbers[5];
    printf("Sisesta 5 täisarvu, eraldades need tühikutega: ");
    for (int i = 0; i < 5; i++) {
        scanf("%d", &numbers[i]); // loeb tühikuga eraldatud arvud
    }

    printf("Sisestatud arvud: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", numbers[i]);
    }
    printf("\n");

    return 0;
}

Näide komaga eraldatud sisendist

#include <stdio.h>

int main() {
    int numbers[3];
    printf("Sisesta 3 täisarvu, eraldades need komadega: ");
    scanf("%d,%d,%d", &numbers[0], &numbers[1], &numbers[2]); // loeb komaga eraldatud arvud

    printf("Sisestatud arvud: %d, %d, %d\n", numbers[0], numbers[1], numbers[2]);
    return 0;
}

Sisendi käsitlemine, mis sisaldab reavahetusi ja tühikuid

Standardse sisendi korral võib sisendis olla nii reavahetusi kui ka tühikuid. Järgnevalt vaatame, kuidas neid õigesti töödelda.

Näide reavahetuse käsitlemisest

Kui sisendis on reavahetus, saab seda töödelda kasutades fgets funktsiooni ning eemaldades reavahetuse käsitsi.

#include <stdio.h>
#include <string.h>

int main() {
    char input[100];
    printf("Sisesta tekstirida: ");
    fgets(input, sizeof(input), stdin);

    input[strcspn(input, "\n")] = '\0'; // eemaldab reavahetuse
    printf("Sisestatud tekst: %s\n", input);

    return 0;
}

Mitmerealise sisendi töötlemine

Mõnikord on vaja lugeda mitut rida järjest ja töödelda neid ükshaaval. Seda kasutatakse näiteks tekstianalüüsis ja andmetöötluses.

Näide mitmerealisest sisendist

#include <stdio.h>

int main() {
    char line[100];

    printf("Sisesta mitu tekstirida (lõpetamiseks vajuta Ctrl+D):\n");
    while (fgets(line, sizeof(line), stdin) != NULL) {
        printf("Sisestatud rida: %s", line);
    }

    return 0;
}

Dünaamilise sisendi töötlemine

Kui sisendandmete arv ei ole ette teada, tuleb neid töödelda dünaamiliselt. Järgmises näites sisestatakse tundmatu arv täisarve ja arvutatakse nende summa.

Näide dünaamilise sisendi töötlemisest

#include <stdio.h>

int main() {
    int number, sum = 0;
    printf("Sisesta täisarve (lõpetamiseks vajuta Ctrl+D):\n");

    while (scanf("%d", &number) == 1) {
        sum += number;
    }

    printf("Sisestatud arvude summa: %d\n", sum);
    return 0;
}

Kokkuvõte

Rakenduslike sisenditöötluse tehnikate abil saab luua programme, mis töötlevad keerulisemaid andmeid ja kohanduvad dünaamiliste olukordadega. Kui harjuda mitme andme korraga lugemise ja reavahetuste käsitlemisega, on võimalik luua veelgi arenenumaid programmilahendusi.

6. Levinumad probleemid ja nende lahendused

C-keeles standardse sisendi kasutamisel puutuvad algajad sageli kokku erinevate probleemidega. Allpool on toodud levinumad olukorrad ja nende lahendused, mis aitavad luua töökindlamaid programme.

scanf probleemid ja lahendused

Probleem 1: Sisendi vahelejätmine

scanf kasutamisel võivad reavahetused või tühikud mõjutada järgmist sisendilugemist.

Näide
#include <stdio.h>

int main() {
    int number;
    char letter;

    printf("Sisesta täisarv: ");
    scanf("%d", &number);

    printf("Sisesta märk: ");
    scanf("%c", &letter); // loeb kogemata reavahetuse

    printf("Sisestatud arv: %d, märk: %c\n", number, letter);
    return 0;
}
Lahendus

Et vältida reavahetuse lugemist, lisa enne %c vormingutühiku.

scanf(" %c", &letter); // tühik ignoreerib eelnevad reavahetused

Probleem 2: Puhvrimälu ületäitumine

Kui scanf abil loetakse liiga pikk string, võib see puhvrimälu ületada ja põhjustada programmi kokkujooksmise.

Näide
char input[10];
scanf("%s", input); // kui sisend on pikem kui 9 märki, tekib ületäitumine
Lahendus

Piira sisendi pikkust vormingutunnusega.

scanf("%9s", input); // piirab sisendi 9 märgiga

Või kasuta turvalisemat alternatiivi – fgets.

fgets probleemid ja lahendused

Probleem 1: Reavahetuse käsitlemine

fgets lisab sisestuse lõppu reavahetuse märgi, mis võib põhjustada ootamatut käitumist stringivõrdluse või andmete töötlemise ajal.

Näide
char input[20];
fgets(input, sizeof(input), stdin);
if (strcmp(input, "yes") == 0) {
    printf("Sisend on 'yes'.\n");
}

Sellisel juhul loetakse stringiks "yes\n" ning võrdlus ebaõnnestub.

Lahendus

Eemalda reavahetuse märk.

input[strcspn(input, "\n")] = '\0';

Sisendvigade käsitlemine

Probleem 1: Vigane sisend

Kui oodatakse täisarvu, aga sisestatakse hoopis string, võib programm töötada valesti.

Näide
int number;
scanf("%d", &number); // kui sisestatakse tekst, võib programm käituda valesti
Lahendus

Kontrolli alati sisendi kehtivust.

if (scanf("%d", &number) != 1) {
    printf("Vigane sisend.\n");
    while (getchar() != '\n'); // puhasta sisendipuhver
}

Mitmerealise sisendi probleemid

Probleem: scanf ja fgets segakasutus

Kui programmis kasutatakse nii scanf kui ka fgets, võib tekkida ootamatu käitumine.

Põhjus

scanf jätab reavahetuse sisendipuhvrisse, mistõttu järgmine fgets loeb selle kohe ära.

Lahendus

Puhasta sisendipuhver pärast scanf kasutamist.

while (getchar() != '\n');

Kokkuvõte

C-keeles standardse sisendi kasutamisel on mitmeid ohukohti, kuid kui mõista probleemide põhjuseid ja neid õigesti lahendada, on võimalik kirjutada turvalisi ja tõhusaid programme. Eriti oluline on mõista scanf ja fgets erinevusi ning valida vastavalt vajadusele sobiv meetod.

7. Näited standardse sisendi kasutamisest

Siin toome praktilised näited, kuidas standardset sisendit kasutada erinevates olukordades – alates lihtsatest juhtumitest kuni veidi keerukamate lahendusteni.

Näide 1: Arvude summa ja keskmise leidmine

Programm võtab mitu täisarvu standardse sisendi kaudu ning arvutab nende summa ja keskmise.

#include <stdio.h>

int main() {
    int numbers[100];
    int count = 0, sum = 0;
    float average;

    printf("Sisesta täisarve, eralda need tühikutega (lõpetamiseks vajuta Ctrl+D):\n");

    while (scanf("%d", &numbers[count]) == 1) {
        sum += numbers[count];
        count++;
    }

    if (count > 0) {
        average = (float)sum / count;
        printf("Summa: %d, Keskmine: %.2f\n", sum, average);
    } else {
        printf("Ühtegi arvu ei sisestatud.\n");
    }

    return 0;
}

Näide 2: Palindroomi kontroll

Programm kontrollib, kas sisestatud string on palindroom (loe sama nii eest- kui tagantpoolt).

#include <stdio.h>
#include <string.h>
#include <stdbool.h>

bool isPalindrome(char str[]) {
    int start = 0;
    int end = strlen(str) - 1;

    while (start < end) {
        if (str[start] != str[end]) {
            return false;
        }
        start++;
        end--;
    }
    return true;
}

int main() {
    char input[100];

    printf("Sisesta string: ");
    fgets(input, sizeof(input), stdin);

    // eemalda reavahetus
    input[strcspn(input, "\n")] = '\0';

    if (isPalindrome(input)) {
        printf("Sisestatud string on palindroom.\n");
    } else {
        printf("Sisestatud string ei ole palindroom.\n");
    }

    return 0;
}

Näide 3: CSV-andmete töötlemine

Programm loeb komaga eraldatud väärtusi ja töötleb neid ükshaaval.

#include <stdio.h>
#include <string.h>

int main() {
    char input[200];
    char *token;

    printf("Sisesta andmed CSV-formaadis: ");
    fgets(input, sizeof(input), stdin);

    // eemalda reavahetus
    input[strcspn(input, "\n")] = '\0';

    // eralda komadega
    token = strtok(input, ",");
    while (token != NULL) {
        printf("Väärtus: %s\n", token);
        token = strtok(NULL, ",");
    }

    return 0;
}

Näide 4: Interaktiivne programm dünaamilise sisendiga

Programm võtab kasutajalt korduvalt sisendit ja käitub vastavalt sisestatud käsule.

#include <stdio.h>
#include <string.h>

int main() {
    char input[50];

    printf("Sisesta 'exit', et programm lõpetada.\n");
    while (1) {
        printf("Sisesta käsk: ");
        fgets(input, sizeof(input), stdin);

        // eemalda reavahetus
        input[strcspn(input, "\n")] = '\0';

        if (strcmp(input, "exit") == 0) {
            printf("Programm lõpetati.\n");
            break;
        } else {
            printf("Sisestatud käsk: %s\n", input);
        }
    }

    return 0;
}

Kokkuvõte

Rakenduslikud näited standardse sisendi kasutamisest aitavad mõista, kuidas luua interaktiivseid ja praktilisi programme. Kasuta neid näiteid inspiratsiooniks ning kohanda neid vastavalt oma projektide vajadustele.

8. Kokkuvõte

Selles artiklis selgitasime standardse sisendi kasutamist C-keeles alates põhitõdedest kuni rakenduslike näideteni. Loodetavasti mõistad nüüd paremini, kuidas standardne sisend töötab, kuidas seda turvaliselt kasutada ja kuidas seda praktikas rakendada.

Standardse sisendi olulisuse kordamine

  • Standardne sisend on põhiline viis, kuidas programm saab andmeid väljastpoolt ja töötab dünaamiliselt.
  • C-keeles saab kasutada scanf ja fgets funktsioone numbrite ja stringide lugemiseks.

Turvalise ja tõhusa sisendi töötlemise põhipunktid

  1. Väldi puhvrimälu ületäitumist
  • Kasuta scanf puhul sisendi pikkuse piiranguid (nt %9s).
  • Pikemate stringide või ridade lugemiseks kasuta fgets.
  1. Sisendi valideerimine
  • Numbrilise sisendi korral kasuta strtol või strtod ning kontrolli vigu.
  • Vigase sisendi korral rakenda korrektset veakäsitlust.
  1. Rakenduslikud tehnikad
  • Mitme andme korraga lugemisel ja reavahetuste töötlemisel kasuta õigeid meetodeid.

Järgmised sammud

Kui oled omandanud standardse sisendi põhitõed, saad oma oskusi täiendada järgmiste sammudega:

  • Standardne väljund ja veaväljund: õpi, kuidas väljastada veateateid ja logisid.
  • Failide töötlemine: kombineeri failisisend standardse sisendiga, et luua keerukamaid programme.
  • Praktilised ülesanded: loo ise väikseid projekte, võttes eeskujuks artiklis toodud näiteid.

Sõnum lugejale

Standardne sisend on C-keele programmeerimise üks alustalasid, kuid samas ka sügav teema. Selle artikli abil loodetavasti mõistad, kuidas sisendit turvaliselt ja tõhusalt töödelda. Õpi samm-sammult, lahenda väikeseid küsimusi ja liigu järk-järgult edasi – see on kindel tee programmeerimisoskuste arendamiseks.

年収訴求