Bestandsinvoer en -uitvoer in C

Leer hoe C-programma’s bestanden lezen en schrijven met de standaardbibliotheek voor invoer en uitvoer. Open bestanden als gegevensstromen in de juiste modus en verbind programma’s met standaardinvoer, standaarduitvoer en standaardfoutuitvoer. Verwerk tekst teken voor teken, met opgemaakte lees- en schrijfbewerkingen en regel voor regel met 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

  1. 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 uit
      Beschrijf 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 fclose
      Open 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 pipes
      Gebruik de drie gegevensstromen waarmee elk C-programma begint, stuur resultaten en foutmeldingen naar de juiste plaats en laat gebruikers die vanaf de opdrachtregel omleiden.
  2. 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 teken
      Gebruik 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 fscanf
      Schrijf 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 fgets
      Lees tekst regel voor regel met fgets, handel nieuweregeltekens, lange regels en Windows-regeleinden af en analyseer elke regel na het lezen.
  3. 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 fwrite
      Gebruik 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 werken
      Leg 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.
  4. 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 rewind
      Vraag 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 bij
      Open 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.
  5. 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 fouten
      Weet 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 strerror
      Gebruik 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 resultaat
      Sluit elke gegevensstroom op elk uitvoeringspad, laat geen halfgeschreven uitvoer achter en geef een afsluitstatus terug die het resultaat eerlijk weergeeft.

Bestandsinvoer en -uitvoer in C: uitleg

5 hoofdstukken

1

Open 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.

BufferingsmodusWanneer uitvoer wordt overgedragen
Volledig gebufferdWanneer de buffer vol raakt, of expliciet wordt leeggemaakt of gesloten
RegelgebufferdOok wanneer een nieuweregelteken wordt geschreven
OngebufferdZo 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
ModusBestaand bestandOntbrekend bestandToegang
"r"Behoudt inhoudFaaltLezen vanaf het begin
"w"Wist inhoudMaakt aanSchrijven vanaf het begin
"a"Behoudt inhoudMaakt aanSchrijven aan het einde
"r+"Behoudt inhoudFaaltLezen en schrijven
"w+"Wist inhoudMaakt aanLezen en schrijven
"a+"Behoudt inhoudMaakt aanOveral 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 fopen op NULL voordat 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, stdout en stderr zonder ze te openen.
  • Stuur programmaresultaten naar stdout en diagnostische meldingen naar stderr.
  • 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.
2

Lees 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 fgetc in int, zodat bytewaarden te onderscheiden blijven van EOF.
  • 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 ungetc is 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 %s bij invoer tot de arraygrootte min één.
  • Vergelijk het resultaat van fscanf met het exacte aantal verwachte toewijzingen.
  • snprintf geeft 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

  • fgets bewaart 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 strcspn om alleen een aanwezig regeleinde te verwijderen.
  • Lees eerst en analyseer en valideer daarna elk record.
  • Controleer bij strtol op cijfers, bereikfouten en gegevens aan het einde.
3

Sla 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

  • fread en fwrite geven 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:

KeuzeWat kan verschillen
Typegrootteint kan twee of vier bytes zijn; long verschilt vaak tussen 64-bits Windows en Linux
BytevolgordeDe minst of meest significante byte kan eerst komen
StructindelingOpvulling 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.
4

Ga 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.

ReferentieBetekenis van de offset
SEEK_SETVanaf het begin
SEEK_CURVanaf de huidige positie
SEEK_ENDVanaf 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 fseek op een waarde ongelijk aan nul en ftell op -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.
  • rewind wist beide indicatoren; een geslaagde fseek wist 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:

WisselingVereiste handeling tussen bewerkingen
Van schrijven naar lezenMaak de uitvoerbuffer leeg of voer een positioneringsbewerking uit
Van lezen naar schrijvenVoer 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:

  1. Open met "r+b" en controleer de pointer.
  2. Ga naar byte 72 en controleer of dit is geslaagd.
  3. Lees precies één volledig record.
  4. Verander het veld score in 99.
  5. Ga terug naar het begin van het record.
  6. 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.
5

Detecteer 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
BewerkingSignaal om af te handelen
fopen, fgetsNULL
fgetc, fputc, fflush, fcloseEOF
fprintfEen negatief resultaat
fputsEOF
fread, fwriteMinder volledige elementen dan gevraagd
fscanfMinder toewijzingen dan verwacht, of EOF
fseek, fgetpos, fsetposEen 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 errno interpreteert.
  • Sla errno direct op vóór aanroepen voor meldingen of opruimen.
  • perror gebruikt de huidige errno; strerror accepteert een opgeslagen code.
  • Zet errno vóór strtol op nul en controleer ERANGE.
  • 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

5 Walkthroughs

1

Open een gegevensstroom zonder bestaande gegevens te verliezen

12 stappen
Kies een bestandsmodus, volg de tekstvertaling en verbind dezelfde verwerkingscode met standaardstromen.
  1. 1
    IntroductieBepaal 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.

  2. 2
    UitlegBereik 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 zoals fprintf.

    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.

  3. 3
    VoorbeeldVolg 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.

  4. 4
    VerbandKies een modus voor een afbeelding

    Een bytereeks in een gecomprimeerde afbeelding kan waarden bevatten die de tekstmodus vertaalt. Door b toe 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.

  5. 5
    UitdagingControleer 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.

  6. 6
    UitlegKies 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.

  7. 7
    TipOpen en sluit één gegevensstroom

    Controleer na FILE *fp = fopen(name, "r"); of fp == NULL voordat 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 fclose nodig 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.

  8. 8
    UitdagingBescherm 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.

  9. 9
    UitlegScheid resultaten van verklaringen

    De al geopende gegevensstromen zijn stdin voor invoer, stdout voor resultaten en stderr voor diagnostische meldingen. Een functie die FILE *out accepteert, kan dezelfde opmaak naar een bestand of naar stdout schrijven.

    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.

  10. 10
    KernpuntLaat 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.

  11. 11
    UitdagingVolg 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_FAILURE terug. Het resultatenbestand blijft gescheiden van de melding en de shell kan de fout detecteren.

  12. 12
    SamenvattingGa 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.

