- 1 1. Sissejuhatus
- 2 2. read funktsiooni põhialused
- 3 3. read funktsiooni kasutusnäited
- 4 4. read funktsiooni rakendused
- 5 5. read funktsiooni kasutamisel tähelepanekud
- 6 6. Korduma kippuvad küsimused (FAQ)
- 6.1 Q1. Mis vahe on read ja fread funktsioonidel?
- 6.2 Q2. Kui read tagastab 0, kas see tähendab viga?
- 6.3 Q3. Kuidas kasutada read funktsiooni mitteblokeerivas režiimis?
- 6.4 Q4. Mida teha, kui read tagastab -1?
- 6.5 Q5. Kuidas töödelda väga suuri faile read funktsiooniga?
- 6.6 Q6. Miks read mõnikord tagastab ainult osa andmetest?
- 6.7 FAQ kokkuvõte
- 7 7. Kokkuvõte
1. Sissejuhatus
C-keele read
funktsioon on süsteemiprogrammeerimises üks kõige põhilisemaid tööriistu. See on madala taseme sisend/väljund funktsioon, mis loeb andmeid otse failidest või seadmetest, võimaldades detailselt kontrollida süsteemi käitumist võrreldes teiste I/O funktsioonidega.
Selles artiklis käsitleme laialdaselt read
funktsiooni – alates põhikasutusest kuni rakenduslike näideteni ning sageli esinevate küsimuste lahendamiseni. Eriti pöörame tähelepanu algajatele keerulistele kohtadele ja praktilistele koodinäidetele ning vahepealse taseme programmeerijatele anname ülevaate asünkroonsest I/O-st ja veakäitlusest. Artikli lõpuks omandad teadmised, mis aitavad read
funktsiooni tõhusalt ja turvaliselt kasutada.
Mis on C-keele read
funktsioon?
read
funktsioon on üks POSIX-standardis määratletud süsteemikutseid ning seda kasutatakse laialdaselt Linuxi ja UNIXi süsteemides. Funktsioon loeb andmeid failikirjeldaja kaudu – näiteks failidest, standard-sisendist või soklitest.
Kuigi read
võimaldab madala taseme toiminguid, võib see algajatele tunduda keeruline, eriti puhvri halduse ja veakäitluse osas. Võrreldes kõrgema taseme funktsioonidega (nt fread
, scanf
), sõltub read
otseselt operatsioonisüsteemi käitumisest. See annab paindlikkust, kuid nõuab hoolikat implementeerimist.
Erinevus teistest I/O funktsioonidest
C-keeles on lisaks read
funktsioonile ka teisi sisend/väljund funktsioone. Vaatame lühidalt nende erinevusi.
Funktsiooni nimi | Tase | Põhikasutus | Omadused |
---|---|---|---|
read | Madal tase | Failide ja seadmete lugemine | Süsteemikõne, kõrge paindlikkus |
fread | Kõrgem tase | Failivoo lugemine | Standardne C-teek, lihtne kasutada |
scanf | Kõrgem tase | Standard-sisendi lugemine | Võimaldab vormingu määramist |
read
funktsioon on eriti kasulik olukordades, kus on vaja madala taseme kontrolli (nt seadmeside või suurte failide töötlemine). Seevastu fread
ja scanf
sobivad paremini lihtsuse ja mugavuse nõudmisel.
Artiklis käsitletavad teemad
Selles artiklis selgitame põhjalikult järgmisi teemasid:
- Põhiline kasutamine
Õpidread
funktsiooni prototüüpi, argumentide tähendust ja tagastusväärtusi. - Konkreetseid näiteid
Näidised failide lugemisest, standard-sisendist ja soklikommunikatsioonist. - Rakendused ja tõrkeotsing
Asünkroonse I/O seadistamine ja parimad praktikad veakäitluses. - Korduma kippuvad küsimused
FAQ-vormis tüüpiliste probleemide lahendused.
Sisu on mõeldud nii algajatele kui ka kesktasemel programmeerijatele.
2. read
funktsiooni põhialused
C-keele read
funktsioon on madala taseme I/O funktsioon, mis loeb andmeid failidest või seadmetest. Selles jaotises selgitame read
funktsiooni põhilisi omadusi koos koodinäidetega.
read
funktsiooni prototüüp
read
funktsiooni prototüüp on järgmine:
ssize_t read(int fd, void *buf, size_t count);
Argumentide selgitus
fd
(failikirjeldaja)
- Määrab sihtkoha, kust lugeda.
- Näiteks
open
funktsiooniga saadud failikirjeldaja või standard-sisend (0
), standard-väljund (1
).
buf
(puhver)
- Mälu aadress, kuhu loetud andmed ajutiselt salvestatakse.
- Puhver peab olema piisavalt suur, et mahutada loetavad andmed.
count
(baitide arv)
- Määrab maksimaalse baitide arvu, mida loetakse.
- Soovitav on määrata väärtus, mis ei ületa puhvri suurust.
Tagastusväärtus
- Edukas lugemine: tagastab loetud baitide arvu (0 tähistab EOF-i).
- Viga: tagastab
-1
ja veainfo on saadavalerrno
kaudu.
Põhiline kasutusnäide
Järgmine on näide, kuidas lugeda andmeid failist.
Koodinäide
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main() {
int fd = open("example.txt", O_RDONLY);
if (fd == -1) {
perror("Faili avamine ebaõnnestus");
return 1;
}
char buffer[128];
ssize_t bytesRead;
while ((bytesRead = read(fd, buffer, sizeof(buffer) - 1)) > 0) {
buffer[bytesRead] = '\0'; // Lisa lõputähis, et käsitleda stringina
printf("%s", buffer);
}
if (bytesRead == -1) {
perror("Faili lugemine ebaõnnestus");
}
close(fd);
return 0;
}
Koodi selgitus
- Faili avamine
open
funktsiooniga
- Avatakse ainult lugemiseks, kasutades
O_RDONLY
. - Kui fail ei avane, kuvatakse veateade.
- Andmete lugemine
read
funktsiooniga
- Loetakse kuni 128 baiti ja salvestatakse
buffer
-isse. - Tagastatakse tegelikult loetud baitide arv.
- Veakäitlus
- Kui faili ei eksisteeri või õigused puuduvad, tagastatakse
-1
.
- Puhvri lõpetamine
- Lisatakse
'\0'
, et töödelda loetud andmeid stringina.
Mida arvestada read
funktsiooni kasutamisel
Puhvri suurus ja turvalisus
- Kui proovida lugeda rohkem kui puhver mahutab, võib tekkida mälukahjustus.
count
väärtus peab olema puhvri suurusest väiksem või sellega võrdne.
EOF (faili lõpp) käsitlemine
- Kui
read
tagastab0
, tähendab see EOF-i. Sellisel juhul pole rohkem andmeid lugeda.
Osaline lugemine
read
ei pruugi alati korraga lugeda määratud baitide arvu. Eriti soklite või torude puhul võib osutuda vajalikuks korduv lugemine tsüklis, kuni kõik andmed on saadud.
3. read
funktsiooni kasutusnäited
Selles jaotises tutvustame mitmeid praktilisi näiteid read
funktsiooni kasutamisest – alates failide lugemisest kuni standard-sisendi ja võrgusoklite andmevahetuseni.
Faili põhjalik lugemine
Alustame lihtsaimast näitest, kuidas lugeda andmeid failist. read
sobib nii tekstifailide kui ka binaarfailide töötlemiseks.
Koodinäide: Tekstifaili lugemine
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main() {
int fd = open("example.txt", O_RDONLY);
if (fd == -1) {
perror("Faili avamine ebaõnnestus");
return 1;
}
char buffer[128];
ssize_t bytesRead;
while ((bytesRead = read(fd, buffer, sizeof(buffer) - 1)) > 0) {
buffer[bytesRead] = '\0';
printf("%s", buffer);
}
if (bytesRead == -1) {
perror("Faili lugemine ebaõnnestus");
}
close(fd);
return 0;
}
Koodi selgitus
- Faili avamine
open
funktsioon avab faili ainult lugemiseks. Kui avamine ebaõnnestub, kuvatakse viga.
- Lugemine tsüklis
read
abil
- Failist loetakse andmeid kuni EOF-ini. Iga kord salvestatakse puhvri lõppu
'\0'
.
- Veakäitlus
- Kui
read
tagastab-1
, on tekkinud viga ja see väljastatakseperror
abil.
- Faili sulgemine
- Pärast lugemist sulgetakse fail
close
funktsiooniga.
Andmete lugemine standard-sisendist
Järgmine näide näitab, kuidas lugeda kasutaja sisestatud andmeid klaviatuurilt. Seda meetodit kasutatakse tihti CLI-tööriistades.
Koodinäide: Kasutaja sisendi lugemine
#include <unistd.h>
#include <stdio.h>
int main() {
char buffer[64];
printf("Sisesta tekst: ");
ssize_t bytesRead = read(0, buffer, sizeof(buffer) - 1); // 0 = stdin
if (bytesRead == -1) {
perror("Sisendi lugemine ebaõnnestus");
return 1;
}
buffer[bytesRead] = '\0';
printf("Sisestasid: %s\n", buffer);
return 0;
}
Koodi selgitus
- Standard-sisendi määramine
read
esimene argument on0
, mis tähistab stdin-i.
- Puhvri lõpetamine
- Pärast lugemist lisatakse
'\0'
, et käsitleda sisendit stringina.
- Veakäitlus
- Kui lugemine ebaõnnestub, kuvatakse viga
perror
abil.
Andmete vastuvõtt sokli kaudu
read
funktsiooni saab kasutada ka võrguprogrammeerimises. Näites loome lihtsa serveri, mis võtab vastu kliendi saadetud sõnumi.
Koodinäide: Andmete lugemine soklist
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main() {
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {
perror("Sokli loomine ebaõnnestus");
return 1;
}
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(8080);
if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) == -1) {
perror("Bind ebaõnnestus");
close(server_fd);
return 1;
}
if (listen(server_fd, 3) == -1) {
perror("Listen ebaõnnestus");
close(server_fd);
return 1;
}
int client_fd = accept(server_fd, NULL, NULL);
if (client_fd == -1) {
perror("Accept ebaõnnestus");
close(server_fd);
return 1;
}
char buffer[1024];
ssize_t bytesRead = read(client_fd, buffer, sizeof(buffer) - 1);
if (bytesRead > 0) {
buffer[bytesRead] = '\0';
printf("Sõnum saadud: %s\n", buffer);
} else if (bytesRead == -1) {
perror("Lugemine ebaõnnestus");
}
close(client_fd);
close(server_fd);
return 0;
}
Koodi selgitus
- Sokli loomine
socket
funktsioon loob TCP sokli.
- Aadressi sidumine
- Sokkel seotakse IP-aadressi ja pordiga.
- Ühenduse ootamine
listen
ootab klientide ühendusi.
- Kliendi ühenduse vastuvõtt
accept
võtab ühenduse vastu ja loob uue failikirjeldaja kliendi jaoks.
- Andmete lugemine
- Kliendi saadetud andmed loetakse
read
abil ja väljastatakse.
Näidete kokkuvõte
Need näited näitavad, et read
funktsioon ei piirdu ainult failide töötlemisega, vaid seda saab rakendada ka standard-sisendis ja võrgusidet kasutavates programmides. Soklite puhul mängib read
võtmerolli andmete vastuvõtmisel.
4. read
funktsiooni rakendused
read
funktsiooni saab kasutada mitte ainult põhiliseks failitöötluseks, vaid ka keerukamate programmeerimisülesannete lahendamiseks. Selles jaotises käsitleme näiteid nagu asünkroonne I/O, suurte andmemahtude efektiivne töötlemine ja binaarsete andmete lugemine.
Asünkroonse I/O kasutamine
Asünkroonse I/O puhul saab read
funktsioon tagastada kohe, ilma et ootaks andmete saabumist, võimaldades programmil paralleelselt täita muid ülesandeid. See parandab rakenduse jõudlust.
Asünkroonse režiimi seadistamine
Asünkroonse töö lubamiseks kasutatakse fcntl
funktsiooni, millega failikirjeldajale määratakse mitteblokeeriv režiim.
Koodinäide: Asünkroonne I/O
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
int main() {
int fd = open("example.txt", O_RDONLY);
if (fd == -1) {
perror("Faili avamine ebaõnnestus");
return 1;
}
// Mitteblokeeriva režiimi seadistamine
int flags = fcntl(fd, F_GETFL, 0);
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
perror("Mitteblokeeriva režiimi seadmine ebaõnnestus");
close(fd);
return 1;
}
char buffer[128];
ssize_t bytesRead;
while ((bytesRead = read(fd, buffer, sizeof(buffer) - 1)) != 0) {
if (bytesRead > 0) {
buffer[bytesRead] = '\0';
printf("Loetud andmed: %s\n", buffer);
} else if (bytesRead == -1 && errno == EAGAIN) {
printf("Hetkel andmeid pole, proovi hiljem uuesti\n");
} else if (bytesRead == -1) {
perror("Lugemisviga");
break;
}
}
close(fd);
return 0;
}
Koodi selgitus
- Mitteblokeeriva režiimi määramine
fcntl
abil lisatakseO_NONBLOCK
lipp failikirjeldajale.
- Veakäitlus
- Kui andmeid pole veel saadaval, seatakse
errno
väärtuseksEAGAIN
võiEWOULDBLOCK
.
- Korduv lugemine
- Asünkroonses režiimis tuleb vajadusel
read
funktsiooni kutsuda korduvalt.
Suurte andmete efektiivne lugemine
Suurte failide töötlemisel on oluline puhvri suuruse ja mälu kasutuse optimeerimine.
Tehnika 1: Puhvri suuruse optimeerimine
- Suurem puhver vähendab süsteemikõnede arvu ja parandab jõudlust.
- Üldiselt soovitatakse kasutada süsteemi lehe suurust (
getpagesize()
abil).
Koodinäide: Suur puhver
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
int fd = open("largefile.bin", O_RDONLY);
if (fd == -1) {
perror("Faili avamine ebaõnnestus");
return 1;
}
size_t bufferSize = 4096; // 4KB
char *buffer = malloc(bufferSize);
if (!buffer) {
perror("Puhvri eraldamine ebaõnnestus");
close(fd);
return 1;
}
ssize_t bytesRead;
while ((bytesRead = read(fd, buffer, bufferSize)) > 0) {
printf("Loetud %zd baiti\n", bytesRead);
// Lisa vajadusel töötlemisloogika
}
if (bytesRead == -1) {
perror("Lugemisviga");
}
free(buffer);
close(fd);
return 0;
}
Binaarsete andmete lugemine
read
funktsioon sobib hästi ka binaarfailide (nt pildid või käivitatavad failid) lugemiseks. Selliste andmete töötlemisel tuleb arvestada baitide järjekorra (endianness) ja struktuuride joondamisega.
Koodinäide: Binaarfaili lugemine
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>
typedef struct {
uint32_t id;
float value;
} DataRecord;
int main() {
int fd = open("data.bin", O_RDONLY);
if (fd == -1) {
perror("Faili avamine ebaõnnestus");
return 1;
}
DataRecord record;
ssize_t bytesRead;
while ((bytesRead = read(fd, &record, sizeof(record))) > 0) {
printf("ID: %u, Väärtus: %.2f\n", record.id, record.value);
}
if (bytesRead == -1) {
perror("Lugemisviga");
}
close(fd);
return 0;
}
Koodi selgitus
- Struktuuri lugemine
- Kasutatakse
read
funktsiooni, et lugeda korraga terve struktuur.
- Andmete töötlemine
- Struktuuri liikmeid saab otse kasutada andmete väljastamiseks või töötlemiseks.
- Endianness
- Kui fail on loodud teises platvormis, võib osutuda vajalikuks baitide järjekorra teisendamine.
Rakendusnäidete kokkuvõte
read
funktsiooni rakendamine võimaldab lahendada keerukaid programmeerimisülesandeid efektiivselt. Asünkroonne I/O suurendab ressursikasutuse tõhusust ning suure andmemahu või binaarfailide lugemine muutub paindlikumaks ja töökindlamaks.
5. read
funktsiooni kasutamisel tähelepanekud
read
on paindlik ja võimas tööriist, kuid selle kasutamisel tuleb arvestada mitmete oluliste aspektidega. Selles jaotises selgitame, kuidas kasutada read
funktsiooni turvaliselt ja tõhusalt.
Puhvri ületäitumise vältimine
Kui read
loeb rohkem andmeid, kui puhver mahutab, võib tekkida mälurike (buffer overflow). See võib põhjustada programmi krahhi või turvanõrkusi.
Kuidas ennetada
- Õige puhvri suurus
- Puhver peaks olema piisavalt suur eeldatava andmemahu jaoks.
count
argumendina tuleks alati anda väärtus, mis on väiksem või võrdne puhvri suurusega.
- Puhvri lõpetamine
- Kui töötled andmeid stringina, lisa alati lõppu
'\0'
.
Koodinäide: Turvaline puhvri kasutus
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main() {
char buffer[128];
int fd = open("example.txt", O_RDONLY);
if (fd == -1) {
perror("Faili avamine ebaõnnestus");
return 1;
}
ssize_t bytesRead = read(fd, buffer, sizeof(buffer) - 1);
if (bytesRead == -1) {
perror("Faili lugemine ebaõnnestus");
close(fd);
return 1;
}
buffer[bytesRead] = '\0'; // Lisa lõputähis
printf("Loetud andmed: %s\n", buffer);
close(fd);
return 0;
}
EOF (faili lõpp) käsitlemine
Kui read
tagastab väärtuse 0
, tähendab see, et jõuti faili lõppu (EOF). See on normaalne olukord. Kui EOF-i ei käsitleta õigesti, võib programm jääda lõputusse tsüklisse.
Õige lähenemine EOF-i käsitlemisel
- Tagastusväärtuse kontrollimine
- Kui väärtus on
0
, pole enam andmeid lugeda.
- Tsükli tingimus
- EOF-i töötlemiseks kasuta tsüklis tingimust
bytesRead > 0
.
Koodinäide: EOF käsitlemine
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main() {
char buffer[128];
int fd = open("example.txt", O_RDONLY);
if (fd == -1) {
perror("Faili avamine ebaõnnestus");
return 1;
}
ssize_t bytesRead;
while ((bytesRead = read(fd, buffer, sizeof(buffer) - 1)) > 0) {
buffer[bytesRead] = '\0';
printf("Loetud: %s\n", buffer);
}
if (bytesRead == -1) {
perror("Lugemisviga");
}
close(fd);
return 0;
}
Osaline lugemine ja tõrkeotsing
read
ei pruugi alati korraga lugeda kõiki määratud baite. See on eriti tavaline soklite ja torude puhul, kus andmete saabumine võib viibida.
Põhjused
- Signaalide katkestus
- Kui süsteemikõne katkestatakse signaaliga, võib lugemine peatuda.
- Mitteblokeeriv režiim
- Mitteblokeerivas režiimis tagastab
read
kohe, isegi kui andmeid veel pole.
- Puhvri suuruse piirang
- Kui andmed ei mahu korraga puhvri sisse, tuleb neid lugeda mitu korda.
Lahendused
- Korduv lugemine
- Tee tsükkel, mis loeb andmeid seni, kuni kogu info on käes.
- Veakoodide kontrollimine
- Kasuta
errno
väärtusi naguEINTR
jaEAGAIN
, et rakendada õige käitumine.
Koodinäide: Osalise lugemise käsitlemine
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
int main() {
char buffer[128];
int fd = open("example.txt", O_RDONLY);
if (fd == -1) {
perror("Faili avamine ebaõnnestus");
return 1;
}
ssize_t bytesRead;
size_t totalBytesRead = 0;
while ((bytesRead = read(fd, buffer + totalBytesRead, sizeof(buffer) - totalBytesRead - 1)) > 0) {
totalBytesRead += bytesRead;
}
if (bytesRead == -1 && errno != EINTR) {
perror("Lugemisviga");
} else {
buffer[totalBytesRead] = '\0';
printf("Kokku loetud: %s\n", buffer);
}
close(fd);
return 0;
}
Tähelepanekute kokkuvõte
- Puhvri suurus: määra alati piisavalt suur puhver ja hoia turvalisus.
- EOF tuvastamine: kasuta õigesti tagastusväärtust, et vältida lõputuid tsükleid.
- Osaline lugemine: kasuta korduvat lugemist ja kontrolli veakoode.
Nende põhimõtete järgimine aitab read
funktsiooni kasutada turvaliselt ja tõhusalt.
6. Korduma kippuvad küsimused (FAQ)
Siin käsitleme C-keele read
funktsiooniga seotud tüüpilisi küsimusi ja lahendusi. Need aitavad nii algajatel kui ka kesktasemel programmeerijatel süvendada arusaamist funktsiooni kasutamisest.
Q1. Mis vahe on read
ja fread
funktsioonidel?
Vastus:
read
funktsioon:- Süsteemikõne, mis suhtleb otse operatsioonisüsteemiga.
- Kasutab failikirjeldajat ja võimaldab madala taseme I/O-d.
- Paindlik, kuid nõuab veakäitlust ja puhvri haldamist.
fread
funktsioon:- Kõrgema taseme funktsioon standardses C-teegis.
- Kasutab failipointerit ja töötab voogude tasandil.
- Puhvri haldus toimub automaatselt, lihtsam kasutada.
Millal kumbagi kasutada:
read
: sobib süsteemiprogrammeerimiseks ja soklite puhul, kui on vaja madalat taset kontrollida.fread
: sobib tavaliste failide lugemiseks, kus mugavus on tähtsam kui paindlikkus.
Q2. Kui read
tagastab 0, kas see tähendab viga?
Vastus:
Ei, kui read
tagastab 0
, tähendab see EOF (End of File – faili lõpp). See on normaalne ja näitab, et kõik andmed on loetud.
Kuidas käituda:
- Kui
read
tagastab 0, tuleb lugemisprotsess lõpetada. - Tsüklis kasutades kasuta tingimust
bytesRead > 0
.
Q3. Kuidas kasutada read
funktsiooni mitteblokeerivas režiimis?
Vastus:
Mitteblokeerivas režiimis tagastab read
kohe, isegi kui andmeid veel pole. Selle seadistamiseks kasutatakse fcntl
funktsiooni.
Koodinäide:
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
int main() {
int fd = open("example.txt", O_RDONLY);
if (fd == -1) {
perror("Faili avamine ebaõnnestus");
return 1;
}
// Mitteblokeeriva režiimi seadmine
int flags = fcntl(fd, F_GETFL, 0);
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
perror("Režiimi muutmine ebaõnnestus");
close(fd);
return 1;
}
char buffer[128];
ssize_t bytesRead = read(fd, buffer, sizeof(buffer));
if (bytesRead == -1 && errno == EAGAIN) {
printf("Andmed pole hetkel saadaval\n");
} else if (bytesRead > 0) {
buffer[bytesRead] = '\0';
printf("Loetud andmed: %s\n", buffer);
}
close(fd);
return 0;
}
Märkus:
- Kui andmeid pole, tagastab
read
-1
jaerrno
väärtuseks saabEAGAIN
võiEWOULDBLOCK
. - Mitteblokeeriv režiim eeldab sageli sündmustepõhist programmeerimist.
Q4. Mida teha, kui read
tagastab -1
?
Vastus:
Kui read
tagastab -1
, tähendab see viga. Täpne põhjus on leitav globaalsest muutujast errno
.
Levinumad veakoodid:
EINTR
: lugemine katkestati signaaliga – proovige uuesti.EAGAIN
võiEWOULDBLOCK
: mitteblokeerivas režiimis pole andmeid saadaval.- Muud vead: nt vigane failikirjeldaja (
EBADF
).
Näide:
if (bytesRead == -1) {
if (errno == EINTR) {
// Korda lugemist
} else {
perror("Lugemine ebaõnnestus");
}
}
Q5. Kuidas töödelda väga suuri faile read
funktsiooniga?
Vastus:
Suurte failide puhul tuleb lugeda andmeid osade kaupa.
- Jaga lugemine plokkideks
- Määra puhver ja loe andmeid korduvate
read
väljakutsetega.
- Mälu kasutuse optimeerimine
- Kasuta
malloc
funktsiooni, kui vajad dünaamilist puhvrit.
Koodinäide:
char buffer[4096];
while ((bytesRead = read(fd, buffer, sizeof(buffer))) > 0) {
// Töötle andmeid
}
Q6. Miks read
mõnikord tagastab ainult osa andmetest?
Vastus:
Võimalikud põhjused:
- Osaline lugemine
- Eriti soklite ja torude puhul ei pruugi kogu andmehulk olla korraga saadaval.
- Signaalide katkestus
- Lugemine võib katkeda signaali tõttu.
- Mitteblokeeriv režiim
- Kui andmeid pole piisavalt, tagastatakse kohe.
Kuidas lahendada:
- Korda
read
väljakutset seni, kuni kogu vajalik andmehulk on loetud. - Käsitle veakoode õigesti (nt
EINTR
,EAGAIN
).
FAQ kokkuvõte
Need küsimused ja vastused aitavad lahendada tüüpilisi probleeme, mis tekivad read
funktsiooni kasutamisel. Eriti oluline on mõista veakäitlust, mitteblokeeriva režiimi käitumist ja EOF-i töötlemist.
7. Kokkuvõte
Selles artiklis vaatasime põhjalikult üle C-keele read
funktsiooni – alates põhikasutusest kuni rakenduslike näidete, tähelepanekute ja KKK-ni. Järgnevalt kordame peamised punktid.
read
funktsiooni põhiülevaade
- Üldine kirjeldus:
read
on madala taseme I/O funktsioon, mis loeb andmeid failikirjeldaja abil. - Süntaks:
ssize_t read(int fd, void *buf, size_t count);
- Põhiomadused:
- Paindlik ja sobib failide, seadmete ning soklite andmeside jaoks.
- On süsteemikõne, mis eeldab veakäitlust ja puhvri haldamist.
Peamised kasutusnäited
- Failist lugemine:
Faili avamine ja sisu lugemine tsükli abil kuni EOF-ini. - Standard-sisend:
Kasutaja sisendi lugemine ja töötlemine. - Soklite puhul:
Serveri ja kliendi vahelise andmeside näited.
Rakenduslik kasutus
- Asünkroonne I/O:
Failikirjeldaja seadistamine mitteblokeerivaksfcntl
abil. - Suurte andmete töötlemine:
Optimeeritud puhvrisuuruse ja mälu kasutuse olulisus. - Binaarsete andmete lugemine:
Struktuuride lugemine ja endianness’i arvestamine.
Tähtsad tähelepanekud ja tõrkeotsing
- Puhvri ületäitumise vältimine:
Seadista puhvri suurus õigesti. - EOF töötlemine:
read
tagastab0
faili lõpus. - Osaline lugemine:
Soklite ja mitteblokeeriva režiimi korral kasuta korduvat lugemist.
KKK-st lahendatud peamised küsimused
read
vsfread
read
= madala taseme I/O,fread
= kõrgema taseme I/O.
- Mitteblokeeriv režiim
- Kasuta
fcntl
ja kontrollierrno
väärtusi.
- Veakäitluse parimad praktikad
- Käsitle
errno
koodid õigesti (EINTR
,EAGAIN
jne).
Mida sellest artiklist õppisid
- Põhikasutus:
Kuidas lugeda andmeid failidest ja sisendseadmetest turvaliselt. - Rakendused:
Asünkroonne I/O, suurte ja binaarsete andmete töötlemine. - Veakäitlus:
Kuidas käsitleda osalist lugemist ja EOF-i.
Järgmised sammud õppimiseks
Pärast read
funktsiooniga tutvumist on kasulik edasi õppida järgmisi teemasid:
write
funktsioon:
Madala taseme andmete kirjutamine failidesse ja seadmetesse.open
jaclose
funktsioonid:
Failihalduse põhitõed.- Asünkroonne programmeerimine:
Sündmustepõhine arhitektuur ja efektiivsem I/O töötlemine.
Lõppsõna
read
funktsioon on C-keeles failide ja seadmete käsitlemisel hädavajalik tööriist. Selle paindlikkus ja jõudlus tulevad hästi esile, kui seda kasutatakse õigesti ja turvaliselt. Loodetavasti aitab see artikkel nii algajatel kui ka kesktasemel programmeerijatel read
funktsiooni täiel määral omandada.