- Leren
- Informatica & ICT
- Programmeren
- Programmeren in C: pointers, geheugenbeheer en debuggen
- Bestandsinvoer en -uitvoer in C
Bestandsinvoer en -uitvoer in C
fgets. Sla arrays en records met fread en fwrite op in binaire bestanden, ontwerp bestandsformaten die op andere computers correct worden gelezen en spring met fseek naar een willekeurig record. Daarbij controleer je elk resultaat, verklaar je fouten met errno en ruim je op, zodat je programma’s veilig falen en hun resultaat eerlijk melden.BeginnerLes 7 van 8 in Cursus Programmeren in C: pointers, geheugenbeheer en debuggen
Wat je leert
- Leg uit hoe C-programma’s bestanden bereiken via gebufferde gegevensstromen, open en sluit bestanden in de juiste modus en gebruik de drie standaardstromen met omleiding via de opdrachtregel.
- Leg gegevensstromen, buffering en het verschil tussen tekstmodus en binaire modus uitBeschrijf wat een gegevensstroom en een
FILE *zijn, hoe buffering en de bestandspositie werken en hoe de tekstmodus verschilt van de binaire modus op Windows en Unix-achtige systemen. - Open en sluit bestanden met fopen en fcloseOpen een bestand in de modus die bij de taak past, controleer of het is geopend, vind bestanden via een pad en sluit elke gegevensstroom als het werk klaar is.
- Gebruik stdin, stdout en stderr met omleiding en pipesGebruik de drie gegevensstromen waarmee elk C-programma begint, stuur resultaten en foutmeldingen naar de juiste plaats en laat gebruikers die vanaf de opdrachtregel omleiden.
- Leg gegevensstromen, buffering en het verschil tussen tekstmodus en binaire modus uit
- Verwerk tekstbestanden teken voor teken, met opgemaakte lees- en schrijfbewerkingen en regel voor regel. Kies voor elk een veilig patroon en controleer elk resultaat.
- Lees en schrijf teken voor tekenGebruik fgetc en fputc met een int-variabele, stop correct aan het bestandseinde en tel, kopieer en verander tekst teken voor teken.
- Schrijf en lees opgemaakte tekst met fprintf en fscanfSchrijf getallen en strings in een gekozen opmaak naar bestanden, bouw bestandsnamen met sprintf en lees opgemaakte gegevens met fscanf terwijl je controleert hoeveel items zijn geconverteerd.
- Lees regels veilig met fgetsLees tekst regel voor regel met fgets, handel nieuweregeltekens, lange regels en Windows-regeleinden af en analyseer elke regel na het lezen.
- Lees en schrijf teken voor teken
- Schrijf en lees ruwe bytes, arrays en structs met fwrite en fread en ontwerp binaire bestandsformaten die op andere computers correct worden gelezen.
- Lees en schrijf binaire gegevens met fread en fwriteGebruik fread en fwrite met de juiste elementgrootte en het juiste aantal, kopieer bestanden in blokken, herken bestandstypen aan hun eerste bytes en sla arrays en structs op.
- Ontwerp binaire bestandsformaten die op verschillende computers werkenLeg uit hoe typegroottes, bytevolgorde en opvulling in structs ruwe binaire bestanden afhankelijk maken van de computer en lees en schrijf velden van meerdere bytes in een vastgelegd formaat.
- Lees en schrijf binaire gegevens met fread en fwrite
- Bepaal en wijzig de bestandspositie om rechtstreeks naar de benodigde gegevens te springen en werk records van vaste grootte op hun bestaande positie bij zonder het hele bestand opnieuw te schrijven.
- Bepaal en wijzig de bestandspositie met ftell, fseek en rewindVraag de huidige positie op, verplaats de positie ten opzichte van het begin, de huidige plaats of het einde, bereken offsets van records en bestandsgroottes en ken de beperkingen voor tekststromen.
- Werk records van vaste grootte op hun bestaande bestandspositie bijOpen een bestand om zowel te lezen als te schrijven, volg de regel voor het wisselen tussen beide en wijzig, voeg records toe of verwijder ze zonder het bestand te beschadigen.
- Bepaal en wijzig de bestandspositie met ftell, fseek en rewind
- Controleer het resultaat van elke bestandsbewerking, verklaar fouten aan de gebruiker met errno en geef elke hulpbron vrij, zodat programma’s veilig falen en succes eerlijk melden.
- Controleer elk resultaat en onderscheid het bestandseinde van foutenWeet hoe elke stdio-functie een fout meldt, gebruik feof en ferror nadat een leeslus eindigt en vang schrijffouten af die pas zichtbaar worden wanneer de buffer wordt weggeschreven.
- Verklaar fouten met errno, perror en strerrorGebruik errno om te achterhalen waarom een bibliotheekaanroep is mislukt en druk duidelijke foutmeldingen af die het programma, het bestand en de reden noemen.
- Ruim hulpbronnen op en meld het resultaatSluit elke gegevensstroom op elk uitvoeringspad, laat geen halfgeschreven uitvoer achter en geef een afsluitstatus terug die het resultaat eerlijk weergeeft.
- Controleer elk resultaat en onderscheid het bestandseinde van fouten
Bestandsinvoer en -uitvoer in C: uitleg
1Open bestanden als gegevensstromen en verbind programma’s met hun invoer en uitvoer
Bestanden bereiken een programma via gegevensstromen, die de toegang, buffering en positie regelen. De keuze van een modus kan oude gegevens behouden, wissen of elke schrijfbewerking naar het einde sturen. Openen hoort daarom bij het ontwerp van het programma. De drie standaardstromen breiden dezelfde interface uit naar terminals, omgeleide bestanden en pipes. Als je hun verbindingen begrijpt, kun je één verwerkingsroutine schrijven die in meerdere situaties werkt en diagnostische meldingen gescheiden houdt van bruikbare resultaten.
1.1 Leg gegevensstromen, buffering en het verschil tussen tekstmodus en binaire modus uit
Een bestand bestaat uit opgeslagen gegevens; een gegevensstroom is het kanaal dat een draaiend programma gebruikt om die te bereiken. Dezelfde functies voor gegevensstromen kunnen ook met een terminal of pipe werken. Neem <stdio.h> op en bewaar de gegevensstroompointer in een variabele van het type FILE *.
Wat de gegevensstroom onthoudt
Het FILE-object van de bibliotheek houdt een buffer, de huidige bestandspositie en indicatoren voor het bestandseinde en fouten bij. Geef de pointer door aan functies zoals fgetc en fprintf; bekijk of wijzig de velden niet. Een lees- of schrijfbewerking schuift de positie voorbij de verwerkte gegevens. Als een binair bestand begint met "hello\nworld\n", begint de volgende leesbewerking na het lezen van de eerste zes bytes bij de w.
Buffering groepeert kleine bewerkingen tot grotere overdrachten. Een geslaagde schrijfbewerking kan betekenen dat gegevens een geheugenbuffer hebben bereikt, terwijl de overdracht naar het besturingssysteem nog moet plaatsvinden. fflush(fp) stuurt uitvoer die nog in de buffer staat naar het besturingssysteem. fclose(fp) doet dat voordat de gegevensstroom wordt vrijgegeven. Geen van beide garandeert dat gegevens een plotselinge stroomuitval overleven.
| Bufferingsmodus | Wanneer uitvoer wordt overgedragen |
|---|---|
| Volledig gebufferd | Wanneer de buffer vol raakt, of expliciet wordt leeggemaakt of gesloten |
| Regelgebufferd | Ook wanneer een nieuweregelteken wordt geschreven |
| Ongebufferd | Zo snel mogelijk |
Gewone bestanden die met fopen zijn geopend, zijn volledig gebufferd. Interactieve apparaten kunnen andere buffering gebruiken. Een terminal of pipe biedt geen gewone bestandsposities om achteruit en vooruit te springen.
Kies tekstmodus of binaire modus
Tekstmodus verwerkt voor mensen leesbare regels. Binaire modus behoudt bytewaarden zonder tekstvertalingen. Voeg b toe aan de openingsmodus voor binaire gegevens: "rb" leest ze en "wb" schrijft ze. Afbeeldingen, gecomprimeerde gegevens en ruwe records vereisen binaire modus, ook als een versie in tekstmodus op je eigen computer lijkt te werken.
Op Linux en macOS is een nieuweregelteken één LF-byte, 0x0A, en gedragen de twee modi zich normaal gesproken hetzelfde. In Windows vertaalt tekstuitvoer '\n' naar de twee bytes CR LF, 0x0D 0x0A. Tekstinvoer vertaalt CR LF terug naar één '\n'. De tekstinvoer van Microsoft herkent ook 0x1A, Ctrl+Z, als bestandseinde.
Als je bijvoorbeeld "a\nb\nc\n" in tekstmodus schrijft, levert dat zes bytes op Linux of macOS en negen op Windows op. Drie letters nemen drie bytes in; drie Windows-regeleinden nemen zes bytes in. Als je het Windows-bestand op Windows in tekstmodus leest, krijg je zes tekens, terwijl binaire modus alle negen bytes zichtbaar maakt.
Houd de bytes van het bestand gescheiden van de tekst
Een op Windows gemaakte regel die op Linux wordt gelezen, kan "\r\n" behouden. Als je alleen '\n' verwijdert, blijft '\r' staan, waardoor een vergelijking met "yes" kan mislukken. Een positie in een tekststroom kan ook de vertaaltoestand weerspiegelen. Sla tekstposities daarom op en herstel ze in plaats van byte-offsets te berekenen.
De garantie van C dat tekst exact kan worden teruggelezen, geldt voor afdrukbare tekens, tabs en nieuweregeltekens, zonder spatie direct vóór een nieuweregelteken en met een nieuweregelteken aan het einde van de laatste regel. Andere bytereeksen horen in binaire gegevensstromen. C-bytes hebben ten minste acht bits; voorbeelden met bytewaarden van 0–255 en gangbare bestandsformaten gebruiken bytes van acht bits.
Belangrijkste punten
- Een
FILE *geeft bibliotheekfuncties toegang tot een gegevensstroom en de toestand ervan. - Gebufferde uitvoer kan falen bij het leegmaken van de buffer of het sluiten; controleer die resultaten.
- De Windows-tekstmodus vertaalt regeleinden; de binaire modus behoudt bytes.
- Sla tekstposities op; bereken offsets in binaire bestanden.
- Maak de aanname van bytes van acht bits expliciet wanneer je gangbare binaire formaten interpreteert.
1.2 Open en sluit bestanden met fopen en fclose
Kies een openingsmodus voordat je verwerkingscode schrijft. De modus bepaalt of het bestand moet bestaan, of de inhoud behouden blijft en waar schrijfbewerkingen terechtkomen. fopen werkt direct op het bestand: een geslaagde opening met "w" wist een bestaand bestand, ook als er daarna niet wordt geschreven.
Kies de modus die bij de taak past
| Modus | Bestaand bestand | Ontbrekend bestand | Toegang |
|---|---|---|---|
"r" | Behoudt inhoud | Faalt | Lezen vanaf het begin |
"w" | Wist inhoud | Maakt aan | Schrijven vanaf het begin |
"a" | Behoudt inhoud | Maakt aan | Schrijven aan het einde |
"r+" | Behoudt inhoud | Faalt | Lezen en schrijven |
"w+" | Wist inhoud | Maakt aan | Lezen en schrijven |
"a+" | Behoudt inhoud | Maakt aan | Overal lezen; schrijven aan het einde |
Voeg b toe voor binaire modus; "r+b" en "rb+" betekenen hetzelfde. C11 voegt exclusieve bestandsaanmaak met x toe aan schrijfmodi, zoals "wx" en "wbx": het openen faalt als het bestand bestaat. Gebruik een gedocumenteerde modusstring; extra modusletters zijn niet overdraagbaar.
Controleer voordat je de pointer gebruikt
fopen geeft bij succes een nieuwe gegevensstroompointer terug en bij een fout NULL. Ontbrekende bestanden, ontoegankelijke paden en ontbrekende mappen kunnen allemaal een fout veroorzaken.
FILE *fp = fopen("scores.txt", "r");
if (fp == NULL) {
fprintf(stderr, "Cannot open scores.txt\n");
return EXIT_FAILURE;
}
/* Read through fp here. */
if (fclose(fp) == EOF) {
fprintf(stderr, "Cannot close scores.txt\n");
return EXIT_FAILURE;
}
Het codefragment gebruikt <stdio.h> en <stdlib.h>. Functies zoals fgetc, fprintf en fclose hebben een geldige gegevensstroom nodig; NULL doorgeven leidt tot ongedefinieerd gedrag. fflush(NULL) is een bewuste uitzondering die alle uitvoerstromen leegmaakt.
fclose geeft 0 terug bij succes en EOF bij een fout. Na de aanroep is de gegevensstroom afgesloten, ook als het sluiten een fout meldt. Probeer fclose niet opnieuw op dezelfde pointer en gebruik die niet voor verdere invoer of uitvoer. Sluit bestanden op tijd: het aantal beschikbare gegevensstromen is beperkt en C garandeert ondersteuning voor ten minste acht open gegevensstromen, inclusief de drie standaardstromen.
Bepaal paden vanuit het draaiende programma
Een relatief pad zoals "data/input.txt" begint bij de huidige werkmap, niet bij de map van het bronbestand. Als je het programma ergens anders start, verwijst dat pad naar iets anders. Voorwaartse schuine strepen werken ook met de C-bibliotheek van Microsoft. Een achterwaartse schuine streep in een C-string moet worden verdubbeld, zoals in "C:\\data\\in.txt"; anders kan een escapesequentie zoals \n de string veranderen.
Gebruik voor namen die op de opdrachtregel worden opgegeven int main(int argc, char *argv[]). Bij een kopieeropdracht met twee namen moet argc 3 zijn, is argv[1] de invoernaam en argv[2] de uitvoernaam. Open eerst de invoer, zodat een verkeerd gespelde invoernaam geen uitvoerbestand aanmaakt of wist. Als het openen van de uitvoer mislukt, sluit dan de al geopende invoer. Gebruik verschillende invoer- en uitvoerbestanden: eerst de invoer openen beschermt die niet als de uitvoernaam naar hetzelfde bestand verwijst.
Normaal terugkeren uit main of exit sluit gegevensstromen, maar door ze expliciet te sluiten kan het programma vertraagde uitvoerfouten detecteren. Bij een crash kunnen gebufferde gegevens verloren gaan; abort biedt niet de opruimgarantie van een normale afsluiting.
Belangrijkste punten
- Openen met
"w"wist een bestaand bestand direct. - Controleer
fopenopNULLvoordat je de gegevensstroom gebruikt. - Relatieve paden beginnen bij de huidige werkmap.
- Sluit elke geopende gegevensstroom eenmaal en gebruik die nooit opnieuw na
fclose. - Open bij kopiëren de invoer vóór de uitvoer en gebruik verschillende bestanden.
- Exclusieve schrijfmodi uit C11 falen als het doelbestand bestaat.
1.3 Gebruik stdin, stdout en stderr met omleiding en pipes
Een C-programma begint met drie open gegevensstromen: standaardinvoer stdin, standaarduitvoer stdout en standaardfoutuitvoer stderr. Ze zijn meestal verbonden met de terminal, maar de shell kan ze verbinden met bestanden of andere programma’s. Je kunt ze rechtstreeks doorgeven aan functies voor gegevensstromen.
Houd resultaten gescheiden van diagnostische meldingen
printf schrijft naar stdout; scanf leest uit stdin. getchar() leest een teken uit stdin en putchar(c) schrijft er een naar stdout. puts(s) schrijft een string naar stdout en voegt een nieuweregelteken toe. Met hun vormen voor gegevensstromen kan dezelfde code met een bestand werken:
void print_total(FILE *out, int total)
{
fprintf(out, "Total: %d\n", total);
}
De aanroep print_total(stdout, 12) drukt een resultaat af; als je een pointer naar een geopend bestand doorgeeft, gaat dezelfde opmaak naar een bestand. Laat de hulpfunctie in productiecode een mislukte schrijfbewerking melden aan de aanroepende functie.
Schrijf diagnostische meldingen met fprintf(stderr, ...). Een programma dat gegevens verwerkt, kan dan een schoon resultatenbestand maken en fouten afzonderlijk verklaren. Een foutmelding op stdout kan de gegevens beschadigen die een ander programma verwacht te lezen.
Laat de shell de verbindingen kiezen
In een Unix-achtige shell scheidt deze opdracht alle drie de gegevensstromen:
./sortnums < numbers.txt > sorted.txt 2> errors.txt
< levert invoer, > maakt uitvoer aan of vervangt die, >> voegt uitvoer toe en 2> leidt foutmeldingen om. De omleidingsoperatoren werken ook in de Windows-opdrachtprompt, waar de programmanaam vaak wordt geschreven als sortnums.exe. Een pipe, zoals in generate | sortnums, stuurt de standaarduitvoer van het eerste programma naar de standaardinvoer van het tweede programma. Standaard wordt stderr niet meegenomen.
Een filter kan een optionele invoernaam accepteren en anders stdin lezen. Houd het eigenaarschap bij: sluit een gegevensstroom die je functie heeft geopend, maar laat een geleende gegevensstroom over aan de eigenaar. Voor een eenvoudig opdrachtregelfilter herkent if (in != stdin) het bestand dat het zelf heeft geopend.
Maak de uitvoerbuffer leeg voordat je op invoer wacht
Een terminal maakt stdout doorgaans regelgebufferd. Een omgeleid bestand of een pipe maakt die doorgaans volledig gebufferd. stderr is niet volledig gebufferd. Dit verklaart waarom de uitvoervolgorde kan veranderen wanneer beide gegevensstromen naar dezelfde bestemming worden gestuurd.
Na printf("Saving...") kan de tekst in de buffer blijven staan omdat er geen nieuweregelteken is. Gebruik fflush(stdout) en controleer op EOF als de tekst vóór de volgende bewerking zichtbaar moet zijn. Het leegmaken van de buffer verstuurt uitvoer die nog klaarstaat; het leest of verwijdert geen invoer. Standaard C definieert fflush(stdin) niet als een bewerking om invoer te wissen. Om een regel te verwijderen, lees je tekens tot '\n' of EOF.
Als een programma twee regels naar stdout afdrukt en daartussen een waarschuwing naar stderr, toont een terminal doorgaans de programmavolgorde. Met > out.txt 2>&1 kan de waarschuwing het bestand vóór de gebufferde standaarduitvoer bereiken. Gebruik de weergavevolgorde van gemengde gegevensstromen niet als vastlegging van de uitvoeringsvolgorde.
Geef scripts een bruikbaar resultaat terug
Geef EXIT_SUCCESS of 0 terug als de volledige taak slaagt en EXIT_FAILURE als die mislukt. Beide macro’s zijn gedeclareerd in <stdlib.h>. Een shellopdracht zoals convert in.csv out.txt && echo done voert de tweede opdracht alleen na succes uit. Een eerlijke afsluitstatus is even belangrijk als een nuttige diagnostische melding.
Belangrijkste punten
- Gebruik
stdin,stdoutenstderrzonder ze te openen. - Stuur programmaresultaten naar
stdouten diagnostische meldingen naarstderr. - Omleiding verandert verbindingen; een pipe voert standaarduitvoer naar standaardinvoer.
- Maak de uitvoerbuffer leeg wanneer een invoerverzoek direct zichtbaar moet zijn.
- Geef een afsluitstatus voor een fout terug wanneer de gevraagde taak mislukt.
2Lees en schrijf tekstbestanden
Tekstverwerking kent drie nuttige niveaus: afzonderlijke tekens, opgemaakte waarden en hele regels. De keuze verandert wat het programma kan valideren en hoe het herstelt van ongeldige invoer. Een tekenlus moet bytes onderscheiden van het bestandseinde; een opgemaakte leesbewerking moet het aantal geslaagde toewijzingen controleren; een regelinlezer moet rekening houden met capaciteit en onvolledige records. Uitgewerkte voorbeelden laten zien hoe je de werkelijk gelezen gegevens behoudt en de latere analyse en melding onder controle houdt.
2.1 Lees en schrijf teken voor teken
Tekeninvoer en -uitvoer is nuttig wanneer elk teken bepaalt wat er daarna gebeurt: scheidingstekens tellen, hoofdletters en kleine letters omzetten of een eenvoudig tekstformaat herkennen. fgetc leest één teken en fputc schrijft er één. Beide zijn gedeclareerd in <stdio.h>.
Bewaar de retourwaarde in een int
fgetc(fp) geeft het teken terug als een naar int geconverteerde unsigned char, of de negatieve constante EOF wanneer geen teken beschikbaar is omdat de invoer is geëindigd of een leesbewerking is mislukt. Op de gebruikelijke systemen met bytes van acht bits lopen geldige bytewaarden van 0 tot 255. Een int houdt die waarden gescheiden van EOF.
int c;
while ((c = fgetc(in)) != EOF) {
if (fputc(c, out) == EOF) {
/* Report a write failure and clean up. */
break;
}
}
/* Check ferror(in) before declaring the input complete. */
Een gewone char kan dit onderscheid verliezen. Bij een char met teken kan byte 0xFF -1 worden en op het bestandseinde lijken. Bij een char zonder teken wordt de negatieve EOF-waarde naar een byte geconverteerd en kan die bij vergelijking nooit gelijk zijn aan EOF.
Behoud beide haakjes rond de toewijzing. In c = fgetc(in) != EOF gebeurt de vergelijking eerst, zodat c 1 of 0 ontvangt in plaats van het teken. getc leest op dezelfde manier en kan een macro zijn; fputc geeft het geschreven teken of EOF terug.
Tel geslaagde leesbewerkingen, geen pogingen
De lusvoorwaarde moet de leesbewerking uitvoeren. feof beschrijft een eerdere poging die het einde heeft bereikt; de functie kan de volgende poging niet voorspellen. Een lus die !feof(in) controleert en daarna onvoorwaardelijk telt, voegt aan het einde een extra doorgang toe.
Voor de tekst "hi\nyo\n" leveren zes geslaagde leesbewerkingen zes tekens en twee nieuweregeltekens op. Alleen nieuweregeltekens tellen werkt voor regels die eindigen op een nieuweregelteken. De tekst "red\ngreen\nblue" heeft twee nieuweregeltekens maar drie regels, omdat het laatste "blue" nog steeds een regel is.
Om een laatste regel zonder nieuweregelteken mee te tellen, houd je ook het laatste teken bij:
size_t lines = 0;
int c, last = '\n';
while ((c = fgetc(in)) != EOF) {
if (c == '\n') ++lines;
last = c;
}
if (last != '\n') ++lines;
Door last te laten beginnen met '\n' telt een leeg bestand als nul regels. Een leesfout vereist nog steeds een apart foutpad; een gedeeltelijke telling is geen volledig resultaat.
Verander tekens of kijk vooruit
toupper uit <ctype.h> kan letters veranderen voordat ze worden geschreven. Een geslaagd resultaat van fgetc ligt al binnen het geldige invoerbereik voor toupper, dus fputc(toupper(c), out) is veilig binnen de lus. Willekeurige char-waarden met teken moeten naar unsigned char worden geconverteerd voordat je ze aan tekenclassificatiefuncties doorgeeft.
ungetc(c, in) plaatst een teken terug, zodat de volgende leesbewerking het opnieuw teruggeeft. Terugplaatsen van één teken is gegarandeerd; controleer op EOF als het terugplaatsen mislukt. Dit helpt een parser om het volgende teken te bekijken zonder het te verliezen. Een positioneringsbewerking maakt het terugplaatsen ongedaan.
De telling beschrijft wat de gekozen gegevensstroommodus oplevert. In Windows telt tekstmodus een CR LF-paar als één '\n'; binaire modus maakt beide bytes zichtbaar. Een tekentelling via een tekststroom hoeft daarom niet gelijk te zijn aan het opgeslagen aantal bytes.
Belangrijkste punten
- Bewaar resultaten van
fgetcinint, zodat bytewaarden te onderscheiden blijven vanEOF. - Controleer elk leesresultaat voordat je het verwerkt of meetelt.
- Tel een niet-leeg laatste stuk zonder nieuweregelteken als een regel.
- Terugplaatsen van één teken met
ungetcis gegarandeerd; een positiewijziging maakt dit ongedaan.
2.2 Schrijf en lees opgemaakte tekst met fprintf en fscanf
Opgemaakte invoer en uitvoer converteert tussen C-waarden en leesbare tekst. fprintf schrijft in een opmaak die je kiest; fscanf leest waarden die bij een formaat passen. Hun conversiespecificaties lijken op elkaar, maar invoer vereist pointers naar de juiste doeltypen.
Schrijf leesbare waarden
Gebruik %d voor een int, %ld voor een long, %zu voor een size_t, %s voor een string en %c voor een teken. Voor uitvoer drukt %f een double af; een float-argument wordt gepromoveerd tot double. %% drukt een procentteken af.
int result = fprintf(fp, "%-8s%6.2f\n", "Oslo", 3.14159);
if (result < 0) {
/* Handle a write failure. */
}
%-8s lijnt "Oslo" links uit in acht kolommen; %6.2f lijnt "3.14" rechts uit in zes. De resulterende regel is "Oslo 3.14\n" en de retourwaarde is 15 tekens. Veldbreedte is een minimum, geen beperking van de uitvoerlengte. Precisie verandert ook de opgeslagen informatie: %.2f schrijft 3.14159 als "3.14", zodat die extra cijfers niet uit het bestand kunnen worden teruggehaald.
Een eenvoudige CSV-schrijver kan "Lima,2024,19.5" uitvoeren met een vaste volgorde en komma’s tussen de velden. Tekst met komma’s, aanhalingstekens of nieuweregeltekens vereist dat CSV-velden tussen aanhalingstekens worden geplaatst. Zet zo’n veld tussen dubbele aanhalingstekens en verdubbel elk aanhalingsteken erin. Een schrijver met alleen %s is alleen geschikt als tekstvelden die tekens niet bevatten.
Maak tekst op in een begrensde array
sprintf heeft geen argument voor de grootte van de bestemming. Voor het opmaken van "007.jpg" zijn acht bytes nodig, inclusief het afsluitende nulteken. Gebruik bij voorkeur snprintf:
char name[32];
int n = snprintf(name, sizeof name, "%03d.jpg", 7);
if (n < 0 || (size_t)n >= sizeof name) {
/* Formatting failed or the full result did not fit. */
}
De niet-negatieve retourwaarde is de lengte die de volledige tekst nodig zou hebben, exclusief het afsluitende nulteken. Een waarde gelijk aan de buffergrootte betekent al dat de tekst is afgekapt. Controleer of het resultaat negatief is voordat je het naar size_t converteert.
Lees het verwachte aantal waarden
char name[20];
int age;
int matched = fscanf(fp, "%19s %d", name, &age);
Deze aanroep slaagt alleen volledig als die 2 teruggeeft. De breedte 19 laat een byte over voor het afsluitende nulteken. De arraynaam levert het adres; het gehele getal heeft &age nodig. Voor invoer heeft %f een float * nodig en %lf een double *. Een afwijkend type leidt tot ongedefinieerd gedrag, niet tot een onschuldige conversie.
De meeste conversies slaan witruimte aan het begin over, inclusief nieuweregeltekens. fscanf dwingt dus niet één record per regel af en %s kan "New York" niet als één waarde lezen. Een tekenset zoals %19[^,] leest tot aan een komma; voor records is invoer van hele regels vaak duidelijker.
Bij "10 abc 20" slaat %d eerst 10 op en geeft 1 terug. De volgende aanroep geeft 0 terug omdat "abc" niet past; die tekst blijft ongelezen. Testen op != EOF herhaalt de fout eindeloos. Test op == 1 en handel ongeldige gegevens vervolgens afzonderlijk af van het einde van de invoer of een fout in de gegevensstroom. Een conversieresultaat is een telling van toegewezen waarden, geen waarheidswaarde voor het hele bestand.
Belangrijkste punten
- Laat elke conversiespecificatie overeenkomen met het argumenttype.
- Begrens
%sbij invoer tot de arraygrootte min één. - Vergelijk het resultaat van
fscanfmet het exacte aantal verwachte toewijzingen. snprintfgeeft de vereiste volledige lengte terug; een retourwaarde die minstens gelijk is aan de capaciteit betekent afkapping.- CSV-tekstvelden met komma’s, aanhalingstekens of nieuweregeltekens moeten tussen aanhalingstekens staan.
- Opgemaakte uitvoer van drijvendekommagetallen kan precisie verliezen.
2.3 Lees regels veilig met fgets
Door een volledige regel te lezen voordat je die analyseert, houd je tekstrecords van elkaar gescheiden. Als een record ongeldig is, kun je het regelnummer melden en doorgaan met het volgende record. fgets begrenst hoeveel de functie schrijft, maar je moet nog steeds rekening houden met behouden nieuweregeltekens en regels die groter zijn dan de array.
Lees in een buffer met bekende capaciteit
char line[256];
while (fgets(line, sizeof line, fp) != NULL) {
/* Process this successful read. */
}
if (ferror(fp)) {
/* Report an interrupted read. */
}
fgets leest maximaal één teken minder dan de capaciteit, stopt na een nieuweregelteken als de functie er een tegenkomt en voegt een afsluitend nulteken toe. Bij succes geeft de functie de buffer terug. Een NULL-resultaat betekent dat er aan het einde van het bestand geen tekens beschikbaar waren of dat er een fout is opgetreden; verwerk de buffer niet na een leesfout.
sizeof line levert in dit geldigheidsgebied de capaciteit van de array. In een hulpfunctie met parameter char *line is het de grootte van de pointer. Geef de werkelijke capaciteit afzonderlijk door. De oude functie gets kan invoer niet begrenzen en is in C11 verwijderd.
Herken stukken van lange regels
Met char buf[8] komt "temperature\n" binnen als "tempera" en daarna "ture\n". De eerste aanroep gebruikt zeven bytes voor tekens en één voor het afsluitende nulteken. Een geslaagde leesbewerking zonder nieuweregelteken kan een stuk van een lange regel zijn of het laatste stuk aan het bestandseinde zonder afsluitend nieuweregelteken.
Kies een aanpak: voeg stukken samen tot een groter record, verwerk ze als een aaneengesloten reeks of wijs het record af en verwerp alle resterende stukken ervan. Controleer bij een groottelimiet het volgende teken voordat je een precies gevulde buffer afwijst. Een volgend nieuweregelteken voltooit het record; EOF zonder fout in de gegevensstroom voltooit een laatste regel zonder nieuweregelteken; een ander teken betekent dat er meer inhoud is. Plaats een teken alleen terug als latere code het opnieuw moet lezen.
Verwijder alleen een regeleinde dat je echt hebt gevonden
Nadat je hebt bepaald of het record compleet is, verwijder je regeleinden:
line[strcspn(line, "\r\n")] = '\0';
strcspn uit <string.h> vindt het eerste van de opgegeven tekens, of het bestaande afsluitende nulteken als geen van beide voorkomt. Dit verwerkt zowel achtergebleven CR LF als LF. Onvoorwaardelijk line[strlen(line) - 1] verwijderen kan echte gegevens verwijderen van een laatste regel zonder nieuweregelteken en is ongeldig voor een lege string.
fputs(line, out) schrijft de string zonder een nieuweregelteken toe te voegen; puts(line) schrijft naar stdout en voegt er wel een toe. Een tekstkopie kan elk met fgets geslaagd gelezen stuk doorgeven aan fputs en op fouten controleren, ook als een lange regel uit meerdere stukken bestaat. Gebruik binaire blokinvoer en -uitvoer als de oorspronkelijke bytes exact behouden moeten blijven.
Analyseer en valideer afzonderlijk
sscanf(line, "%d %9s", &qty, item) geeft 2 terug voor "7 apples" en 0 voor "seven apples". Bij een mislukte analyse blijft geen ongeldig token in de invoerstroom achter, omdat de regel al is gelezen. Numerieke conversies met sscanf vereisen nog steeds invoer die binnen het bereik kan worden weergegeven; gebruik strtol voor strikte bereikcontrole.
Zet voor strtol errno op nul, geef een eindpointer door en controleer op ontbrekende cijfers, ERANGE en onverwachte tekst aan het einde. Bij "42abc" is het resultaat 42 slechts een numeriek voorvoegsel. Sta voor een veld met een geheel getal de gekozen witruimte aan het einde toe en vereis daarna het einde van de string. Controleer het bereik van het doeltype voordat je een geldige long versmalt naar int.
Belangrijkste punten
fgetsbewaart een nieuweregelteken als het past en reserveert bij succes altijd ruimte voor een afsluitend nulteken.- Een volle buffer zonder nieuweregelteken vereist een controle op volledigheid.
- Geef capaciteiten expliciet door aan hulpfuncties die met pointers werken.
- Gebruik
strcspnom alleen een aanwezig regeleinde te verwijderen. - Lees eerst en analyseer en valideer daarna elk record.
- Controleer bij
strtolop cijfers, bereikfouten en gegevens aan het einde.
3Sla gegevens op in binaire bestanden en lees ze in
Binaire bestanden dragen objectbytes over zonder ze naar leesbare tekst te converteren. Elementgroottes en aantallen bepalen hoe volledige arrays, records en onvolledige laatste blokken worden verwerkt. Een bestand dat tussen computers wordt gedeeld, heeft ook een expliciete representatie nodig: veldbreedtes, bytevolgorde en indeling. Het lezen van een signatuur is slechts het begin van de controle van die representatie; lengtes en aantallen moeten worden gevalideerd voordat het programma de resterende gegevens vertrouwt of er ruimte voor reserveert.
3.1 Lees en schrijf binaire gegevens met fread en fwrite
Binaire invoer en uitvoer draagt de bytes van objecten over zonder ze naar leesbare getallen of woorden te converteren. fread en fwrite ontvangen een adres, een elementgrootte, een aantal elementen en een gegevensstroom. Hun retourwaarden tellen volledige elementen, dus de gekozen elementgrootte bepaalt wat je uit het resultaat kunt afleiden.
Schrijf en lees arrays
int a[5] = {1, 2, 3, 4, 5};
if (fwrite(a, sizeof a[0], 5, out) != 5) {
/* Handle a write failure. */
}
Op een systeem waar int vier bytes groot is, schrijft dit 20 bytes en geeft het 5 terug. Door de gegevens in een andere array van vijf elementen met dezelfde representatie te lezen, kun je de waarden terugkrijgen. Gebruik "wb" en "rb", zodat tekstvertalingen de bytes niet kunnen veranderen.
Geef voor één object het adres door: fwrite(&rec, sizeof rec, 1, out). Gebruik voor objecten die via een pointer p worden bereikt fwrite(p, sizeof *p, count, out). sizeof p meet de pointer en &p wijst naar de pointervariabele zelf. Geen van beide beschrijft de bedoelde gegevensoverdracht. Als de elementgrootte of het aantal nul is, geven beide functies nul terug zonder gegevens over te dragen.
Kopieer precies de gelezen bytes
unsigned char buf[512];
size_t n;
while ((n = fread(buf, 1, sizeof buf, in)) > 0) {
if (fwrite(buf, 1, n, out) != n) {
/* Handle failure and stop copying. */
break;
}
}
Een elementgrootte van 1 maakt n tot een aantal bytes. Een invoer van 1.300 bytes levert 512, 512 en 276 bytes op, en daarna nul. De laatste schrijfbewerking moet 276 gebruiken, niet de capaciteit 512. Als je de hele buffer schrijft, voeg je 236 bytes toe die van eerdere leesbewerkingen zijn overgebleven.
Met fread(buf, 512, 1, in) geeft dezelfde invoer achtereenvolgens 1, 1 en 0 terug. De laatste 276 bytes vormen slechts een onvolledig element; ze worden niet meegeteld en de waarde van dat onvolledige element is onbepaald. Kies elementen van één byte wanneer een onvolledig laatste blok nuttig is. Kies elementen ter grootte van een record wanneer onvolledige records moeten worden afgewezen.
Een fread die minder elementen oplevert dan gevraagd, kan wijzen op het bestandseinde of een fout. Controleer na het kopiëren ferror en controleer fclose op de uitvoer: een schrijfbewerking die in een buffer is geaccepteerd, kan later falen. Een onvolledige bestandskop of een onvolledig record mag nooit als een volledig object worden behandeld.
Bekijk signaturen en opgeslagen objecten
Gebruik unsigned char voor ruwe bytes; op systemen met bytes van acht bits bevat dit type waarden van 0–255 zonder negatieve waarden. JPEG-gegevens beginnen met 0xFF 0xD8. Foto’s van camera’s en telefoons gaan vaak verder met een APPn-markering, wat het herstelpatroon FF D8 FF E0 tot en met FF D8 FF EF oplevert. Dat patroon is een nuttige test om mogelijke bestanden te vinden; valideer het formaat voordat je het vertrouwt. Sommige JPEG-bestanden gebruiken een ander volgend segment.
Een struct met char name[20] en een int score neemt meestal 24 bytes in. Een ruwe schrijfbewerking bevat alle opgeslagen bytes, inclusief opvulling. Een pointerveld slaat een adres op, niet de string waarnaar het verwijst. Gebruik een ingebedde array of schrijf de lengte en tekens van de string afzonderlijk.
Leesbare tekst kan meer ruimte innemen: "1000000\n" gebruikt acht bytes op Linux en negen in Windows-tekstmodus. Een expliciet gecodeerd 32-bits geheel getal gebruikt vier bytes van acht bits. Ruwe opslag van objecten is compact, maar bindt het bestand aan de representatie van het systeem dat het heeft gemaakt.
Belangrijkste punten
freadenfwritegeven aantallen volledige elementen terug, niet altijd aantallen bytes.- Gebruik elementgrootte 1 wanneer een onvolledig laatste blok van belang is.
- Schrijf het gelezen aantal, niet de volledige buffercapaciteit.
- Een ruw opgeslagen pointerveld bevat een adres, niet de gegevens waarnaar het wijst.
- Controleer overdrachten met minder elementen dan gevraagd en het sluiten van de uitvoer.
- Gebruik signaturen om mogelijke bestanden te herkennen en valideer daarna het bestand.
3.2 Ontwerp binaire bestandsformaten die op verschillende computers werken
Een binaire gegevensstroom behoudt bytes, maar twee computers kunnen dezelfde bytes verschillend interpreteren. Een overdraagbaar formaat legt vast wat elke byte betekent, in plaats van de indeling over te nemen die de compiler aan een object geeft.
Leg groottes, volgorde en indeling vast
Drie keuzes maken het rechtstreeks wegschrijven van een object afhankelijk van de computer:
| Keuze | Wat kan verschillen |
|---|---|
| Typegrootte | int kan twee of vier bytes zijn; long verschilt vaak tussen 64-bits Windows en Linux |
| Bytevolgorde | De minst of meest significante byte kan eerst komen |
| Structindeling | Opvulling en offsets van velden kunnen veranderen |
Little-endian slaat de minst significante byte eerst op. Big-endian slaat de meest significante byte eerst op. De waarde 258, hexadecimaal 0x00000102, is 02 01 00 00 in little-endian-volgorde en 00 00 01 02 in big-endian-volgorde.
Een gangbare structindeling voor een char gevolgd door een int neemt acht bytes in: één byte voor het teken, drie opvulbytes en vier voor het gehele getal. De waarden van de opvulbytes zijn niet gespecificeerd. Typen met exacte bitbreedte uit <stdint.h>, zoals uint32_t, leggen de bitbreedte vast waar ze beschikbaar zijn; ze leggen de bytevolgorde niet vast en verwijderen geen opvulling. Veelgebruikte voorbeelden van bestandsformaten gaan uit van bytes van acht bits en de beschikbaarheid van die typen.
Decodeer velden expliciet
Lees vier bytes volledig voordat je ze interpreteert. Voor een little-endian-veld:
uint32_t v = (uint32_t)b[0]
| (uint32_t)b[1] << 8
| (uint32_t)b[2] << 16
| (uint32_t)b[3] << 24;
Hier is b een array van bytes zonder teken. Elke expliciete typeconversie vindt vóór de bitverschuiving plaats, zodat de berekening de vereiste breedte zonder teken heeft. Om te coderen haal je de laagste acht bits eruit, daarna de volgende acht, enzovoort. Controleer elke uitvoerbewerking. Als je v rechtstreeks met fwrite schrijft, breng je de bytevolgorde van de computer weer in het formaat binnen.
Definieer een volledig klein formaat
Een scoreformaat kan "SCR1" gebruiken als signatuur van vier bytes, gevolgd door een little-endian-aantal van vier bytes en records die bestaan uit 20 naambytes plus vier little-endian-scorebytes. De signatuur geeft ook de versie aan. De bestandskop is acht bytes; elk record is precies 24 bytes.
Vijf records nemen 128 bytes in. Record met index 3 begint bij byte 80: sla acht bytes van de bestandskop en drie volledige records over. Leg bij een echt formaat de codering van het naamveld en de regels voor afsluiting of opvulling vast; een naamveld van 20 bytes is niet automatisch een C-string.
Controleer de volledige lengte en signatuur van een bestandskop voordat je de velden gebruikt. Controleer daarna aantallen en lengtes aan de hand van redelijke limieten en de beschikbare gegevens voordat je geheugen reserveert. Een aannemelijk aantal is niet genoeg als latere records ontbreken.
Lees de werkelijke structuur van het formaat
Sommige PCM WAV-bestanden hebben een eenvoudige bestandskop van 44 bytes, met de bemonsteringsfrequentie op bytes 24–27. De bytes 44 AC 00 00 worden gedecodeerd als 44.100 monsters per seconde. Andere WAV-bestanden bevatten extra blokken, dus volgt een algemeen leesprogramma de bloknamen en -groottes om het fmt -blok te vinden. Als je voor elk bestand uitgaat van één vaste bestandskopindeling, kun je een niet-gerelateerd veld als frequentie lezen.
Tekst zoals CSV vermijdt problemen met de grootte van gehele getallen, bytevolgorde en opvulling in structs en is gemakkelijk te bekijken. Wel zijn nog steeds een vastgelegde veldindeling, regels voor aanhalingstekens en gecontroleerde analyse nodig. Kies tekst wanneer leesbaarheid en uitwisseling belangrijker zijn dan een compacte binaire representatie.
Belangrijkste punten
- Een overdraagbaar binair formaat legt veldbreedtes, bytevolgorde en offsets vast.
- Geheelgetaltypen met exacte bitbreedte maken het rechtstreeks wegschrijven van een struct op zichzelf niet overdraagbaar.
- Converteer bytes vóór het verschuiven naar de bedoelde breedte zonder teken.
- Valideer de hele bestandskop en begrens aantallen voordat je ze vertrouwt.
- Volg de blokstructuur wanneer een formaat extra blokken toestaat.
- Een tekstveld met vaste breedte heeft expliciete regels voor codering en lengte nodig.
4Ga naar een willekeurige positie in een bestand
Bestandsposities maken directe toegang mogelijk wanneer de locaties van records bekend zijn. Om die locaties te berekenen moet je letten op bestandskoppen, recordbreedtes en indexen die bij nul beginnen. Voor tekstposities gelden strengere regels, omdat vertaling hun betekenis kan veranderen. Zodra een gegevensstroom zowel lezen als schrijven ondersteunt, bepaalt positionering ook of je geldig van richting wisselt. Eén record bijwerken combineert daarom een locatieberekening met een gecontroleerde reeks stappen die de omringende gegevens behoudt.
4.1 Bepaal en wijzig de bestandspositie met ftell, fseek en rewind
Bij sequentieel lezen worden gegevens op volgorde verwerkt. Met directe toegang kan een programma naar een bekende locatie springen, zoals één record in een groot bestand. Positioneren werkt alleen als de onderliggende gegevensstroom dit ondersteunt, en de betekenis van een positie verschilt tussen binaire gegevensstromen en tekststromen.
Positie opvragen en verplaatsen
ftell(fp) geeft een long-positie terug of -1L bij een fout. Voor een binair bestand telt een positie bytes vanaf het begin. fseek(fp, offset, whence) verplaatst de positie ten opzichte van een gekozen referentiepunt en geeft 0 terug bij succes en een waarde ongelijk aan nul bij een fout.
| Referentie | Betekenis van de offset |
|---|---|
SEEK_SET | Vanaf het begin |
SEEK_CUR | Vanaf de huidige positie |
SEEK_END | Vanaf het einde |
Zo vraagt fseek(fp, -4L, SEEK_END) om de laatste vier bytes van een binair bestand op een systeem dat verplaatsingen ten opzichte van het einde ondersteunt. Controleer het resultaat voordat je leest. Een verzoek op een pipe of terminal, of een verzoek om naar vóór het begin van een gewoon bestand te gaan, kan mislukken.
Bereken de locatie van een record
Voor binaire records van vaste grootte zonder bestandskop geeft de index vanaf nul, vermenigvuldigd met de recordbreedte, de beginoffset. Bij records van 24 bytes begint index 3 bij byte 72. Na het lezen van dat volledige record is de positie 96.
long offset = 3L * (long)sizeof(struct record);
if (fseek(fp, offset, SEEK_SET) != 0) {
/* Handle the failed move before reading. */
}
Dit voorbeeld gaat ervan uit dat het bestand met dezelfde structindeling is geschreven. Een vastgelegd overdraagbaar formaat gebruikt in plaats daarvan de gedefinieerde recordbreedte. Tel bytes van een aanwezige bestandskop mee. Het derde record geteld vanaf 1 heeft index 2, niet index 3. Valideer bij grote indexen de vermenigvuldiging en controleer of het resultaat in long past voordat je de positie wijzigt.
Op gangbare systemen kun je een binair bestand meten door naar het einde te gaan en ftell te gebruiken: een bestand van 120 bytes met records van 24 bytes bevat vijf records. Controleer beide aanroepen en herstel daarna de positie. Standaard C vereist geen zinvolle SEEK_END voor elke binaire gegevensstroom, dus de methode hangt af van de ondersteuning door die gegevensstroom.
Sla tekstposities op zonder ermee te rekenen
Tekstmodus kan regeleinden vertalen. Een teruggegeven tekstpositie is informatie om naar een plaats terug te keren, geen getal om bij op te tellen of van af te trekken. Een positiewijziging in tekstmodus met een waarde ongelijk aan nul gebruikt een eerder door ftell voor hetzelfde bestand teruggegeven waarde, met SEEK_SET. Een offset van nul is toegestaan met elk referentiepunt.
rewind(fp) keert terug naar het begin en wist zowel de bestandseinde-indicator als de foutindicator. De functie geeft geen resultaat terug, dus gebruik een gecontroleerde fseek(fp, 0L, SEEK_SET) wanneer moet worden bevestigd dat de verplaatsing is geslaagd. Een geslaagde fseek wist de bestandseinde-indicator en maakt het terugplaatsen met ungetc ongedaan, maar laat een bestaande foutindicator staan.
Bewaar een positiewaarde
fgetpos(fp, &pos) slaat een positie op in fpos_t; fsetpos(fp, &pos) herstelt die. Controleer bij beide of een waarde ongelijk aan nul een fout aangeeft. Behandel fpos_t als een ondoorzichtige waarde waarmee je niet rekent. Die kan meer positie-informatie bevatten dan een eenvoudige telling van bytes.
fseek en ftell gebruiken long, dat op Windows meestal 32 bits is en de weer te geven posities beperkt tot ongeveer 2 GiB. Een positiewaarde voorkomt dat de interface aan long wordt gebonden, maar de ondersteunde bestandsgrootte hangt nog steeds af van de implementatie en configuratie.
Belangrijkste punten
- Controleer
fseekop een waarde ongelijk aan nul enftellop-1L. - Een recordoffset omvat de bestandskop en alle eerdere records.
- Gebruik consequent indexen die bij nul beginnen.
- Herstel tekstposities met opgeslagen positiewaarden, zonder met offsets te rekenen.
rewindwist beide indicatoren; een geslaagdefseekwist de bestandseinde-indicator.- Positionering vereist een gegevensstroom die positiewijziging ondersteunt en offsets die kunnen worden weergegeven.
4.2 Werk records van vaste grootte op hun bestaande bestandspositie bij
Een gegevensstroom voor lezen en schrijven leest en schrijft hetzelfde bestand. Gebruik "r+b" om een bestaand binair bestand te behouden terwijl je de records wijzigt. "w+b" wist het oude bestand direct en toevoegmodi sturen elke schrijfbewerking naar het einde.
Wissel geldig van richting
Voor een gegevensstroom voor lezen en schrijven gelden twee wisselregels:
| Wisseling | Vereiste handeling tussen bewerkingen |
|---|---|
| Van schrijven naar lezen | Maak de uitvoerbuffer leeg of voer een positioneringsbewerking uit |
| Van lezen naar schrijven | Voer een positioneringsbewerking uit, tenzij de leesbewerking het bestandseinde heeft bereikt |
Positioneringsfuncties zijn onder meer fseek, fsetpos en rewind. Controleer functies die een resultaat melden voordat je verdergaat. Een richtingswisseling zonder de vereiste handeling leidt tot ongedefinieerd gedrag; je kunt er niet op rekenen dat alleen het volgende record wordt beïnvloed.
Werk één volledig record bij
Stel dat een bestand zonder bestandskop vijf records van 24 bytes bevat met dezelfde representatie als struct record. Om index 3 te wijzigen:
- Open met
"r+b"en controleer de pointer. - Ga naar byte 72 en controleer of dit is geslaagd.
- Lees precies één volledig record.
- Verander het veld
scorein 99. - Ga terug naar het begin van het record.
- Schrijf precies één record en controleer het resultaat van het sluiten.
De belangrijkste bewerkingen daartussen zijn:
if (fread(&rec, sizeof rec, 1, fp) != 1) {
/* Report the incomplete input, then clean up. */
goto cleanup;
}
rec.score = 99;
if (fseek(fp, -(long)sizeof rec, SEEK_CUR) != 0) {
/* Report the failed seek, then clean up. */
goto cleanup;
}
if (fwrite(&rec, sizeof rec, 1, fp) != 1) {
/* Report the failed write, then clean up. */
goto cleanup;
}
De omvattende functie bevat een label cleanup dat de gegevensstroom sluit en bij deze takken een foutstatus teruggeeft. Na het lezen is de positie 96. Door 24 bytes terug te gaan, selecteer je het oorspronkelijke record en maak je de wisseling van lezen naar schrijven mogelijk. Na een geslaagde vervanging blijft het bestand 120 bytes groot en bevat het vijf records. Een opgeslagen beginpositie herstellen is een andere geldige manier om de wijziging uit te voeren.
Bepaal wanneer je opnieuw moet schrijven
Als je een record van 24 bytes toevoegt, groeit dat bestand tot 144 bytes. Door naar het begin van een "ab"- of "a+b"-gegevensstroom te gaan, kun je de volgende schrijfbewerking niet laten overschrijven; die gaat nog steeds naar het huidige einde.
Een vervanging op dezelfde positie moet passen in de ruimte die ze vervangt. "Bo,7" veranderen in "Bo,9" past. Als je "Bo,12" over "Bo,7\n" schrijft, wordt het nieuweregelteken overschreven en wordt de volgende tekst aan het record vastgemaakt. Om records groter of kleiner te maken, schrijf je een gecorrigeerd nieuw bestand. Standaard C heeft geen functie voor gegevensstromen die een reeks bytes uit het midden van een bestand verwijdert. Voor verwijdering kun je in plaats daarvan alle overblijvende records kopiëren of een record van vaste grootte als verwijderd markeren.
Zorg dat vervanging herstelbaar blijft
Controleer remove en rename; beide geven 0 terug bij succes. Of rename een bestaande bestemming vervangt, wordt door de implementatie bepaald. Linux en macOS ondersteunen vervanging; de C-bibliotheek van Microsoft faalt wanneer de bestemming bestaat.
Een overdraagbaar herstelplan schrijft en sluit het nieuwe bestand, hernoemt het origineel naar een afzonderlijke back-upnaam en hernoemt het nieuwe bestand naar de oorspronkelijke naam. Als het plaatsen mislukt, houd je de back-up beschikbaar voor herstel en meld je waar die staat. Verwijder de back-up pas nadat het plaatsen is geslaagd. Deze reeks maakt herstel mogelijk; het is geen overdraagbare atomaire transactie en geen garantie tegen stroomuitval.
Belangrijkste punten
- Gebruik
"r+b"om een bestaand bestand bij te werken zonder het leeg te maken. - Volg de wisselregel voor lezen en schrijven, ook als de bedoelde positie vanzelfsprekend lijkt.
- Ga na het lezen van een record terug voordat je het vervangt.
- Schrijfbewerkingen in toevoegmodus gaan altijd naar het einde.
- Schrijf gegevens opnieuw wanneer hun lengte verandert.
- Controleer elke vervangingsstap en bewaar een herstelbaar origineel.
5Detecteer en meld bestandsfouten en herstel de werking na een fout
Een bestandsbewerking kan mislukken nadat het openen is geslaagd en gebufferde uitvoer kan pas bij het sluiten falen. Betrouwbare programma’s lezen retourwaarden, onderscheiden ongeldige gegevens van toegangsproblemen, bewaren nuttige foutinformatie en ruimen op elk pad op. Deze taken hangen samen: de diagnostische melding moet de oorspronkelijke fout verklaren, terwijl de uiteindelijke status ook fouten moet weergeven die tijdens het opruimen zijn gevonden. Een volledig kopieerprogramma laat zien hoe deze keuzes in de gewone besturingsstroom van C passen.
5.1 Controleer elk resultaat en onderscheid het bestandseinde van fouten
Een bestand kan met succes worden geopend en toch falen tijdens lezen, schrijven, positioneren of sluiten. Een robuust programma controleert het resultaat van de bewerking die het daadwerkelijk heeft geprobeerd. Het onderscheidt een voltooide leesbewerking van onderbroken invoer en onjuist opgemaakte gegevens van een falende gegevensstroom.
Leer elk foutsignaal kennen
| Bewerking | Signaal om af te handelen |
|---|---|
fopen, fgets | NULL |
fgetc, fputc, fflush, fclose | EOF |
fprintf | Een negatief resultaat |
fputs | EOF |
fread, fwrite | Minder volledige elementen dan gevraagd |
fscanf | Minder toewijzingen dan verwacht, of EOF |
fseek, fgetpos, fsetpos | Een resultaat ongelijk aan nul |
ftell | -1L |
rewind, clearerr en perror geven geen waarde terug. Als moet worden gecontroleerd of de verplaatsing naar het begin is geslaagd, gebruik dan fseek in plaats van op een resultaat van rewind te vertrouwen.
Controleer de gegevensstroom nadat het lezen stopt
De gegevensstroom houdt afzonderlijke indicatoren voor het bestandseinde en fouten bij. feof(fp) test de eerste; ferror(fp) test de tweede. Elk geeft een waarde ongelijk aan nul terug als de bijbehorende indicator is gezet.
while (fgets(line, sizeof line, fp) != NULL) {
/* Process successful input. */
}
if (ferror(fp)) {
fprintf(stderr, "Input could not be completed\n");
/* Return failure after cleanup. */
}
De retourwaarde van fgets alleen maakt geen onderscheid tussen de twee redenen om te stoppen. Op dezelfde manier geeft fgetc EOF terug en geeft fread een kleiner aantal terug, zowel bij het bestandseinde als bij een leesfout. Test feof niet vóór het lezen om te voorspellen of er nog gegevens zijn.
Indicatoren blijven gezet. clearerr wist ze beide, net als rewind. Een geslaagde fseek of fsetpos wist de bestandseinde-indicator, maar niet een bestaande foutindicator. Het wissen van een indicator geeft aan dat je die hebt verwerkt; het herstelt niet de verbinding met een verwijderd station en repareert geen beschadigd bestand. Probeer het pas opnieuw als de oorzaak is aangepakt.
Onderscheid ongeldige gegevens van mislukte toegang
Als fscanf(fp, "%d", &value) 0 teruggeeft bij "abc", is de conversie mislukt vóór een toewijzing. Daarvoor hoeft geen bestandseinde of leesfout te zijn opgetreden. Meld een ongeldig veld, bij voorkeur met een regelnummer. Een melding over de schijf zou het verkeerde probleem beschrijven.
Als een vereiste bestandskop als één element wordt gelezen en fread 0 teruggeeft, is de bestandskop onvolledig. Bekijk dat gedeeltelijk gevulde object niet. Als ferror is gezet, meld je een invoerfout; als het bestand is geëindigd, meld je afgekorte gegevens. Een leeg bestand en een onvolledige bestandskop kunnen hetzelfde aantal elementen opleveren, dus het verwachte formaat bepaalt dat de bestandskop aanwezig moet zijn.
Vang vertraagde uitvoerfouten af
Een schijf kan vol raken nadat het programma de uitvoer heeft geopend. Gebufferde schrijfbewerkingen kunnen geslaagd lijken totdat fflush of fclose de wachtende bytes daadwerkelijk overdraagt. Controleer de afsluitende sluitbewerking en laat een fout daarbij een verder geslaagde taak doen mislukken. Sluit dezelfde gegevensstroom daarna niet opnieuw.
Op een terminal geeft Ctrl+D aan het begin van een regel op Linux of macOS, of Ctrl+Z gevolgd door Enter op Windows, doorgaans het einde van de invoer aan. clearerr(stdin) wist de resulterende indicator als het programma nog een poging nodig heeft. De handeling op de terminal en een leesfout blijven verschillende oorzaken.
Belangrijkste punten
- Controleer elke functie aan de hand van de eigen gedocumenteerde retourwaarde.
- Lees eerst en gebruik daarna indicatoren van de gegevensstroom om de reden voor het stoppen te bepalen.
- Een afwijking van het formaat betekent ongeldige gegevens, niet noodzakelijk een fout in de gegevensstroom.
- Wijs onvolledige vereiste bestandskoppen en records af.
- Een vertraagde fout bij het sluiten van de uitvoer moet de taak doen mislukken.
5.2 Verklaar fouten met errno, perror en strerror
Een retourwaarde vertelt je dat een bewerking is mislukt. Een foutcode kan helpen verklaren waarom. Gebruik errno alleen binnen de context van het gedocumenteerde gedrag van de bewerking en bewaar de waarde voordat andere bibliotheekaanroepen die kunnen veranderen.
Laat de bewerking de fout vaststellen
errno is een wijzigbare int-expressie uit <errno.h>, met in C11 een afzonderlijke opslagduur per thread. De waarde begint op nul. Bibliotheekaanroepen zetten de waarde niet terug op nul en een geslaagde aanroep kan een waarde ongelijk aan nul laten staan of instellen. Als je errno na een geslaagde fopen test, vertelt dat je dus niet dat het openen is mislukt.
Op POSIX-systemen stelt een mislukte fopen een foutcode in. De C-bibliotheek van Microsoft biedt ook bestandsfoutcodes. Standaard C vereist niet dat fopen errno instelt. Voor overdraagbare foutmeldingen kun je de waarde vóór het openen op nul zetten en een algemene melding gebruiken als geen code is opgegeven:
errno = 0;
FILE *fp = fopen(name, "r");
if (fp == NULL) {
int saved = errno;
if (saved != 0)
fprintf(stderr, "Cannot open %s: %s\n",
name, strerror(saved));
else
fprintf(stderr, "Cannot open %s\n", name);
return EXIT_FAILURE;
}
Dit codefragment heeft ook <stdio.h>, <stdlib.h> en <string.h> nodig. Door de code direct op te slaan voorkom je dat latere aanroepen voor meldingen of opruimen de oorspronkelijke reden vervangen. Een opgeslagen code is ook nuttig als de uiteindelijke melding veel later wordt afgedrukt.
Kies een functie voor meldingen
perror(label) schrijft het label, een dubbele punt en spatie, de huidige foutmelding en een nieuweregelteken naar stderr. Een typische melding voor een ontbrekend bestand is "missing.txt: No such file or directory"; de exacte formulering hangt af van het systeem.
strerror(code) geeft de melding van de bibliotheek voor een opgegeven code terug en past dus bij een eigen diagnostische melding die het programma, de bewerking en het bestand noemt. Wijzig de teruggegeven string niet. Gebruik na het opruimen strerror(saved) in plaats van de nieuwe waarde van errno op te vragen.
C definieert EDOM, ERANGE en EILSEQ. Veelvoorkomende bestandscodes zoals ENOENT, EACCES, EEXIST en ENOSPC worden geleverd door POSIX en breed beschikbare C-bibliotheken. Stem je code af op de interface en het platform waarvoor je schrijft. Ga er niet van uit dat numerieke codewaarden of exacte Engelse meldingsteksten overal gelijk zijn.
Handel een dubbelzinnige retourwaarde af
Bij sommige functies kan het resultaat alleen geen onderscheid maken tussen succes en een bereikfout. strtol kan LONG_MAX teruggeven als geldig getal of als resultaat van overloop. Zet errno vóór de conversie op nul en controleer daarna de gedocumenteerde bereikcode:
errno = 0;
char *end;
long value = strtol(text, &end, 10);
if (end == text) {
/* No digits were converted. */
} else if (errno == ERANGE) {
/* The value is outside the long range. */
} else {
/* Validate trailing characters and the target range. */
}
Een nuttige diagnostische melding geeft de gebruiker informatie om te handelen. Noem het bestand en de beoogde handeling; behoud de reden van het systeem als die beschikbaar is. Stuur de melding naar stderr, zodat stdout beschikbaar blijft voor geldige resultaatgegevens.
Belangrijkste punten
- Gebruik het functieresultaat om een fout vast te stellen voordat je
errnointerpreteert. - Sla
errnodirect op vóór aanroepen voor meldingen of opruimen. perrorgebruikt de huidigeerrno;strerroraccepteert een opgeslagen code.- Zet
errnovóórstrtolop nul en controleerERANGE. - Vermeld het bestand en de beoogde handeling in een diagnostische melding.
5.3 Ruim hulpbronnen op en meld het resultaat
Foutafhandeling is pas compleet wanneer hulpbronnen zijn vrijgegeven en het programma het werkelijke resultaat meldt. Bepaal het eigenaarschap wanneer je een gegevensstroom opent en zorg dat elk later pad die afspraak volgt.
Sluit elke geopende gegevensstroom eenmaal
Als het openen van de invoer mislukt, is er geen gegevensstroom om te sluiten. Als het openen van de uitvoer mislukt nadat de invoer is geopend, sluit je de invoer. Als het kopiëren mislukt nadat beide zijn geopend, sluit je beide, controleer je het sluiten van de uitvoer en behoud je een foutstatus. Een pointer tweemaal sluiten is geen extra bescherming; je gebruikt dan een afgesloten gegevensstroom.
Code die vroegtijdig terugkeert, sluit hulpbronnen meestal in omgekeerde volgorde. Een gedeeld opruimlabel is nuttig wanneer meerdere foutpaden hetzelfde werk vereisen. Initialiseer pointers op NULL, houd standaard een foutstatus aan en stel pas succes in nadat de verwerkingsfase is voltooid.
Het volgende volledige programma kopieert verschillende invoer- en uitvoerbestanden in binaire modus. Het gebruikt algemene meldingen omdat standaard C niet vereist dat elke stdio-fout errno instelt.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *in = NULL, *out = NULL;
int status = EXIT_FAILURE;
int output_acquired = 0;
unsigned char buf[512];
size_t n;
if (argc != 3) {
fprintf(stderr, "Usage: %s input output\n", argv[0]);
return EXIT_FAILURE;
}
in = fopen(argv[1], "rb");
if (in == NULL) {
fprintf(stderr, "Cannot open input: %s\n", argv[1]);
goto cleanup;
}
out = fopen(argv[2], "wb");
if (out == NULL) {
fprintf(stderr, "Cannot open output: %s\n", argv[2]);
goto cleanup;
}
output_acquired = 1;
while ((n = fread(buf, 1, sizeof buf, in)) > 0) {
if (fwrite(buf, 1, n, out) != n) {
fprintf(stderr, "Write failed: %s\n", argv[2]);
goto cleanup;
}
}
if (ferror(in)) {
fprintf(stderr, "Read failed: %s\n", argv[1]);
goto cleanup;
}
status = EXIT_SUCCESS;
cleanup:
if (out != NULL && fclose(out) == EOF) {
fprintf(stderr, "Close failed: %s\n", argv[2]);
status = EXIT_FAILURE;
}
if (in != NULL && fclose(in) == EOF) {
fprintf(stderr, "Close failed: %s\n", argv[1]);
status = EXIT_FAILURE;
}
if (output_acquired && status != EXIT_SUCCESS) {
if (remove(argv[2]) != 0)
fprintf(stderr, "Cannot remove incomplete output: %s\n",
argv[2]);
}
return status;
}
De invoerlus verwerkt alleen bytes die daadwerkelijk zijn gelezen. Een schrijfbewerking die minder bytes schrijft dan gevraagd, springt naar het opruimen; een invoerfout wordt na de lus gecontroleerd. Het sluiten van de uitvoer kan succes alsnog in een fout veranderen. Verwijdering wordt alleen geprobeerd als deze uitvoering de uitvoer met succes heeft geopend, zodat een openingsfout niet leidt tot verwijdering van een niet-gerelateerd bestand. De eis van verschillende bestanden is van belang: "wb" zou de invoer wissen als beide namen naar hetzelfde bestand verwijzen.
Behoud belangrijke bestaande gegevens
Door de uitvoer rechtstreeks te openen kun je het leegmaken niet ongedaan maken. Om waardevolle gegevens te vervangen, schrijf je eerst onder een afzonderlijke tijdelijke naam. Controleer elke schrijfbewerking en de afsluitende sluitbewerking voordat je bestanden hernoemt. Bewaar het origineel onder een back-upnaam totdat het nieuwe bestand is geplaatst; controleer elke hernoeming en verwijdering. Als het plaatsen mislukt, meld dan de locatie van het herstelbare bestand in plaats van het te verwijderen.
Een hulpfunctie moet sluiten wat ze heeft geopend en een resultaat voor succes of een fout teruggeven aan de aanroepende functie. Die kan beslissen of ze het opnieuw probeert of afsluit. Door na elke onvolledige stap EXIT_FAILURE terug te geven, kan een script stoppen vóór een afhankelijke handeling, zoals het verwijderen van het origineel na een back-up.
Belangrijkste punten
- Leg het eigenaarschap van gegevensstromen vast en houd een fout als standaardstatus aan.
- Sluit elke succesvol geopende gegevensstroom op elk pad precies eenmaal.
- Laat een fout bij het sluiten de uiteindelijke status veranderen.
- Verwijder onvolledige uitvoer alleen als deze uitvoering die heeft aangemaakt of geopend.
- Schrijf vervangingen afzonderlijk en bewaar het origineel totdat het plaatsen is geslaagd.
- Hulpfuncties melden fouten aan hun aanroepende functies, zodat die een herstelactie kunnen kiezen.
Bestandsinvoer en -uitvoer in C: voorbeelden
1Open een gegevensstroom zonder bestaande gegevens te verliezen
12 stappen
- 1IntroductieBepaal wat behouden moet blijven
Een programma staat op het punt resultaten op te slaan. Bepaal voordat het iets opent of oude gegevens behouden moeten blijven en of het bestand leesbare regels of exacte bytes bevat. Die keuzes bepalen de modus en een verkeerde keuze kan oude gegevens wissen.
Je volgt één schrijfbewerking vanaf de tekst van het programma door de gegevensstroom (het kanaal van de bibliotheek naar een bestand of terminal). Daarna kies je waar normale resultaten en diagnostische meldingen naartoe moeten (de standaardstromen). Openen is al een bewerking op het bestand, dus deze beslissing komt vóór de eerste schrijfbewerking.
- 2UitlegBereik gegevens via een gegevensstroom
Een gegevensstroom is het kanaal van de standaardbibliotheek naar een bestand, terminal of pipe. Bewaar de verwijzing ernaar als een
FILE *uit<stdio.h>en geef die door aan functies zoalsfprintf.Het bibliotheekobject onthoudt de buffer, positie en statusindicatoren. Je wijzigt de velden niet. Een bestandspositie schuift op wanneer gegevens worden verwerkt; een terminal of pipe kun je voor positiewijzigingen niet als een gewoon bestand behandelen.
- 3VoorbeeldVolg nieuweregeltekens tot aan de opgeslagen bytes
Het schrijven van
"a\nb\nc\n"levert drie letters en drie nieuweregeltekens aan. Op Linux of macOS bevat het tekstbestand zes bytes. Op Windows wordt elk nieuweregelteken CR LF, wat negen opgeslagen bytes oplevert.Als je dat Windows-bestand op Windows in tekstmodus leest, krijg je zes tekens terug. Binaire invoer maakt alle negen bytes zichtbaar. De tekst van het programma en de opgeslagen bytereeks zijn twee verschillende weergaven van dezelfde regels.
- 4VerbandKies een modus voor een afbeelding
Een bytereeks in een gecomprimeerde afbeelding kan waarden bevatten die de tekstmodus vertaalt. Door
btoe te voegen kies je de binaire modus en blijven die waarden behouden.Dit verklaart waarom een programma dat afbeeldingen met
"r"leest op Linux kan werken maar op Windows kan falen. Het moet"rb"gebruiken omdat het een vastgelegde reeks bytes verwerkt, geen regels die de bibliotheek moet vertalen. - 5UitdagingControleer een tekentelling
Op Windows wordt
"m\nn\n"in tekstmodus opgeslagen. Hoeveel bytes bevat het resulterende bestand en waarom?---reveal---
Het bevat zes bytes: twee letters plus twee CR LF-paren. De vier door het programma aangeleverde tekens nemen meer ruimte in omdat elk nieuweregelteken twee opgeslagen bytes wordt. In binaire modus zouden diezelfde vier aangeleverde bytes zonder die vertaling worden geschreven.
- 6UitlegKies vóór toegang wat behouden blijft
Voor een bestaand bestand staat
"r"lezen toe, wist"w"de inhoud en staat het schrijven toe, en behoudt"a"de gegevens en voegt eraan toe. Een+staat zowel invoer als uitvoer toe.Gebruik
"r+"om een bestaand bestand voor wijzigingen te behouden."w+"wist het nog steeds."wx"uit C11 faalt als het bestand bestaat, wat nuttig is wanneer een bestaande bestemming moet worden beschermd. - 7TipOpen en sluit één gegevensstroom
Controleer na
FILE *fp = fopen(name, "r");offp == NULLvoordat je een bewerking op de gegevensstroom uitvoert. Bij een fout is er geen gegevensstroom om te verwerken of te sluiten.Bij succes is het programma eigenaar van een gegevensstroom die één
fclosenodig heeft. Controleer ook die sluitbewerking. Na de aanroep is de gegevensstroom afgesloten, ook als het sluiten mislukt; als je opnieuw probeert te sluiten, gebruik je een gegevensstroom die niet meer geldig is. - 8UitdagingBescherm het oude logbestand
Een bestaand logbestand moet zijn vermeldingen behouden en er moet een nieuwe regel worden toegevoegd. Een cursist kiest
"w"omdat de taak schrijft. Welke modus moet worden gebruikt en wanneer zou"w"de oude vermeldingen vernietigen?---reveal---
Gebruik
"a". Modus"w"vernietigt de bestaande inhoud zodra het openen slaagt, zelfs vóór een schrijfbewerking. De keuze moet het logbestand tijdens het openen beschermen, niet alleen tijdens de latere verwerkingscode. - 9UitlegScheid resultaten van verklaringen
De al geopende gegevensstromen zijn
stdinvoor invoer,stdoutvoor resultaten enstderrvoor diagnostische meldingen. Een functie dieFILE *outaccepteert, kan dezelfde opmaak naar een bestand of naarstdoutschrijven.Een shell kan standaarduitvoer omleiden naar een bestand of via een pipe naar een ander programma sturen. Houd diagnostische tekst op
stderr, zodat er geen foutmeldingen tussen de gegevensregels van een machinaal leesbaar resultaat terechtkomen. - 10KernpuntLaat gebufferde tekst verschijnen
Uitvoer kan in een geheugenbuffer wachten. Standaarduitvoer naar een terminal is doorgaans regelgebufferd; omgeleide uitvoer is doorgaans volledig gebufferd. Standaardfoutuitvoer is niet volledig gebufferd.
Een invoerverzoek zonder nieuweregelteken kan vóór het begin van de invoer een gecontroleerde
fflush(stdout)nodig hebben. Het leegmaken van de buffer verwijdert geen invoer. Om een ongewenste invoerregel over te slaan, lees je tekens tot en met het nieuweregelteken of tot het bestandseinde. - 11UitdagingVolg de uitvoer van een filter
Een filter leest standaardinvoer, schrijft resultaten naar standaarduitvoer en schrijft fouten naar standaardfoutuitvoer. Het wordt uitgevoerd als
filter < raw.txt > clean.txt. Waar moet een melding over een afgewezen record verschijnen en welke status moet het filter teruggeven als het de taak niet kan voltooien?---reveal---
De diagnostische melding gaat naar de overgeërfde foutbestemming, meestal de terminal. Geef
EXIT_FAILUREterug. Het resultatenbestand blijft gescheiden van de melding en de shell kan de fout detecteren. - 12SamenvattingGa bewust om met de levenscyclus van een gegevensstroom
Kies vóór het openen wat behouden moet blijven en of tekstmodus of binaire modus nodig is. Controleer of het openen een gegevensstroom opleverde, verwerk gegevens alleen via geldige pointers en sluit elke gegevensstroom waarvan je eigenaar bent eenmaal.
Via standaardstromen kan de shell de verbindingen leveren. Scheid resultaten van diagnostische meldingen en geef een status terug die de voltooide taak beschrijft. Door deze keuzes kan hetzelfde kleine programma interactief, met bestanden of binnen een pijplijn werken.
2Lees een tekstrecord en valideer de inhoud
15 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieLees op het juiste niveau
- 2UitlegHoud een teken te onderscheiden van EOF
- 3VoorbeeldTel alleen een geslaagde leesbewerking
- 4UitdagingTel de laatste regel zonder afsluitend nieuweregelteken mee
- 5VoorbeeldSchrijf een gecontroleerde opmaak
- 6TipReserveer het afsluitende nulteken van de string
- 7UitlegGebruik het aantal geslaagde toewijzingen
- 8UitdagingKies een begrensde opgemaakte naam
- 9UitlegLees een regel in geheugen van bekende grootte
- 10VoorbeeldVolg een regel over twee leesbewerkingen
- 11TipVerwijder het regeleinde zonder gegevens te verwijderen
- 12UitlegAnalyseer de al gelezen regel
- 13UitdagingAccepteer een regel die precies past
- 14UitdagingWijs een voorvoegsel met een ongeldig achtervoegsel af
- 15SamenvattingHoud lezen en interpreteren gescheiden
3Draag bytes over en leg hun betekenis vast
12 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieScheid bytes van waarden
- 2UitlegKies wat de retourwaarde telt
- 3VoorbeeldVolg het laatste korte blok
- 4KernpuntOnderscheid een record van een byteblok
- 5UitdagingGebruik het aantal dat is gelezen
- 6UitlegLaat de compiler het formaat niet ontwerpen
- 7VoorbeeldDecodeer een little-endian-waarde
- 8VoorbeeldBepaal de locatie van een record uit de indeling
- 9UitdagingControleer de opgegeven hoeveelheid gegevens
- 10VerbandVolg de structuur die het formaat toestaat
- 11UitdagingVereis een volledig veld vóór het decoderen
- 12SamenvattingLaat overdracht en formaat overeenkomen
4Zoek een record en vervang het veilig
13 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieSpring naar de gegevens die je nodig hebt
- 2UitlegKies een referentiepunt
- 3VoorbeeldZoek record met index 3 vanaf nul
- 4TipHerstel tekstposities als opgeslagen waarden
- 5UitdagingZoek het derde record
- 6UitlegBehoud het bestaande bestand
- 7VoorbeeldHerstel het begin van het record
- 8KernpuntVoltooi de vervanging
- 9UitdagingKies zowel de modus als de verplaatsing
- 10VerbandSchrijf opnieuw wanneer een record groeit
- 11TipZorg dat het plaatsen herstelbaar blijft
- 12UitdagingInterpreteer een voltooide toevoeging
- 13SamenvattingPositionering hoort bij een geldige wijziging
5Geef een fout door bij het melden en opruimen
12 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieSucces omvat de afsluitende sluitbewerking
- 2UitlegLees elk functieresultaat
- 3UitlegVerklaar waarom de invoer stopte
- 4UitdagingWijs een afgekorte bestandskop af
- 5UitlegLeg de oorspronkelijke reden vast
- 6VoorbeeldDruk een melding af met de opgeslagen code
- 7UitdagingKies de juiste bron voor de diagnostische melding
- 8UitlegHoud bij welke gegevensstromen zijn geopend
- 9VoorbeeldLaat het opruimen succes herzien
- 10UitdagingBepaal de volgende handeling van een script
- 11KernpuntBehoud het origineel na een mislukte hernoeming
- 12SamenvattingGeef het resultaat van de volledige taak terug
Bestandsinvoer en -uitvoer in C: oefenvragen
Set 1: Kies gegevensstromen en verwerk volledige invoer10 Oefenvragen
Vraag 1 van 10
Toon antwoordVerberg antwoord
"rb" opent een bestaand bestand voor binaire invoer en behoudt de opgeslagen bytes. Gewoon "r" kiest tekstmodus, die regeleinden kan vertalen en Ctrl+Z op Windows als einde van de invoer kan herkennen. De schrijfmodi bieden niet de gevraagde leestoegang.Vraag 2 van 10
Toon antwoordVerberg antwoord
"w" zou de eerdere logvermeldingen wissen zodra het openen slaagt. Modus "r" behoudt gegevens maar staat niet toe de nieuwe vermelding te schrijven; "w+" staat invoer en uitvoer toe maar wist het bestand nog steeds.Vraag 3 van 10
fgets slaagt op de laatste regel, die "maple" zonder nieuweregelteken bevat. Wat blijft er over na line[strlen(line) - 1] = '\0';?Toon antwoordVerberg antwoord
"maple". fgets verzint geen nieuweregelteken als het bestand er geen heeft. Zoek CR of LF met strcspn voordat je het regeleinde verwijdert, zodat een laatste regel zonder regeleinde alle werkelijke gegevens behoudt.Vraag 4 van 10
0xFF en bevat het meer gegevens. Waarom kan char c; while ((c = fgetc(fp)) != EOF) direct stoppen?Toon antwoordVerberg antwoord
fgetc geeft de byte terug als een niet-negatieve int, maar opslag in een char van acht bits met teken kan 255 in -1 veranderen. Die waarde kan bij vergelijking gelijk zijn aan de EOF van de implementatie, waardoor de lus stopt vóór de latere bytes. De haakjes zijn hier correct; het versmallende bestemmingstype veroorzaakt het probleem.Vraag 5 van 10
char tag[14]; int count;, welke aanroep leest veilig één woord en een geheel getal in deze objecten?Toon antwoordVerberg antwoord
&count, een pointer naar de bestemming. Breedte 14 lijkt op de arraygrootte, maar staat één invoerteken meer toe dan de array samen met het afsluitende nulteken veilig kan bevatten.Vraag 6 van 10
Toon antwoordVerberg antwoord
stderr houdt diagnostische meldingen gescheiden van de rapportgegevens die naar stdout gaan. Als je de melding op stdout afdrukt, komt die in het omgeleide rapport terecht en kan het verwachte formaat worden verstoord. Standaardinvoer levert gegevens om te lezen; het is geen bestemming voor een diagnostische melding.Vraag 7 van 10
fwrite(a, 4, 7, fp) schrijft elk gevraagd element. Wat geeft de functie terug?Toon antwoordVerberg antwoord
fwrite geeft het aantal volledig geschreven elementen terug, dus de functie geeft 7 terug. De overdracht omvat 28 bytes omdat elk element grootte 4 heeft, maar dat totale aantal bytes is niet de retourwaarde. Aantallen bytes en aantallen elementen vallen alleen samen als de elementgrootte 1 is.Vraag 8 van 10
07 02 00 00. Welke waarde stelt het voor?Toon antwoordVerberg antwoord
07 02 als een big-endian-paar leest, krijg je 1.794. De eigen bytevolgorde van de computer verandert de door het bestandsformaat vastgelegde interpretatie niet.Vraag 9 van 10
Toon antwoordVerberg antwoord
SEEK_CUR doorgeeft, wordt die absolute locatie bij de huidige positie opgeteld in plaats van geselecteerd.Vraag 10 van 10
fread(&h, 16, 1, fp). De functie geeft 0 terug, ferror(fp) is nul en feof(fp) is ongelijk aan nul. Hoe moet het leesprogramma verdergaan?Toon antwoordVerberg antwoord
ferror maakt de velden niet veilig om te interpreteren.Set 2: Werk met tekstgrenzen en binaire indelingen10 Oefenvragen
Sla deze les op om elk antwoord te zien en elke set te oefenen.
Vraag 1 van 10
/work/run en opent "data/items.txt". Naar welk pad wordt dit vertaald?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 2 van 10
producer | consumer > result.txt de standaardfoutuitvoer van de producent standaard naartoe?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 3 van 10
fread(buf, 1, 600, in). Welke groottes moeten de geslaagde schrijfbewerkingen gebruiken?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 4 van 10
char name[8]; ontvangt snprintf(name, sizeof name, "part-%d", 2468), dat 9 teruggeeft. Wat moet het programma concluderen?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
"cedars\nmaplesx". De validator roept herhaaldelijk fgets(buf, 8, fp) aan. Welke recordgrenzen moet die herkennen?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 6 van 10
"north\nsouth\neast". Hoeveel regels en nieuweregeltekens bevat die?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 7 van 10
uint32_t, maar verschillen in bytevolgorde en opvulling in structs. Een programma schrijft struct { char kind; uint32_t count; } rechtstreeks weg met één fwrite. Welk herontwerp maakt de representatie onafhankelijk van beide verschillen?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 8 van 10
long p = ftell(fp); en gecontroleerd. Welke geldige aanroep keert er na verder lezen naar terug?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
"r+b"-gegevensstroom gaat naar byte 40 en leest één volledig record zonder EOF. Een veld wordt gewijzigd. Welke bewerking vóór het schrijven staat de wisseling toe en selecteert hetzelfde record?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 10 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Set 3: Werk records bij en behoud betrouwbare resultaten10 Oefenvragen
Sla deze les op om elk antwoord te zien en elke set te oefenen.
Vraag 1 van 10
"x\ny\n" in tekstmodus geschreven. Hoeveel opgeslagen bytes levert dat op?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 2 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 3 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 4 van 10
"64km" wordt aan strtol doorgegeven nadat errno op nul is gezet. De retourwaarde is 64, errno blijft 0 en end wijst naar k. Het veld moet een geheel getal met optionele witruimte aan het einde bevatten. Wat is de juiste beslissing?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
fread(b, 16, 1, fp) met een array van 16 bytes. Wat geeft de tweede aanroep terug en hoe mag het element worden gebruikt?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 6 van 10
"18 pear 26". Na één geslaagde fscanf(fp, "%d", &n) geeft dezelfde aanroep tweemaal 0 terug. Waarom heeft die 26 niet bereikt?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 7 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 8 van 10
"r+b" gaat naar byte 38 en leest één volledig record zonder EOF. Het wijzigt een veld. Welke volgende bewerking selecteert hetzelfde record en maakt de vervangende schrijfbewerking mogelijk?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 10 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Set 4: Achterhaal fouten in een volledige bestandsverwerking10 Oefenvragen
Sla deze les op om elk antwoord te zien en elke set te oefenen.
Vraag 1 van 10
"ready" mislukt hoewel de zichtbare letters overeenkomen. Welk achtergebleven teken verklaart dit?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 2 van 10
totals.txt. Welke omleidingsoperator is nodig?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 3 van 10
"a+b" gaat een programma naar byte 12, leest één record, verandert de score in 25, gaat terug naar byte 12 en schrijft het record. Alle aanroepen slagen. Welke scores bevatten het tweede en het laatste record daarna?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 4 van 10
char buf[8] geven leesbewerkingen eerst "8642kg\n" en daarna "1234567" terug; de volgende gecontroleerde leesbewerking bevestigt een normaal bestandseinde. strtol vindt 8642 met kg erachter in het eerste record en converteert alle cijfers in het tweede, zonder bereikfouten. Welke records zijn geldig?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 6 van 10
"a\nb". Een programma gebruikt int c; size_t n = 0, lines = 0; while (c = fgetc(in) != EOF) { ++n; if (c == '\n') ++lines; }. Welke totalen ontstaan en welke correctie laat het programma regels correct tellen?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 7 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 8 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 10 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Kom je niet uit je eigen opgave? Huiswerkhulp
Kernbegrippen
A
Aantal geslaagde toewijzingen
fscanf alleen compleet als het teruggegeven aantal geslaagde toewijzingen 2 is.Afsluitend nulteken
fgets mag lezen.Afsluitstatus
&& mag niet worden uitgevoerd na een onvolledige bestandskopie.B
Bestandseinde-indicator
Bestandskop
Bestandspositie
Bestandssignatuur
Bestandsvervanging met herstelmogelijkheid
Big-endian-bytevolgorde
00 00 01 02 in een 32-bits big-endian-veld.Binaire modus
"rb", zodat tekstvertaling de gecomprimeerde bytes niet kan veranderen.Bloksgewijs kopiëren
Buffering
Bytevolgorde
C
clearerr
Conversiespecificatie
%lf een double *, terwijl %f een bestemming van het type float * vereist.CSV-velden tussen aanhalingstekens plaatsen
D
Directe toegang
E
Eigenaarschap van hulpbronnen
Eindpointer
strtol wijst naar het eerste teken na het numerieke voorvoegsel dat de conversie heeft geaccepteerd."86cm" bereikt de pointer de c, zodat een veld voor een geheel getal het achtervoegsel kan afwijzen.EOF
int, zodat een echte byte niet voor deze markering wordt aangezien.ERANGE
errno vóór strtol op nul, zodat een oude bereikfout niet voor een nieuwe wordt aangezien.errno
Exclusieve bestandsaanmaak
"wx" beschermt een bestaand rapport tegen wissen bij het openen.F
fclose
EOF meldt.fclose(fp) terugkeert, kan die gegevensstroom niet opnieuw worden gebruikt, ook niet bij een fout.fgetc
int geconverteerde tekenwaarde zonder teken, of EOF teruggeeft.fgetc met succes een teken heeft teruggegeven.fgets
FILE-pointer
FILE * die bibliotheekfuncties toegang geeft tot een gegevensstroom en de opgeslagen toestand ervan.fp door aan fgetc nadat je hebt gecontroleerd of het openen een geldige gegevensstroompointer heeft teruggegeven.fopen
NULL teruggeeft.fgetc of fclose.Foutindicator
fgets NULL teruggeeft, kan ferror mislukte toegang onderscheiden van normale voltooiing.fpos_t
fgetpos en fsetpos gebruiken om alle vereiste bestandspositie-informatie op te slaan en te herstellen.fputc
EOF teruggeeft.fputc nadat het het gelezen teken heeft veranderd.fread
fseek
ftell
long en geeft bij een fout -1L terug; een tekstpositie moet als een opgeslagen locatie worden behandeld.fwrite
G
Gegevensstroom
Gegevensstroom voor lezen en schrijven
+-modus is geopend, zodat zowel invoer als uitvoer volgens wisselregels is toegestaan."r+b", blijft het behouden voor lezen en schrijven.Geheelgetaltype met exacte bitbreedte
uint32_t-veld heeft nog steeds expliciete bytecodering nodig om een uitwisselingsbestand overdraagbaar te maken.H
Huidige werkmap
"data/input.txt" verwijst.L
Little-endian-bytevolgorde
02 01 00 00 stellen 258 voor in een 32-bits little-endian-veld.N
Niet-passende invoer
"oak" kan 0 teruggeven terwijl de bestandseinde-indicator en foutindicator van de gegevensstroom niet zijn gezet.O
Omleiding
2> kan de diagnostische meldingen van een programma in een afzonderlijk bestand opslaan.Onvolledig element
fread.Openingsmodus
fopen vertelt welke toegang is toegestaan en of bestaande inhoud wordt behouden of gewist."a" kiest, blijven eerdere logvermeldingen behouden en komt elke nieuwe schrijfbewerking aan het einde.Opruimpad
Opvulling in een struct
char gevolgd door een int kan acht bytes innemen, ook als hun groottes samen vijf zijn.P
perror
errno-waarde naar standaardfoutuitvoer, gevolgd door een nieuweregelteken.Pipe
R
Regelanalyse
rename
S
snprintf
Standaardfoutuitvoer
stderr voor diagnostische meldingen, onafhankelijk van de normale resultatenstroom van een programma.stderr worden beschreven terwijl geldige gegevens naar stdout gaan.Standaardinvoer
stdin waaruit een programma normaal gesproken zijn belangrijkste invoer ontvangt.< values.txt leest een filter bestandsgegevens via stdin in plaats van via het toetsenbord.Standaarduitvoer
stdout voor de normale resultaten en andere primaire uitvoer van een programma.stdout van een conversieprogramma naar de standaardinvoer van een ander programma.strcspn
"\r\n" als verzameling afwijstekens vind je een regeleinde zonder gewone gegevens aan het einde te verwijderen.strerror
errno kan hebben vervangen.T
Tekstmodus
Terugplaatsen van een teken
ungetc gebruiken om het voor de volgende fase te laten staan.Toevoegmodus
"a+b"-gegevensstroom naar byte nul gaat, kun je invoer selecteren maar de volgende schrijfbewerking niet omleiden.V
Veldbreedte
%23s ruimte over voor een afsluitend nulteken in een tekenarray van 24 bytes.W
Wijziging op dezelfde bestandspositie
Over deze les
Door StudeliFout melden
Maak je eigen Studiemateriaal
Maak van je aantekeningen, slides en documenten Leermodules, Oefenvragen, Flashcards en 6 andere tools.