2

Lees een tekstrecord en valideer de inhoud

15 stappen
Ga van tekeninvoer naar opgemaakte waarden en volledige regels en handel grenzen af die veelvoorkomende invoerfouten veroorzaken.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieLees op het juiste niveau
  2. 2
    UitlegHoud een teken te onderscheiden van EOF
  3. 3
    VoorbeeldTel alleen een geslaagde leesbewerking
  4. 4
    UitdagingTel de laatste regel zonder afsluitend nieuweregelteken mee
  5. 5
    VoorbeeldSchrijf een gecontroleerde opmaak
  6. 6
    TipReserveer het afsluitende nulteken van de string
  7. 7
    UitlegGebruik het aantal geslaagde toewijzingen
  8. 8
    UitdagingKies een begrensde opgemaakte naam
  9. 9
    UitlegLees een regel in geheugen van bekende grootte
  10. 10
    VoorbeeldVolg een regel over twee leesbewerkingen
  11. 11
    TipVerwijder het regeleinde zonder gegevens te verwijderen
  12. 12
    UitlegAnalyseer de al gelezen regel
  13. 13
    UitdagingAccepteer een regel die precies past
  14. 14
    UitdagingWijs een voorvoegsel met een ongeldig achtervoegsel af
  15. 15
    SamenvattingHoud lezen en interpreteren gescheiden
3

Draag bytes over en leg hun betekenis vast

12 stappen
Kopieer een kort laatste blok correct en decodeer daarna records met een expliciete binaire indeling.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieScheid bytes van waarden
  2. 2
    UitlegKies wat de retourwaarde telt
  3. 3
    VoorbeeldVolg het laatste korte blok
  4. 4
    KernpuntOnderscheid een record van een byteblok
  5. 5
    UitdagingGebruik het aantal dat is gelezen
  6. 6
    UitlegLaat de compiler het formaat niet ontwerpen
  7. 7
    VoorbeeldDecodeer een little-endian-waarde
  8. 8
    VoorbeeldBepaal de locatie van een record uit de indeling
  9. 9
    UitdagingControleer de opgegeven hoeveelheid gegevens
  10. 10
    VerbandVolg de structuur die het formaat toestaat
  11. 11
    UitdagingVereis een volledig veld vóór het decoderen
  12. 12
    SamenvattingLaat overdracht en formaat overeenkomen
4

Zoek een record en vervang het veilig

13 stappen
Bereken een offset, behoud een opgeslagen positie en volg de regels voor gegevensstromen voor lezen en schrijven terwijl je één record vervangt.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieSpring naar de gegevens die je nodig hebt
  2. 2
    UitlegKies een referentiepunt
  3. 3
    VoorbeeldZoek record met index 3 vanaf nul
  4. 4
    TipHerstel tekstposities als opgeslagen waarden
  5. 5
    UitdagingZoek het derde record
  6. 6
    UitlegBehoud het bestaande bestand
  7. 7
    VoorbeeldHerstel het begin van het record
  8. 8
    KernpuntVoltooi de vervanging
  9. 9
    UitdagingKies zowel de modus als de verplaatsing
  10. 10
    VerbandSchrijf opnieuw wanneer een record groeit
  11. 11
    TipZorg dat het plaatsen herstelbaar blijft
  12. 12
    UitdagingInterpreteer een voltooide toevoeging
  13. 13
    SamenvattingPositionering hoort bij een geldige wijziging
5

Geef een fout door bij het melden en opruimen

12 stappen
Bepaal of de invoer compleet is, bewaar de oorspronkelijke fout en laat late uitvoerfouten het eindresultaat veranderen.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieSucces omvat de afsluitende sluitbewerking
  2. 2
    UitlegLees elk functieresultaat
  3. 3
    UitlegVerklaar waarom de invoer stopte
  4. 4
    UitdagingWijs een afgekorte bestandskop af
  5. 5
    UitlegLeg de oorspronkelijke reden vast
  6. 6
    VoorbeeldDruk een melding af met de opgeslagen code
  7. 7
    UitdagingKies de juiste bron voor de diagnostische melding
  8. 8
    UitlegHoud bij welke gegevensstromen zijn geopend
  9. 9
    VoorbeeldLaat het opruimen succes herzien
  10. 10
    UitdagingBepaal de volgende handeling van een script
  11. 11
    KernpuntBehoud het origineel na een mislukte hernoeming
  12. 12
    SamenvattingGeef het resultaat van de volledige taak terug

Bestandsinvoer en -uitvoer in C: oefenvragen

40 Oefenvragen in 4 sets

Set 1: Kies gegevensstromen en verwerk volledige invoer10 Oefenvragen

Vraag 1 van 10

Welke modus leest een gecomprimeerd bestand zonder tekstvertaling?
  1. "r"
  2. "rb"
  3. "wb"
  4. "ab"
Toon antwoordVerberg antwoord
Antwoord: B
"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

Een programma moet een nieuwe vermelding aan een bestaand logbestand toevoegen en eerdere vermeldingen behouden. Welke openingsmodus past?
  1. "w"
  2. "r"
  3. "w+"
  4. "a"
Toon antwoordVerberg antwoord
Antwoord: D
Toevoegmodus behoudt de bestaande inhoud en plaatst elke schrijfbewerking aan het einde. Modus "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';?
  1. "mapl"
  2. "maple"
  3. "maple\n"
  4. "map"
Toon antwoordVerberg antwoord
Antwoord: A
De code vervangt altijd het laatste opgeslagen teken en verwijdert dus de e uit "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

Op een systeem met een char van acht bits met teken begint een binair bestand met byte 0xFF en bevat het meer gegevens. Waarom kan char c; while ((c = fgetc(fp)) != EOF) direct stoppen?
  1. De binaire modus zet 0xFF om in bestandseinde.
  2. De toewijzing vergelijkt voordat de byte wordt opgeslagen.
  3. De byte kan -1 worden bij conversie naar char.
  4. De eerste geslaagde leesbewerking zet de EOF-indicator.
Toon antwoordVerberg antwoord
Antwoord: C
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

Gegeven char tag[14]; int count;, welke aanroep leest veilig één woord en een geheel getal in deze objecten?
  1. fscanf(fp, "%14s %d", tag, &count)
  2. fscanf(fp, "%13s %d", tag, &count)
  3. fscanf(fp, "%13s %d", tag, count)
  4. fscanf(fp, "%14s %d", tag, count)
Toon antwoordVerberg antwoord
Antwoord: B
De woordconversie mag maximaal 13 tekens toestaan, zodat de array van 14 bytes een byte overhoudt voor het afsluitende nulteken. De geheelgetalconversie vereist ook &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

Een rapport wordt omgeleid naar een bestand. Welke gegevensstroom moet een melding bevatten die uitlegt dat één invoerrecord ongeldig is?
  1. stderr
  2. stdout
  3. stdin
  4. De uitvoerstroom van het rapport
Toon antwoordVerberg antwoord
Antwoord: A
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

Een geslaagde fwrite(a, 4, 7, fp) schrijft elk gevraagd element. Wat geeft de functie terug?
  1. 4
  2. 28
  3. 7
  4. 1
Toon antwoordVerberg antwoord
Antwoord: C
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

Een 32-bits little-endian-veld bevat de bytes van acht bits 07 02 00 00. Welke waarde stelt het voor?
  1. 1794
  2. 7
  3. 131079
  4. 519
Toon antwoordVerberg antwoord
Antwoord: D
In little-endian-volgorde heeft de eerste byte gewicht 1 en de tweede gewicht 256. Dat levert 7 plus 512 op, oftewel 519. Als je 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

Een binair bestand heeft een bestandskop van 12 bytes en records van 20 bytes. Nadat een programma naar byte 12 is gegaan, leest het twee volledige records. Welke relatieve fseek-aanroep selecteert nu het vierde record geteld vanaf 1?
  1. fseek(fp, 40L, SEEK_CUR)
  2. fseek(fp, 60L, SEEK_CUR)
  3. fseek(fp, 20L, SEEK_CUR)
  4. fseek(fp, 72L, SEEK_CUR)
Toon antwoordVerberg antwoord
Antwoord: C
De twee leesbewerkingen verplaatsen de positie van byte 12 naar byte 52. Het vierde record heeft index 3, dus het begint bij byte 72, na de bestandskop en drie eerdere records; door 20 bytes vooruit te gaan bereik je het. Als je 72 met SEEK_CUR doorgeeft, wordt die absolute locatie bij de huidige positie opgeteld in plaats van geselecteerd.

Vraag 10 van 10

Een vereiste bestandskop van 16 bytes wordt gelezen met 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?
  1. Wijs de onvolledige bestandskop af zonder de velden te interpreteren.
  2. Interpreteer de bestandskop omdat er geen fout in de gegevensstroom was.
  3. Lees dezelfde bestandskop opnieuw zonder de toestand van de gegevensstroom te wijzigen.
  4. Wis de indicatoren en accepteer de met nullen gevulde velden.
Toon antwoordVerberg antwoord
Antwoord: A
Er is geen volledige bestandskop aangekomen en de indicatoren wijzen op het bestandseinde in plaats van een toegangsfout in de gegevensstroom. Het bestand mist dus vereiste formaatgegevens, ook al is het leesmechanisme niet mislukt. Het object kan een onvolledig element met een onbepaalde waarde bevatten, dus de afwezigheid van 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

Een programma wordt gestart in /work/run en opent "data/items.txt". Naar welk pad wordt dit vertaald?
  1. /work/data/items.txt
  2. /work/run/items.txt
  3. /work/run/data/items.txt
  4. Het pad naast het bronbestand
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Waar gaat in de shellopdracht producer | consumer > result.txt de standaardfoutuitvoer van de producent standaard naartoe?
  1. Naar de standaardinvoer van de consument
  2. Naar de overgeërfde foutbestemming
  3. Met de resultaten naar result.txt
  4. Naar de standaardinvoer van de producent
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Een bestand van 1.540 bytes wordt gekopieerd met een buffer van 600 bytes en fread(buf, 1, 600, in). Welke groottes moeten de geslaagde schrijfbewerkingen gebruiken?
  1. 600, 600, 340
  2. 600, 600, 600
  3. 600, 340, 600
  4. 600, 600, 0
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?
  1. De bestemming bevat nu de volledige naam.
  2. De naam van negen tekens past in de buffer.
  3. Negen opgeslagen bytes bevatten het afsluitende nulteken.
  4. Het resultaat is afgekapt en moet worden afgehandeld.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Een validator staat maximaal zeven inhoudstekens per regel toe. Een foutloos bestand bevat "cedars\nmaplesx". De validator roept herhaaldelijk fgets(buf, 8, fp) aan. Welke recordgrenzen moet die herkennen?
  1. Eén geldig record door beide stukken samen te voegen.
  2. Eén geldig record: “cedars”; wijs “maplesx” af.
  3. Twee geldige records: “cedars” en “maples”.
  4. Twee geldige records: “cedars” en “maplesx”.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

Een foutloze invoer bevat "north\nsouth\neast". Hoeveel regels en nieuweregeltekens bevat die?
  1. 3 regels en 3 nieuweregeltekens
  2. 2 regels en 2 nieuweregeltekens
  3. 3 regels en 2 nieuweregeltekens
  4. 2 regels en 3 nieuweregeltekens
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Twee systemen bieden beide 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?
  1. Codeer vastgelegde velden afzonderlijk in een gekozen bytevolgorde.
  2. Vervang het veld door een gewone unsigned int.
  3. Schrijf sizeof in de bestandskop en behoud de ruwe opslag.
  4. Behoud de struct en open het bestand met "wb".
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

De positie van een tekststroom is opgeslagen met long p = ftell(fp); en gecontroleerd. Welke geldige aanroep keert er na verder lezen naar terug?
  1. fseek(fp, p, SEEK_CUR)
  2. fseek(fp, p, SEEK_SET)
  3. fseek(fp, p + 1, SEEK_SET)
  4. fseek(fp, -p, SEEK_END)
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

Een binair bestand heeft een bestandskop van 8 bytes en vijf records van 16 bytes. Een "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?
  1. fseek(fp, -16L, SEEK_CUR), naar byte 40.
  2. fseek(fp, 0L, SEEK_CUR), op byte 56 blijven.
  3. fseek(fp, 24L, SEEK_SET), naar byte 24.
  4. fflush(fp), de positie blijft byte 56.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

Een mislukte bibliotheekaanroep stelt errno in. Het programma moet een andere gegevensstroom sluiten voordat het de oorspronkelijke oorzaak meldt. Wat moet het eerst bewaren?
  1. De huidige lengte van de uitvoerbuffer
  2. De volgende beschikbare gegevensstroompointer
  3. De huidige waarde van errno
  4. Het resultaat van een nieuwe geslaagde aanroep
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

Op Windows met bytes van acht bits wordt "x\ny\n" in tekstmodus geschreven. Hoeveel opgeslagen bytes levert dat op?
  1. 4
  2. 6
  3. 8
  4. 5
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Een C11-programma moet een nieuwe binaire export aanmaken, maar moet een eventueel bestaand bestand met die naam behouden. Welke modus past?
  1. "wb"
  2. "ab"
  3. "r+b"
  4. "wbx"
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Een formaat heeft een bestandskop van 8 bytes en records van precies 18 bytes. Het aantalveld vermeldt 6 records, maar het volledige bestand is slechts 98 bytes. Wat moet een leesprogramma concluderen voordat het geheugen voor die records reserveert?
  1. Het bestand kan de zes opgegeven records niet leveren.
  2. Het aantal is geldig omdat de bestandskop compleet is.
  3. Het bestand bevat na afronding van de lengte zes records.
  4. Het laatste record kan met nulbytes worden aangevuld.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Een volledige regel "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?
  1. Accepteer omdat errno nul bleef.
  2. Accepteer na het verwijderen van de twee letters.
  3. Wijs af omdat tekst overblijft die geen witruimte is.
  4. Wijs af omdat geen numeriek voorvoegsel is gevonden.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Een foutloos binair bestand bevat 27 bytes. Twee aanroepen gebruiken fread(b, 16, 1, fp) met een array van 16 bytes. Wat geeft de tweede aanroep terug en hoe mag het element worden gebruikt?
  1. 11; de elf bytes vormen een volledig element.
  2. 1; de ontbrekende vijf bytes worden met nullen gevuld.
  3. 0; de gedeeltelijke waarde kan als volgend record dienen.
  4. 0; het onvolledige element mag niet worden geïnterpreteerd.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

De invoer is "18 pear 26". Na één geslaagde fscanf(fp, "%d", &n) geeft dezelfde aanroep tweemaal 0 terug. Waarom heeft die 26 niet bereikt?
  1. De eerste leesbewerking heeft automatisch alle drie de velden gelezen.
  2. Het niet-passende woord blijft ongelezen bij de volgende conversie.
  3. De tweede leesbewerking bereikte EOF en verwijderde het woord.
  4. De bestemming voor het gehele getal moet een double worden.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Welke waarde geeft aan dat fseek is mislukt?
  1. Een resultaat ongelijk aan nul
  2. De nieuwe byte-offset
  3. Altijd precies EOF
  4. Altijd precies -1L
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

Een binair bestand waarin je de positie kunt wijzigen, bevat een bestandskop van 10 bytes en vier records van 14 bytes. Een leesprogramma met een gegevensstroom in modus "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?
  1. fflush(fp)
  2. fseek(fp, 0L, SEEK_CUR)
  3. fseek(fp, 38L, SEEK_SET)
  4. fseek(fp, 52L, SEEK_SET)
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

Alle schrijfbewerkingen naar de uitvoer gaven de verwachte aantallen terug, maar fclose(out) geeft EOF terug. Welk resultaat moet het programma melden?
  1. Succes, omdat alle gegevens de schrijfbuffer bereikten.
  2. Succes, nadat fclose opnieuw op dezelfde gegevensstroom is aangeroepen.
  3. Een fout, alleen als feof(out) ook is gezet.
  4. Een fout, omdat gebufferde uitvoer onvolledig kan zijn.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

De invoer wordt met succes geopend, maar het openen van de uitvoer geeft daarna NULL terug. Wat moet dit pad doen voordat het een fout teruggeeft?
  1. Sluit de invoer eenmaal en meld de fout bij het openen van de uitvoer.
  2. Sluit beide pointers, inclusief de uitvoerpointer met waarde nul.
  3. Verwijder het uitvoerbestand, ook al is het niet geopend.
  4. Keer direct terug en laat de invoer open voor de aanroepende functie.
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

Een programma leest op Linux een regel die op Windows is gemaakt en verwijdert alleen de LF. Vergelijking van het resultaat met "ready" mislukt hoewel de zichtbare letters overeenkomen. Welk achtergebleven teken verklaart dit?
  1. Een tab vóór de letters
  2. Een CR vóór de verwijderde LF
  3. Een tweede afsluitend nulteken
  4. Een door fgets opgeslagen EOF-byte
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Een programma moet zijn normale uitvoer via de shell toevoegen aan een bestand met de naam totals.txt. Welke omleidingsoperator is nodig?
  1. < totals.txt
  2. 2> totals.txt
  3. > totals.txt
  4. >> totals.txt
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Een bestand heeft drie records van 12 bytes met scores 10, 20 en 30. In modus "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?
  1. Tweede: 20; laatste: 25; in totaal vier records.
  2. Tweede: 25; laatste: 30; in totaal drie records.
  3. Tweede: 25; laatste: 25; in totaal vier records.
  4. Tweede: 20; laatste: 30; in totaal drie records.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Records met gehele getallen mogen maximaal zeven inhoudstekens bevatten. Met 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?
  1. Accepteer beide volledige numerieke voorvoegsels.
  2. Wijs beide af als te lang voor de buffer.
  3. Wijs het eerste af; accepteer het tweede.
  4. Accepteer alleen het eerste, dat op LF eindigt.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Een vervangend bestand is volledig geschreven en gesloten. Het hernoemen van het origineel naar een back-up slaagt; het plaatsen van de vervanging mislukt omdat de bestemming nu bezet is. Welke handeling behoudt het origineel zonder het nieuwe bestand op die bestemming te overschrijven?
  1. Verwijder de back-up, behoud de vervanging en geef een fout terug.
  2. Bewaar beide, meld de fout en los het naamconflict op vóór een nieuwe poging.
  3. Hernoem de back-up naar de bezette naam zonder het resultaat te controleren.
  4. Verwijder de bezette bestemming direct en probeer het plaatsen opnieuw.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

Een foutloos bestand bevat "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?
  1. n = 4, lines = 0; test feof vóór het lezen en tel de laatste regel.
  2. n = 3, lines = 1; herstel de haakjes rond de toewijzing zonder de laatste regel af te handelen.
  3. n = 3, lines = 0; herstel de haakjes rond de toewijzing en tel de laatste regel zonder nieuweregelteken.
  4. n = 2, lines = 1; behoud de voorwaarde en verander c van int in char.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Een leesbewerking met grootte 1 vraagt een vereiste bestandskop van 12 bytes op maar krijgt 9 bytes. feof is gezet en ferror is niet gezet. Daarna volgt een geslaagde fseek naar het begin. Wat blijft waar en wat moet de parser doen?
  1. EOF is gewist; wijs de onvolledige bestandskop af tenzij een volledige nieuwe poging slaagt.
  2. EOF blijft gezet; wis beide indicatoren en decodeer de negen bytes.
  3. De bestandskop is compleet geworden; decodeer die na opnieuw feof te controleren.
  4. De foutindicator is nu gezet; meld een nieuwe positioneringsfout.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

Het openen van invoer mislukt en de errno wordt opgeslagen als open_error. Het sluiten van eerder geopende uitvoer mislukt daarna en levert een andere code op, opgeslagen als close_error. Hoe kan een diagnostische melding beide fouten bewaren?
  1. Beschrijf beide bewerkingen met strerror(close_error); de nieuwste code vervangt eerdere oorzaken.
  2. Beschrijf de opening na het opruimen met perror en laat de sluitmelding weg als overbodig.
  3. Beschrijf de opgeslagen openingsfout maar geef succes terug als het opruimen de laatste instructie bereikt.
  4. Beschrijf de opening met strerror(open_error) en de sluiting met strerror(close_error); geef een fout terug.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

fopen geeft een geldige gegevensstroom terug, maar errno bevat nog een eerdere waarde ongelijk aan nul. Wat stelt die waarde vast over deze opening?
  1. De gegevensstroom moet worden gesloten als een mislukte opening.
  2. De waarde stelt geen fout vast bij deze geslaagde opening.
  3. De volgende leesbewerking zal gegarandeerd direct falen.
  4. Het bestand is met een onjuiste toegangsmodus geopend.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

Een kopieerprogramma heeft verschillende invoer- en uitvoerstromen geopend. De invoer meldt een leesfout; daarna sluit het opruimpad de uitvoer, wat EOF teruggeeft. De invoer is nog open en de uitvoer is onvolledig. Welke resterende handelingen en welk resultaat beschermen de bron en geven hulpbronnen vrij?
  1. Probeer de uitvoer opnieuw te sluiten, sluit de invoer eenmaal, verwijder onvolledige uitvoer, behoud de bron en geef een fout terug.
  2. Sluit de invoer eenmaal, verwijder onvolledige uitvoer, behoud de bron en geef succes terug als het opruimen slaagt.
  3. Wis de leesfout, sluit de invoer eenmaal, behoud beide bestanden en geef succes terug als de laatste sluiting slaagt.
  4. Sluit de invoer eenmaal, probeer onvolledige uitvoer te verwijderen en controleer dat, behoud de bron en geef een fout terug.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Kom je niet uit je eigen opgave? Huiswerkhulp

Kernbegrippen

61 Kernbegrippen, elk met een definitie

A

Aantal geslaagde toewijzingen
Het aantal geslaagde toewijzingen is het aantal doelwaarden dat een aanroep voor opgemaakte invoer met succes opslaat.
Een record met twee velden is bij fscanf alleen compleet als het teruggegeven aantal geslaagde toewijzingen 2 is.
Afsluitend nulteken
Een afsluitend nulteken is het teken met waarde nul dat het einde van een C-string aangeeft zonder mee te tellen als zichtbare tekst.
Een buffer heeft één extra byte nodig naast de tekens die fgets mag lezen.
Afsluitstatus
Een afsluitstatus is het resultaat dat een programma aan zijn omgeving teruggeeft, zodat scripts een geslaagde en een mislukte uitvoering kunnen onderscheiden.
Een afhankelijke opdracht na && mag niet worden uitgevoerd na een onvolledige bestandskopie.

B

Bestandseinde-indicator
De bestandseinde-indicator legt vast dat een eerdere invoerbewerking het einde van de gegevensstroom heeft bereikt.
Je controleert die na het lezen; vooraf testen kan niet voorspellen of de volgende byte bestaat.
Bestandskop
Een bestandskop is een begindeel van een bestand met identificerende en structurele velden die nodig zijn om de daaropvolgende gegevens te interpreteren.
Controleer de volledige signatuur en het aantal voordat je de records vertrouwt die een bestandskop opgeeft.
Bestandspositie
Een bestandspositie is de locatie in een gegevensstroom waar de volgende lees- of schrijfbewerking begint, als die gegevensstroom positionering ondersteunt.
Het lezen van één binair record van 24 bytes schuift de positie voorbij alle 24 bytes ervan.
Bestandssignatuur
Een bestandssignatuur is een vastgelegde reeks beginbytes die een leesprogramma helpt een bestandsformaat of versie te herkennen.
De JPEG-beginmarkering helpt mogelijke afbeeldingen te vinden voordat de resterende structuur wordt gecontroleerd.
Bestandsvervanging met herstelmogelijkheid
Een bestandsvervanging met herstelmogelijkheid houdt een bestaand bestand beschikbaar totdat een voltooide nieuwe versie met succes is geplaatst.
Als de hernoeming die de nieuwe versie plaatst mislukt, behoud dan de back-up en meld de locatie voor herstel.
Big-endian-bytevolgorde
Big-endian-bytevolgorde plaatst de meest significante byte van een waarde van meerdere bytes als eerste in de bytereeks.
De waarde 258 verschijnt als 00 00 01 02 in een 32-bits big-endian-veld.
Binaire modus
Binaire modus is toegang tot een gegevensstroom die bytewaarden behoudt zonder de tekenvertalingen die voor tekst worden gebruikt.
Open een afbeelding met "rb", zodat tekstvertaling de gecomprimeerde bytes niet kan veranderen.
Bloksgewijs kopiëren
Bij bloksgewijs kopiëren draag je een bestand over via een herbruikbare buffer en schrijf je alleen de bytes die elke geslaagde leesbewerking heeft geleverd.
Een korte laatste leesbewerking moet een korte laatste schrijfbewerking opleveren, zodat geen overgebleven buffergegevens worden toegevoegd.
Buffering
Buffering is het tijdelijk verzamelen van gegevens in het geheugen, zodat veel kleine invoer- en uitvoerverzoeken grotere overdrachten kunnen delen.
Een rapport kan in de uitvoerbuffer blijven staan totdat de buffer wordt leeggemaakt of de gegevensstroom wordt gesloten.
Bytevolgorde
Bytevolgorde is de volgorde waarin de bytes van een waarde van meerdere bytes in het geheugen of een bestand zijn gerangschikt.
Een bestandsformaat legt zijn bytevolgorde vast onafhankelijk van de computer die het leest.

C

clearerr
clearerr wist zowel de bestandseinde-indicator als de foutindicator van een gegevensstroom zonder de oorzaak van een fout te verhelpen.
Na het oplossen van een invoerprobleem kan een programma de indicatoren wissen vóór een gecontroleerde nieuwe poging.
Conversiespecificatie
Een conversiespecificatie is een instructie die met een procentteken begint en bepaalt hoe opgemaakte invoer en uitvoer een waarde afdrukt of converteert.
Bij invoer vereist %lf een double *, terwijl %f een bestemming van het type float * vereist.
CSV-velden tussen aanhalingstekens plaatsen
CSV-velden tussen aanhalingstekens plaatsen houdt in dat tekstvelden met komma’s, aanhalingstekens of nieuweregeltekens worden ingesloten en aanhalingstekens binnen die velden worden verdubbeld.
Een stadsnaam met een komma moet één veld blijven wanneer het record opnieuw wordt gelezen.

D

Directe toegang
Directe toegang is het rechtstreeks naar een gekozen locatie gaan in een bestand dat positiewijziging ondersteunt, zonder alle voorafgaande gegevens te lezen.
Met een bestandskop en een vaste recordbreedte kan een programma berekenen waar een geselecteerd record begint.

E

Eigenaarschap van hulpbronnen
Eigenaarschap van hulpbronnen geeft aan welk deel van een programma verantwoordelijk is voor het vrijgeven van een gegevensstroom of een andere verkregen hulpbron.
Een hulpfunctie sluit een gegevensstroom die ze heeft geopend en laat een geleende gegevensstroom over aan de aanroepende functie.
Eindpointer
Een eindpointer van strtol wijst naar het eerste teken na het numerieke voorvoegsel dat de conversie heeft geaccepteerd.
Bij "86cm" bereikt de pointer de c, zodat een veld voor een geheel getal het achtervoegsel kan afwijzen.
EOF
EOF is een negatieve geheelgetalconstante die tekeninvoerfuncties gebruiken wanneer de invoer eindigt of een leesfout optreedt.
Bewaar de teruggegeven waarde in een int, zodat een echte byte niet voor deze markering wordt aangezien.
ERANGE
ERANGE is de standaardcode voor een bereikfout die wordt gebruikt wanneer een conversie of berekening een waarde buiten het ondersteunde bereik oplevert.
Zet errno vóór strtol op nul, zodat een oude bereikfout niet voor een nieuwe wordt aangezien.
errno
errno is een wijzigbare geheelgetalexpressie voor een foutcode, waarvan de betekenis afhangt van het gedocumenteerde foutgedrag van een functie.
Sla de waarde direct na een gemelde fout op, voordat een andere bibliotheekaanroep die kan veranderen.
Exclusieve bestandsaanmaak
Exclusieve bestandsaanmaak is het openen van een nieuw bestand alleen als de naam nog niet naar een bestaand bestand verwijst.
De C11-modus "wx" beschermt een bestaand rapport tegen wissen bij het openen.

F

fclose
fclose is de functie die wachtende uitvoer wegschrijft, een gegevensstroom sluit en succes met 0 of een fout met EOF meldt.
Nadat fclose(fp) terugkeert, kan die gegevensstroom niet opnieuw worden gebruikt, ook niet bij een fout.
fgetc
fgetc is de functie die een teken leest als een naar int geconverteerde tekenwaarde zonder teken, of EOF teruggeeft.
Een lus telt een scheidingsteken pas nadat fgetc met succes een teken heeft teruggegeven.
fgets
fgets leest een begrensde reeks tekens, bewaart een aangetroffen nieuweregelteken en voegt bij succes een afsluitend nulteken toe.
Een regel die langer is dan een buffer van 32 bytes wordt over meerdere geslaagde leesbewerkingen teruggegeven.
FILE-pointer
Een FILE-pointer is een waarde van het type FILE * die bibliotheekfuncties toegang geeft tot een gegevensstroom en de opgeslagen toestand ervan.
Geef fp door aan fgetc nadat je hebt gecontroleerd of het openen een geldige gegevensstroompointer heeft teruggegeven.
fopen
fopen is de functie van de standaardbibliotheek die een genoemd bestand in een gekozen modus opent en een gegevensstroompointer of NULL teruggeeft.
Een mislukte opening voor lezen laat geen geldige gegevensstroom achter voor een latere aanroep van fgetc of fclose.
Foutindicator
De foutindicator legt een lees- of schrijffout van een gegevensstroom vast en blijft gezet totdat die expliciet wordt gewist.
Nadat fgets NULL teruggeeft, kan ferror mislukte toegang onderscheiden van normale voltooiing.
fpos_t
fpos_t is het ondoorzichtige type dat fgetpos en fsetpos gebruiken om alle vereiste bestandspositie-informatie op te slaan en te herstellen.
Een opgeslagen positiewaarde wordt via de bibliotheekfuncties hersteld, zonder met de waarde te rekenen.
fputc
fputc is de functie die één teken naar een gegevensstroom schrijft en die tekenwaarde of bij een fout EOF teruggeeft.
Een filter dat hoofdletters maakt, controleert elke fputc nadat het het gelezen teken heeft veranderd.
fread
fread draagt invoerbytes over naar het geheugen en geeft het aantal volledige elementen terug dat bij de gevraagde grootte en het gevraagde aantal is verkregen.
Een onvolledige leesbewerking van een record moet worden gecontroleerd voordat velden van dat record worden geïnterpreteerd.
fseek
fseek vraagt een nieuwe bestandspositie aan ten opzichte van het begin, de huidige positie of het einde en geeft bij een fout een waarde ongelijk aan nul terug.
Een programma controleert de positiewijziging voordat het het record leest dat op de gevraagde offset wordt verwacht.
ftell
ftell meldt de positie van een gegevensstroom als een long en geeft bij een fout -1L terug; een tekstpositie moet als een opgeslagen locatie worden behandeld.
Een programma dat binaire records leest, kan de positie controleren nadat het een volledig record van vaste grootte heeft verwerkt.
fwrite
fwrite draagt objectbytes vanuit het geheugen over naar een gegevensstroom en geeft het aantal volledig geschreven elementen terug.
Om een array te schrijven zijn het gegevensadres, de elementgrootte en het bedoelde aantal elementen nodig.

G

Gegevensstroom
Een gegevensstroom is een kanaal van de standaardbibliotheek dat een programma verbindt met een reeks invoer- of uitvoertekens.
Een leesprogramma kan een schijfbestand of terminalinvoer via dezelfde interface voor gegevensstromen verwerken.
Gegevensstroom voor lezen en schrijven
Een gegevensstroom voor lezen en schrijven is een gegevensstroom die met een +-modus is geopend, zodat zowel invoer als uitvoer volgens wisselregels is toegestaan.
Als je een bestaand binair recordbestand opent met "r+b", blijft het behouden voor lezen en schrijven.
Geheelgetaltype met exacte bitbreedte
Een geheelgetaltype met exacte bitbreedte heeft waar beschikbaar een exact vastgelegde bitbreedte, zonder een externe bytevolgorde te kiezen.
Een uint32_t-veld heeft nog steeds expliciete bytecodering nodig om een uitwisselingsbestand overdraagbaar te maken.

H

Huidige werkmap
De huidige werkmap is de referentiemap van het draaiende programma voor het bepalen van relatieve padnamen.
Als je een programma in een andere map start, verandert het bestand waarnaar "data/input.txt" verwijst.

L

Little-endian-bytevolgorde
Little-endian-bytevolgorde plaatst de minst significante byte van een waarde van meerdere bytes als eerste in de bytereeks.
De vier bytes 02 01 00 00 stellen 258 voor in een 32-bits little-endian-veld.

N

Niet-passende invoer
Bij niet-passende invoer passen invoertekens niet bij een gevraagde opgemaakte conversie, zonder dat daarvoor een invoer- of uitvoerfout nodig is.
Een geheel getal lezen uit "oak" kan 0 teruggeven terwijl de bestandseinde-indicator en foutindicator van de gegevensstroom niet zijn gezet.

O

Omleiding
Omleiding is een shellbewerking die de standaardstromen van een programma met bestanden verbindt zonder de invoer- en uitvoercode te wijzigen.
De operator 2> kan de diagnostische meldingen van een programma in een afzonderlijk bestand opslaan.
Onvolledig element
Een onvolledig element is een bij een leesbewerking verkregen onvolledige eenheid ter grootte van een object, die niet meetelt in het aantal volledige elementen van fread.
Bij invoer met de grootte van een record kunnen resterende bytes aan het einde niet als een geldig record worden behandeld.
Openingsmodus
Een openingsmodus is een string die fopen vertelt welke toegang is toegestaan en of bestaande inhoud wordt behouden of gewist.
Als je "a" kiest, blijven eerdere logvermeldingen behouden en komt elke nieuwe schrijfbewerking aan het einde.
Opruimpad
Een opruimpad is de route in de besturingsstroom die hulpbronnen vrijgeeft die zijn verkregen voordat succes of een fout werd vastgesteld.
Eén label kan de geopende gegevensstromen sluiten na verschillende verwerkingsfouten.
Opvulling in een struct
Opvulling in een struct bestaat uit extra bytes die een compiler voor uitlijning tussen velden of na het laatste veld kan invoegen.
Een char gevolgd door een int kan acht bytes innemen, ook als hun groottes samen vijf zijn.

P

perror
perror schrijft een label en de melding voor de huidige errno-waarde naar standaardfoutuitvoer, gevolgd door een nieuweregelteken.
Een label met de bestandsnaam maakt een melding over een ontbrekend bestand nuttiger dan een melding zonder label.
Pipe
Een pipe verbindt de standaarduitvoer van het ene programma met de standaardinvoer van een ander programma als een gegevensstroom.
Een generator kan rechtstreeks gegevens aan een sorteerprogramma leveren zonder eerst een tussenbestand op te slaan.

R

Regelanalyse
Regelanalyse is het interpreteren van de velden van een volledige invoerregel nadat die regel in het geheugen is gelezen.
Een ongeldige hoeveelheid kan met het regelnummer worden gemeld voordat het volgende record wordt gelezen.
rename
rename verandert de naam van een bestand en geeft 0 terug bij succes; de afhandeling van een bestaande bestemming hangt af van de implementatie.
Houd een back-up beschikbaar als het plaatsen van een voltooide vervanging onder de oorspronkelijke naam mislukt.

S

snprintf
snprintf maakt tekst op in een begrensde array en geeft de volledig benodigde lengte terug, exclusief het afsluitende nulteken, of een negatieve waarde bij een fout.
Een retourwaarde gelijk aan de capaciteit geeft al aan dat de volledige tekst niet paste.
Standaardfoutuitvoer
Standaardfoutuitvoer is de al geopende gegevensstroom stderr voor diagnostische meldingen, onafhankelijk van de normale resultatenstroom van een programma.
Een mislukte conversie kan op stderr worden beschreven terwijl geldige gegevens naar stdout gaan.
Standaardinvoer
Standaardinvoer is de al geopende gegevensstroom stdin waaruit een programma normaal gesproken zijn belangrijkste invoer ontvangt.
Met < values.txt leest een filter bestandsgegevens via stdin in plaats van via het toetsenbord.
Standaarduitvoer
Standaarduitvoer is de al geopende gegevensstroom stdout voor de normale resultaten en andere primaire uitvoer van een programma.
Een shellpipe stuurt stdout van een conversieprogramma naar de standaardinvoer van een ander programma.
strcspn
strcspn vindt de lengte van het begin van een string vóór een teken uit een opgegeven verzameling afwijstekens, of de volledige lengte als geen ervan voorkomt.
Met "\r\n" als verzameling afwijstekens vind je een regeleinde zonder gewone gegevens aan het einde te verwijderen.
strerror
strerror geeft meldingstekst van de bibliotheek terug voor een opgegeven foutcode, zodat een programma een eigen diagnostische melding kan samenstellen.
Gebruik een opgeslagen code wanneer het opruimen de huidige waarde van errno kan hebben vervangen.

T

Tekstmodus
Tekstmodus is toegang tot een gegevensstroom voor leesbare regels, waarbij de bibliotheek tekens kan vertalen volgens systeemconventies.
Op Windows verandert tekstuitvoer een nieuweregelteken van een programma in de opgeslagen CR LF-reeks.
Terugplaatsen van een teken
Terugplaatsen van een teken is een teken teruggeven aan een invoerstroom, zodat de volgende leesbewerking het opnieuw ophaalt.
Een parser kan een scheidingsteken bekijken en ungetc gebruiken om het voor de volgende fase te laten staan.
Toevoegmodus
Toevoegmodus is toegang die elke uitvoerbewerking naar het huidige einde van het bestand dwingt, ongeacht eerdere positiewijzigingen.
Als je in een "a+b"-gegevensstroom naar byte nul gaat, kun je invoer selecteren maar de volgende schrijfbewerking niet omleiden.

V

Veldbreedte
Een veldbreedte bepaalt een minimale uitvoerbreedte of een maximale invoerbreedte, afhankelijk van de functie voor opgemaakte invoer of uitvoer.
Bij invoer laat %23s ruimte over voor een afsluitend nulteken in een tekenarray van 24 bytes.

W

Wijziging op dezelfde bestandspositie
Een wijziging op dezelfde bestandspositie vervangt gegevens binnen de bestaande bestandsruimte zonder volgende records te verschuiven of opnieuw te schrijven.
Een gewijzigd record van vaste grootte moet op de oorspronkelijke beginpositie worden teruggeschreven.

Over deze les

Door StudeliFout melden

Maak je eigen Studiemateriaal

Maak van je aantekeningen, slides en documenten Leermodules, Oefenvragen, Flashcards en 6 andere tools.

Aan de slag