Entrées et sorties de fichiers en C

Apprends comment les programmes C lisent et écrivent des fichiers avec la bibliothèque standard d’entrées-sorties. Ouvre des fichiers sous forme de flux dans le mode adapté et relie les programmes à l’entrée standard, à la sortie standard et à la sortie d’erreur standard. Traite le texte caractère par caractère, par lectures et écritures formatées, puis ligne par ligne avec fgets. Stocke des tableaux et des enregistrements dans des fichiers binaires avec fread et fwrite, conçois des formats de fichiers lisibles correctement sur d’autres machines et accède directement à n’importe quel enregistrement avec fseek. Tout au long de l’unité, vérifie chaque résultat, explique les échecs avec errno et libère les ressources pour que tes programmes échouent sans danger et indiquent fidèlement le résultat de leurs opérations.

DébutantUnité 7 sur 8 de la formation Formation : Langage C : pointeurs, mémoire et structures

Ce que tu vas apprendre

  1. Expliquer comment les programmes C accèdent aux fichiers par des flux mis en tampon, ouvrir et fermer les fichiers dans le mode adapté et utiliser les trois flux standard avec des redirections en ligne de commande.
    • Expliquer les flux, la mise en tampon et la différence entre mode texte et mode binaire
      Décrire ce que sont un flux et un FILE *, le fonctionnement de la mise en tampon et de la position dans le fichier, ainsi que les différences entre mode texte et mode binaire sous Windows et sur les systèmes de type Unix.
    • Ouvrir et fermer des fichiers avec fopen et fclose
      Ouvrir un fichier dans le mode adapté à la tâche, vérifier son ouverture, trouver les fichiers à partir de leur chemin et fermer chaque flux une fois le travail terminé.
    • Utiliser stdin, stdout et stderr avec des redirections et des tubes
      Utiliser les trois flux avec lesquels chaque programme C démarre, envoyer les résultats et les messages d’erreur au bon endroit et permettre aux utilisateurs de les rediriger depuis la ligne de commande.
  2. Traiter les fichiers texte caractère par caractère, par lectures et écritures formatées et ligne par ligne, en choisissant une méthode sûre pour chaque cas et en testant chaque résultat.
    • Lire et écrire caractère par caractère
      Utiliser fgetc et fputc avec une variable int, s’arrêter correctement en fin de fichier, puis compter, copier et transformer du texte caractère par caractère.
    • Écrire et lire du texte formaté avec fprintf et fscanf
      Écrire des nombres et des chaînes dans des fichiers selon une présentation choisie, construire des noms de fichiers avec sprintf et lire des données formatées avec fscanf en vérifiant combien d’éléments ont été convertis.
    • Lire des lignes en toute sécurité avec fgets
      Lire du texte ligne par ligne avec fgets, gérer les caractères de fin de ligne, les lignes longues et les fins de ligne Windows, puis analyser chaque ligne après sa lecture.
  3. Écrire et lire des octets bruts, des tableaux et des structures avec fwrite et fread, puis concevoir des formats de fichiers binaires lisibles correctement sur d’autres machines.
    • Lire et écrire des données binaires avec fread et fwrite
      Utiliser fread et fwrite avec la taille et le nombre d’éléments adaptés, copier des fichiers par blocs, reconnaître les types de fichiers à leurs premiers octets et stocker des tableaux et des structures.
    • Concevoir des formats binaires utilisables sur différentes machines
      Expliquer comment la taille des types, le boutisme et le bourrage des structures rendent les fichiers binaires bruts dépendants de la machine, puis lire et écrire des champs de plusieurs octets selon un format défini.
  4. Obtenir et modifier la position dans le fichier pour accéder directement aux données nécessaires, puis mettre à jour en place des enregistrements de taille fixe sans réécrire tout le fichier.
    • Obtenir et modifier la position dans le fichier avec ftell, fseek et rewind
      Indiquer la position courante, se déplacer par rapport au début, à la position courante ou à la fin, calculer les décalages des enregistrements et les tailles de fichiers, et connaître les restrictions applicables aux flux texte.
    • Mettre à jour en place des enregistrements de taille fixe
      Ouvrir un fichier en lecture et en écriture, respecter la règle de passage de l’une à l’autre et modifier, ajouter ou supprimer des enregistrements sans corrompre le fichier.
  5. Vérifier le résultat de chaque opération sur un fichier, expliquer les échecs à l’utilisateur avec errno et libérer chaque ressource pour que les programmes échouent sans danger et indiquent fidèlement leur réussite.
    • Vérifier chaque résultat et distinguer la fin de fichier des erreurs
      Savoir comment chaque fonction de stdio signale un échec, utiliser feof et ferror après la fin d’une boucle de lecture et détecter les erreurs d’écriture qui n’apparaissent que lorsque le tampon est écrit.
    • Expliquer les échecs avec errno, perror et strerror
      Utiliser errno pour comprendre pourquoi un appel à la bibliothèque a échoué et afficher des messages d’erreur clairs qui indiquent le programme, le fichier et la raison.
    • Libérer les ressources et indiquer le résultat de l’opération
      Fermer chaque flux sur tous les chemins, ne laisser aucun fichier de sortie partiellement écrit et renvoyer un code de retour qui reflète fidèlement le résultat.

Entrées et sorties de fichiers en C : cours

5 chapitres

1

Ouvrir des fichiers sous forme de flux et relier les programmes à leurs entrées et sorties

Les fichiers sont accessibles à un programme par des flux, qui contrôlent l’accès, la mise en tampon et la position. Le choix d’un mode peut préserver les anciennes données, les effacer ou imposer chaque écriture à la fin : l’ouverture fait donc partie de la conception du programme. Les trois flux standard étendent la même interface aux terminaux, aux fichiers redirigés et aux tubes. Comprendre leurs connexions permet d’écrire une même fonction de traitement utilisable dans plusieurs situations et de séparer les diagnostics des résultats utiles.

1.1 Expliquer les flux, la mise en tampon et la différence entre mode texte et mode binaire

Un fichier contient des données stockées ; un flux est le canal qu’un programme en cours d’exécution utilise pour y accéder. Les mêmes fonctions opérant sur des flux peuvent aussi fonctionner avec un terminal ou un tube. Inclus <stdio.h> et conserve le pointeur du flux dans une variable de type FILE *.

Ce que le flux mémorise

L’objet FILE de la bibliothèque suit l’état d’un tampon, la position courante dans le fichier ainsi que les indicateurs de fin de fichier et d’erreur. Passe son pointeur à des fonctions comme fgetc et fprintf ; n’examine ni ne modifie ses champs. Une lecture ou une écriture fait avancer la position au-delà des données traitées. Dans un fichier binaire commençant par "hello\nworld\n", la lecture des six premiers octets place la lecture suivante au w.

La mise en tampon regroupe les petites opérations en transferts plus importants. Une écriture réussie peut signifier que les données ont atteint un tampon en mémoire, tandis que leur transfert au système d’exploitation reste en attente. fflush(fp) envoie au système d’exploitation les sorties en attente. fclose(fp) fait de même avant de libérer le flux. Aucun des deux ne garantit que les données résisteront à une coupure soudaine de courant.

Mode de mise en tamponMoment du transfert des sorties
Entièrement mis en tamponLorsque le tampon est plein, vidé explicitement ou fermé
Mis en tampon par ligneAussi lorsqu’un caractère de fin de ligne est écrit
Non mis en tamponDès que possible

Les fichiers ordinaires ouverts avec fopen sont entièrement mis en tampon. Les périphériques interactifs peuvent utiliser une autre mise en tampon. Un terminal ou un tube ne fournit pas de positions ordinaires permettant de se déplacer en avant et en arrière dans un fichier.

Choisir le mode texte ou binaire

Le mode texte traite des lignes lisibles par une personne. Le mode binaire préserve les valeurs des octets sans traduction de texte. Ajoute b au mode d’ouverture pour les données binaires : "rb" les lit et "wb" les écrit. Les images, les données compressées et les enregistrements bruts exigent le mode binaire, même si une version en mode texte semble fonctionner sur ton ordinateur.

Sous Linux et macOS, une fin de ligne est représentée par un octet LF, 0x0A, et les deux modes se comportent normalement de la même façon. Sous Windows, la sortie en mode texte traduit '\n' en deux octets CR LF, 0x0D 0x0A. L’entrée en mode texte traduit CR LF en un seul '\n'. L’entrée en mode texte de Microsoft reconnaît aussi 0x1A, Ctrl+Z, comme une fin de fichier.

Par exemple, écrire "a\nb\nc\n" en mode texte produit six octets sous Linux ou macOS et neuf sous Windows. Les trois lettres occupent trois octets ; les trois fins de ligne Windows en occupent six. La lecture du fichier Windows en mode texte sous Windows donne six caractères, tandis que le mode binaire donne accès aux neuf octets.

Distinguer les octets du fichier de son texte

Une ligne créée sous Windows et lue sous Linux peut conserver "\r\n". Supprimer seulement '\n' laisse '\r', ce qui peut faire échouer une comparaison avec "yes". Une position dans un flux texte peut aussi refléter l’état de la traduction ; enregistre et restaure donc les positions dans un flux texte au lieu de calculer des décalages en octets.

La garantie du C selon laquelle le texte est restitué exactement s’applique aux caractères imprimables, aux tabulations et aux caractères de fin de ligne, sans espace immédiatement avant une fin de ligne et avec un caractère de fin de ligne à la fin de la dernière ligne. Les autres suites d’octets doivent être placées dans des flux binaires. Les octets en C comptent au moins huit bits ; les exemples portant sur des valeurs d’octets de 0 à 255 et sur les formats de fichiers courants utilisent des octets de huit bits.

Points clés

  • Un FILE * donne aux fonctions de bibliothèque accès à un flux et à son état.
  • Les sorties mises en tampon peuvent échouer lors du vidage du tampon ou de la fermeture ; vérifie ces résultats.
  • Sous Windows, le mode texte traduit les fins de ligne ; le mode binaire préserve les octets.
  • Enregistre les positions dans les flux texte ; calcule les décalages dans les fichiers binaires.
  • Précise que tu supposes les octets sur huit bits lorsque tu interprètes des formats binaires courants.

1.2 Ouvrir et fermer des fichiers avec fopen et fclose

Choisis un mode d’ouverture avant d’écrire le code de traitement. Le mode détermine si le fichier doit exister, si son contenu est conservé et où les écritures ont lieu. fopen agit immédiatement sur le fichier : une ouverture réussie avec "w" efface un fichier existant, même si aucune écriture n’a lieu ensuite.

Adapter le mode à la tâche
ModeFichier existantFichier absentAccès
"r"Conserve le contenuÉchoueLecture depuis le début
"w"Efface le contenuCrée le fichierÉcriture depuis le début
"a"Conserve le contenuCrée le fichierÉcriture à la fin
"r+"Conserve le contenuÉchoueLecture et écriture
"w+"Efface le contenuCrée le fichierLecture et écriture
"a+"Conserve le contenuCrée le fichierLecture partout ; écriture à la fin

Ajoute b pour le mode binaire ; "r+b" et "rb+" ont le même sens. C11 ajoute la création exclusive avec x dans les modes d’écriture, comme "wx" et "wbx" : l’ouverture échoue si le fichier existe. Utilise une chaîne de mode documentée ; les lettres de mode supplémentaires ne sont pas portables.

Vérifier avant d’utiliser le pointeur

fopen renvoie un pointeur vers un nouveau flux si l’ouverture réussit et NULL en cas d’échec. Un fichier absent, un chemin inaccessible ou un répertoire manquant peuvent tous provoquer un échec.

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;
}

Cet extrait utilise <stdio.h> et <stdlib.h>. Des fonctions comme fgetc, fprintf et fclose ont besoin d’un flux valide ; leur passer NULL est un comportement indéfini. fflush(NULL) est une exception volontaire qui vide les tampons de tous les flux de sortie.

fclose renvoie 0 si la fonction réussit et EOF en cas d’échec. Une fois que la fonction a été appelée, le flux est fermé, y compris lorsque la fermeture signale une erreur. Ne rappelle pas fclose sur le même pointeur et ne l’utilise plus pour des entrées-sorties. Ferme rapidement les fichiers : les flux disponibles sont limités et le C garantit la prise en charge d’au moins huit flux ouverts, dont les trois flux standard.

Résoudre les chemins à partir du programme en cours d’exécution

Un chemin relatif comme "data/input.txt" part du répertoire de travail courant, et non du dossier du fichier source. Lancer le programme ailleurs change ce que désigne ce chemin. Les barres obliques fonctionnent aussi avec la bibliothèque C de Microsoft. Une barre oblique inverse dans une chaîne C doit être doublée, comme dans "C:\\data\\in.txt" ; sinon, une séquence d’échappement comme \n peut modifier la chaîne.

Pour les noms fournis en ligne de commande, utilise int main(int argc, char *argv[]). Dans une commande de copie avec deux noms, argc doit valoir 3, argv[1] est le nom de l’entrée et argv[2] celui de la sortie. Ouvre d’abord l’entrée pour qu’un nom d’entrée mal saisi ne crée ni n’efface la sortie. Si l’ouverture de la sortie échoue, ferme l’entrée déjà ouverte. Utilise des fichiers d’entrée et de sortie distincts : ouvrir d’abord l’entrée ne la protège pas si le nom de sortie désigne le même fichier.

Un retour normal de main ou un appel à exit ferme les flux, mais les fermetures explicites permettent au programme de détecter les erreurs de sortie tardives. Un plantage peut faire perdre les données mises en tampon ; abort ne garantit pas la libération des ressources associée à une sortie normale.

Points clés

  • Une ouverture avec "w" efface immédiatement un fichier existant.
  • Vérifie que fopen n’a pas renvoyé NULL avant d’utiliser le flux.
  • Les chemins relatifs partent du répertoire de travail courant.
  • Ferme chaque flux ouvert une seule fois et ne le réutilise jamais après fclose.
  • Ouvre l’entrée de la copie avant la sortie et utilise des fichiers distincts.
  • Les modes d’écriture exclusifs de C11 échouent lorsque la destination existe.

1.3 Utiliser stdin, stdout et stderr avec des redirections et des tubes

Un programme C démarre avec trois flux ouverts : l’entrée standard stdin, la sortie standard stdout et la sortie d’erreur standard stderr. Ils sont généralement reliés au terminal, mais l’interpréteur de commandes peut les relier à des fichiers ou à d’autres programmes. Tu peux les passer directement aux fonctions opérant sur des flux.

Séparer les résultats des diagnostics

printf écrit dans stdout ; scanf lit dans stdin. getchar() lit un caractère dans stdin, et putchar(c) en écrit un dans stdout. puts(s) écrit une chaîne dans stdout et ajoute un caractère de fin de ligne. Leurs formes opérant sur des flux permettent au même code de fonctionner avec un fichier :

void print_total(FILE *out, int total)
{
    fprintf(out, "Total: %d\n", total);
}

L’appel print_total(stdout, 12) affiche un résultat ; passer un pointeur vers un fichier ouvert envoie la même présentation dans ce fichier. Dans du code de production, fais en sorte que la fonction auxiliaire signale un échec d’écriture à sa fonction appelante.

Écris les diagnostics avec fprintf(stderr, ...). Un programme de traitement de données peut alors produire un fichier de résultats propre tout en expliquant les échecs séparément. Une erreur affichée dans stdout peut corrompre les données qu’un autre programme s’attend à lire.

Laisser l’interpréteur de commandes choisir les connexions

Dans un interpréteur de commandes de type Unix, cette commande sépare les trois flux :

./sortnums < numbers.txt > sorted.txt 2> errors.txt

< fournit l’entrée, > crée ou remplace la sortie, >> ajoute à la sortie et 2> redirige les erreurs. Les opérateurs de redirection fonctionnent aussi dans l’invite de commandes Windows, où le nom du programme s’écrit généralement sortnums.exe. Un tube, comme dans generate | sortnums, envoie la sortie standard du premier programme vers l’entrée standard du second. Il ne transporte pas stderr par défaut.

Un filtre peut accepter un nom d’entrée facultatif et, à défaut, lire stdin. Suis la propriété des ressources : ferme un flux que ta fonction a ouvert, mais laisse un flux emprunté à son propriétaire. Pour un filtre simple en ligne de commande, if (in != stdin) permet de reconnaître le fichier qu’il a lui-même ouvert.

Vider les tampons des invites avant d’attendre une entrée

Un terminal entraîne généralement une mise en tampon de stdout par ligne. Un fichier redirigé ou un tube entraîne généralement sa mise en tampon complète. stderr n’est pas entièrement mis en tampon. Cela explique pourquoi l’ordre des sorties peut changer lorsque les deux flux sont envoyés vers une même destination.

Après printf("Saving..."), le texte peut rester dans le tampon en l’absence de caractère de fin de ligne. Utilise fflush(stdout) et vérifie si la fonction renvoie EOF lorsque le texte doit apparaître avant l’opération suivante. Vider le tampon transmet les sorties en attente ; cela ne lit ni n’élimine les entrées. Le C standard ne définit pas fflush(stdin) comme une opération permettant de vider l’entrée. Pour ignorer une ligne, lis les caractères jusqu’à '\n' ou EOF.

Si un programme affiche deux lignes dans stdout et un avertissement dans stderr entre les deux, un terminal les affichera généralement dans l’ordre du programme. Avec > out.txt 2>&1, l’avertissement peut atteindre le fichier avant la sortie standard mise en tampon. N’utilise pas l’ordre d’affichage des flux mélangés comme trace de l’ordre d’exécution.

Renvoyer un résultat exploitable par les scripts

Renvoie EXIT_SUCCESS ou 0 lorsque toute la tâche réussit et EXIT_FAILURE lorsqu’elle échoue. Les deux macros sont déclarées dans <stdlib.h>. Une commande comme convert in.csv out.txt && echo done n’exécute la seconde commande qu’après une réussite. Un code de retour fidèle au résultat compte autant qu’un diagnostic utile.

Points clés

  • Utilise stdin, stdout et stderr sans les ouvrir.
  • Envoie les résultats du programme vers stdout et les diagnostics vers stderr.
  • La redirection modifie les connexions ; un tube transmet la sortie standard à l’entrée standard.
  • Vide le tampon de sortie lorsqu’une invite doit être visible immédiatement.
  • Renvoie un code de retour indiquant l’échec lorsque la tâche demandée échoue.
2

Lire et écrire des fichiers texte

Le traitement du texte peut se faire à trois niveaux utiles : les caractères individuels, les valeurs formatées et les lignes entières. Ce choix modifie ce que le programme peut valider et la façon dont il se remet d’une entrée incorrecte. Une boucle de lecture des caractères doit distinguer les octets de la fin de fichier ; une lecture formatée doit vérifier son nombre d’affectations réussies ; une lecture de ligne doit gérer la capacité et les enregistrements incomplets. Des exemples détaillés montrent comment préserver les données effectivement lues tout en maîtrisant leur analyse et le signalement des problèmes.

2.1 Lire et écrire caractère par caractère

Les entrées-sorties caractère par caractère sont utiles lorsque chaque caractère détermine la suite du traitement : compter des séparateurs, changer la casse ou reconnaître un petit format texte. fgetc lit un caractère et fputc en écrit un. Les deux fonctions sont déclarées dans <stdio.h>.

Conserver la valeur de retour dans un int

fgetc(fp) renvoie le caractère sous forme d’un unsigned char converti en int, ou la constante négative EOF lorsqu’aucun caractère n’est disponible parce que l’entrée est terminée ou qu’une lecture a échoué. Sur les systèmes courants dont les octets comptent huit bits, les valeurs valides des octets vont de 0 à 255. Un int permet de distinguer ces valeurs de 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. */

Un simple char peut perdre cette distinction. Avec un char signé, l’octet 0xFF peut devenir -1 et ressembler à une fin de fichier. Avec un char non signé, la valeur négative EOF est convertie en octet et ne peut jamais être égale à EOF lors d’une comparaison.

Conserve les deux parenthèses autour de l’affectation. Dans c = fgetc(in) != EOF, la comparaison a lieu en premier : c reçoit donc 1 ou 0 au lieu du caractère. getc effectue la même lecture et peut être une macro ; fputc renvoie le caractère écrit ou EOF.

Compter les lectures réussies, pas les tentatives

La condition de la boucle doit effectuer la lecture. feof décrit une tentative précédente qui a atteint la fin ; il ne peut pas prédire la tentative suivante. Une boucle qui vérifie !feof(in) puis compte sans condition ajoute un passage supplémentaire à la fin.

Pour le texte "hi\nyo\n", six lectures réussies donnent six caractères et deux caractères de fin de ligne. Compter uniquement les caractères de fin de ligne fonctionne pour les lignes qui se terminent par un tel caractère. Le texte "red\ngreen\nblue" contient deux caractères de fin de ligne mais trois lignes, car le dernier "blue" est une ligne à part entière.

Pour inclure une dernière ligne sans caractère de fin de ligne, suis aussi le dernier caractère :

size_t lines = 0;
int c, last = '\n';
while ((c = fgetc(in)) != EOF) {
    if (c == '\n') ++lines;
    last = c;
}
if (last != '\n') ++lines;

Initialiser last à '\n' permet de compter zéro ligne pour un fichier vide. Une erreur de lecture exige toujours un chemin de traitement distinct ; un comptage partiel n’est pas un résultat complet.

Transformer ou examiner le caractère suivant

toupper de <ctype.h> peut modifier les lettres avant leur écriture. Le résultat d’un fgetc réussi se trouve déjà dans la plage des valeurs d’entrée valides pour toupper ; fputc(toupper(c), out) est donc sûr dans la boucle. Les valeurs char signées quelconques doivent être converties en unsigned char avant d’être passées aux fonctions de classification des caractères.

ungetc(c, in) remet un caractère dans le flux pour que la lecture suivante le renvoie à nouveau. La remise d’un caractère est garantie ; vérifie si la fonction renvoie EOF en cas d’échec. Cela permet à un analyseur d’examiner le caractère suivant sans le perdre. Une opération de positionnement annule la remise du caractère.

Le comptage décrit ce que fournit le mode de flux choisi. Sous Windows, le mode texte compte une paire CR LF comme un seul '\n' ; le mode binaire donne accès aux deux octets. Un nombre de caractères obtenu par un flux texte n’est donc pas nécessairement égal au nombre d’octets stockés.

Points clés

  • Conserve les résultats de fgetc dans un int pour distinguer les valeurs des octets de EOF.
  • Teste chaque résultat de lecture avant de le traiter ou de le compter.
  • Compte comme une ligne un dernier morceau non vide sans caractère de fin de ligne.
  • La remise d’un caractère avec ungetc est garantie ; un positionnement l’annule.

2.2 Écrire et lire du texte formaté avec fprintf et fscanf

Les entrées-sorties formatées convertissent les valeurs C en texte lisible et inversement. fprintf écrit selon une présentation que tu choisis ; fscanf lit des valeurs qui correspondent à un format. Leurs spécifications de conversion se ressemblent, mais l’entrée exige des pointeurs vers des destinations de types adaptés.

Écrire des valeurs lisibles

Utilise %d pour un int, %ld pour un long, %zu pour un size_t, %s pour une chaîne et %c pour un caractère. En sortie, %f affiche un double ; un argument float est promu en double. %% affiche un signe pourcentage.

int result = fprintf(fp, "%-8s%6.2f\n", "Oslo", 3.14159);
if (result < 0) {
    /* Handle a write failure. */
}

%-8s aligne "Oslo" à gauche sur huit colonnes ; %6.2f aligne "3.14" à droite sur six colonnes. La ligne obtenue est "Oslo 3.14\n" et la valeur renvoyée est de 15 caractères. La largeur de champ est un minimum, et non une limite de longueur de sortie. La précision modifie aussi l’information stockée : %.2f écrit 3.14159 sous la forme "3.14" ; les chiffres supplémentaires ne peuvent donc pas être récupérés depuis le fichier.

Une fonction simple d’écriture CSV peut produire "Lima,2024,19.5" avec un ordre fixe et des virgules entre les champs. Le texte qui contient des virgules, des guillemets ou des caractères de fin de ligne doit être mis entre guillemets selon les règles CSV. Place un tel champ entre guillemets doubles et double tout guillemet à l’intérieur. Une fonction qui écrit avec un simple %s ne convient que si les champs texte ne contiennent aucun de ces caractères.

Formater dans un tableau de capacité limitée

sprintf n’a pas d’argument indiquant la taille de la destination. Formater "007.jpg" exige huit octets, caractère nul de fin de chaîne compris. Préfère 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. */
}

La valeur non négative renvoyée est la longueur nécessaire au texte complet, sans compter le caractère nul de fin de chaîne. Une valeur égale à la taille du tampon indique déjà une troncature. Vérifie que le résultat n’est pas négatif avant de le convertir en size_t.

Lire le nombre de valeurs attendu
char name[20];
int age;
int matched = fscanf(fp, "%19s %d", name, &age);

Cet appel ne réussit complètement que s’il renvoie 2. La largeur 19 laisse un octet pour le caractère nul de fin de chaîne. Le nom du tableau fournit son adresse ; l’entier exige &age. En entrée, %f exige un float *, tandis que %lf exige un double *. Une incompatibilité constitue un comportement indéfini et non une conversion sans conséquence.

La plupart des conversions ignorent les caractères blancs qui précèdent les données, y compris les fins de ligne. fscanf n’impose donc pas un enregistrement par ligne, et %s ne peut pas lire "New York" comme une seule valeur. Un ensemble de caractères à lire comme %19[^,] lit jusqu’à une virgule ; lire une ligne entière est souvent plus clair pour les enregistrements.

Avec "10 abc 20", %d stocke d’abord 10 et renvoie 1. L’appel suivant renvoie 0, car "abc" ne correspond pas ; ce texte reste non lu. Tester != EOF répète l’échec indéfiniment. Teste == 1, puis traite les données invalides séparément d’une fin de l’entrée ou d’une erreur de flux. Un résultat de conversion est un nombre de valeurs affectées, pas une valeur de vérité pour tout le fichier.

Points clés

  • Fais correspondre chaque spécification de conversion au type de l’argument.
  • Limite la largeur de %s en entrée à la taille du tableau moins un.
  • Compare le résultat de fscanf au nombre exact d’affectations attendues.
  • snprintf renvoie la longueur complète nécessaire ; une valeur au moins égale à la capacité indique une troncature.
  • Les champs texte CSV doivent être mis entre guillemets lorsqu’ils contiennent des virgules, des guillemets ou des caractères de fin de ligne.
  • Une sortie formatée de nombres à virgule flottante peut perdre en précision.

2.3 Lire des lignes en toute sécurité avec fgets

Lire une ligne complète avant de l’analyser permet de garder les enregistrements texte séparés. Si un enregistrement est invalide, tu peux signaler son numéro de ligne et passer au suivant. fgets limite la quantité de données écrites, mais tu dois quand même gérer les caractères de fin de ligne conservés et les lignes plus longues que le tableau.

Lire dans un tableau de capacité connue
char line[256];
while (fgets(line, sizeof line, fp) != NULL) {
    /* Process this successful read. */
}
if (ferror(fp)) {
    /* Report an interrupted read. */
}

fgets lit au plus un caractère de moins que la capacité, s’arrête après un caractère de fin de ligne s’il en rencontre un et ajoute un caractère nul de fin de chaîne. Il renvoie le tampon si la lecture réussit. Un résultat NULL signifie soit qu’aucun caractère n’était disponible en fin de fichier, soit qu’une erreur s’est produite ; ne traite pas le tampon après une erreur de lecture.

sizeof line fournit la capacité du tableau dans cette portée. Dans une fonction auxiliaire dont le paramètre est char *line, il donne la taille du pointeur. Passe séparément la capacité réelle. L’ancienne fonction gets ne peut pas limiter l’entrée et a été retirée en C11.

Reconnaître les morceaux de lignes longues

Avec char buf[8], "temperature\n" arrive sous la forme "tempera", puis "ture\n". Le premier appel utilise sept octets pour les caractères et un pour le caractère nul de fin de chaîne. Une lecture réussie sans caractère de fin de ligne peut correspondre à un morceau de ligne longue ou au dernier morceau non terminé en fin de fichier.

Choisis une méthode : réunir les morceaux dans un enregistrement plus grand, les traiter comme une suite continue, ou rejeter l’enregistrement et ignorer tous les morceaux restants. Si une limite de taille est fixée, examine le caractère suivant avant de rejeter un tampon exactement rempli. Un caractère de fin de ligne suivant complète l’enregistrement ; EOF sans erreur de flux complète une dernière ligne non terminée ; un autre caractère indique que le contenu se poursuit. Ne remets un caractère dans le flux que si le code qui suit doit le relire.

Ne supprimer qu’une fin de ligne effectivement trouvée

Après avoir déterminé si l’enregistrement est complet, supprime les caractères de fin de ligne :

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

strcspn de <string.h> trouve le premier caractère indiqué, ou le caractère nul de fin de chaîne existant si aucun des deux n’est présent. Cela traite aussi bien CR LF conservé que LF. Supprimer systématiquement line[strlen(line) - 1] peut retirer une donnée utile d’une dernière ligne sans caractère de fin de ligne ; cette opération est aussi invalide pour une chaîne vide.

fputs(line, out) écrit la chaîne sans ajouter de caractère de fin de ligne ; puts(line) écrit dans stdout et en ajoute un. Une copie en mode texte peut passer chaque morceau lu avec succès par fgets à fputs, en vérifiant l’absence d’échec, même lorsqu’une ligne longue s’étend sur plusieurs morceaux. Pour obtenir exactement les octets d’origine, utilise plutôt des entrées-sorties binaires par blocs.

Analyser et valider séparément

sscanf(line, "%d %9s", &qty, item) renvoie 2 pour "7 apples" et 0 pour "seven apples". Une analyse échouée ne laisse aucun jeton incorrect bloqué dans le flux d’entrée, car la ligne a déjà été lue. Les conversions numériques avec sscanf exigent toutefois une entrée représentable ; utilise strtol pour vérifier strictement la plage de valeurs.

Pour strtol, remets errno à zéro, passe un pointeur de fin de conversion et vérifie l’absence de chiffres, ERANGE et tout texte inattendu à la fin. Avec "42abc", le résultat 42 n’est qu’un préfixe numérique. Pour un champ entier, autorise les caractères blancs de fin choisis, puis exige la fin de la chaîne. Vérifie la plage du type de destination avant de convertir un long valide en int.

Points clés

  • fgets conserve un caractère de fin de ligne s’il tient dans le tampon et réserve toujours de la place pour un caractère nul de fin de chaîne en cas de réussite.
  • Un tampon plein sans caractère de fin de ligne exige une vérification pour savoir si la ligne est complète.
  • Passe explicitement les capacités aux fonctions auxiliaires qui utilisent des pointeurs.
  • Utilise strcspn pour ne supprimer qu’une fin de ligne présente.
  • Lis d’abord chaque enregistrement, puis analyse-le et valide-le.
  • Avec strtol, vérifie la présence de chiffres, les erreurs de dépassement de plage et les données restantes.
3

Stocker et charger des données dans des fichiers binaires

Les fichiers binaires transfèrent les octets des objets sans les convertir en texte lisible. La taille et le nombre d’éléments déterminent comment sont traités les tableaux complets, les enregistrements et les derniers blocs incomplets. Un fichier partagé entre plusieurs machines exige aussi une représentation explicite : largeur des champs, ordre des octets et disposition. La lecture d’une signature n’est que le début de la vérification de cette représentation ; les longueurs et les nombres doivent être validés avant que le programme ne se fie aux données restantes ou n’alloue de l’espace pour elles.

3.1 Lire et écrire des données binaires avec fread et fwrite

Les entrées-sorties binaires transfèrent les octets des objets sans les convertir en nombres ou en mots lisibles. fread et fwrite reçoivent une adresse, une taille d’élément, un nombre d’éléments et un flux. Leurs valeurs de retour comptent les éléments complets : le choix de la taille d’un élément détermine donc ce que le résultat permet de savoir.

Écrire et lire des tableaux
int a[5] = {1, 2, 3, 4, 5};
if (fwrite(a, sizeof a[0], 5, out) != 5) {
    /* Handle a write failure. */
}

Sur un système où int occupe quatre octets, cette opération écrit 20 octets et renvoie 5. Une lecture dans un autre tableau de cinq éléments ayant la même représentation peut restituer les valeurs. Utilise "wb" et "rb" pour qu’aucune traduction de texte ne puisse modifier les octets.

Pour un seul objet, passe son adresse : fwrite(&rec, sizeof rec, 1, out). Pour des objets accessibles par un pointeur p, utilise fwrite(p, sizeof *p, count, out). sizeof p mesure le pointeur et &p pointe vers la variable pointeur elle-même. Aucun des deux ne désigne le transfert de données voulu. Lorsque la taille ou le nombre d’éléments vaut zéro, les deux fonctions renvoient zéro sans transférer de données.

Copier exactement les octets lus
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;
    }
}

Une taille d’élément de 1 fait de n un nombre d’octets. Une entrée de 1 300 octets fournit 512, 512 et 276 octets, puis zéro. La dernière écriture doit utiliser 276, et non la capacité de 512. Écrire tout le tampon ajoute 236 octets restants des lectures précédentes.

Avec fread(buf, 512, 1, in), la même entrée renvoie 1, 1, puis 0. Les 276 derniers octets ne forment qu’un élément incomplet ; ils ne sont pas comptés et la valeur de cet élément incomplet est indéterminée. Choisis des éléments d’un octet lorsque le dernier bloc incomplet est utile. Choisis des éléments de la taille d’un enregistrement lorsque les enregistrements incomplets doivent être rejetés.

Un résultat de fread inférieur au nombre demandé peut indiquer une fin de fichier ou une erreur. Vérifie ferror après la copie et vérifie fclose sur la sortie : une écriture acceptée dans un tampon peut échouer plus tard. Un en-tête ou un enregistrement incomplet ne doit jamais être traité comme un objet complet.

Examiner les signatures et les objets stockés

Utilise unsigned char pour les octets bruts ; sur les systèmes dont les octets comptent huit bits, ce type contient les valeurs de 0 à 255 sans valeurs négatives. Les données JPEG commencent par 0xFF 0xD8. Les photos d’appareils photo et de téléphones se poursuivent souvent par un marqueur APPn, ce qui produit un motif de récupération allant de FF D8 FF E0 à FF D8 FF EF. Ce motif permet de repérer des fichiers candidats ; valide le format avant de t’y fier. Certains fichiers JPEG utilisent un autre segment ensuite.

Une structure contenant char name[20] et un int score occupe généralement 24 octets. Une écriture brute comprend tous ses octets stockés, y compris le bourrage. Un membre pointeur enregistre une adresse, et non la chaîne pointée. Utilise un tableau intégré ou écris séparément la longueur et les caractères de la chaîne.

Le texte lisible peut occuper plus d’espace : "1000000\n" utilise huit octets sous Linux et neuf en mode texte sous Windows. Un entier de 32 bits encodé explicitement utilise quatre octets de huit bits. Le stockage brut d’objets est compact, mais lie le fichier à la représentation utilisée par le programme qui l’a produit.

Points clés

  • fread et fwrite renvoient un nombre d’éléments complets, qui n’est pas toujours un nombre d’octets.
  • Utilise une taille d’élément de 1 lorsque le dernier bloc incomplet est utile.
  • Écris le nombre d’octets lus, pas toute la capacité du tampon.
  • Un membre pointeur stocké à l’état brut contient une adresse, pas les données pointées.
  • Vérifie les transferts incomplets et la fermeture de la sortie.
  • Utilise les signatures pour repérer des fichiers candidats, puis valide le fichier.

3.2 Concevoir des formats binaires utilisables sur différentes machines

Un flux binaire préserve les octets, mais deux machines peuvent interpréter les mêmes octets différemment. Un format portable définit le sens de chaque octet au lieu d’adopter la disposition que le compilateur donne à un objet.

Préciser les tailles, l’ordre et la disposition

Trois éléments rendent une copie brute d’objet dépendante de la machine :

ÉlémentCe qui peut différer
Taille des typesint peut occuper deux ou quatre octets ; long diffère généralement entre Windows et Linux 64 bits
Ordre des octetsL’octet de poids faible ou celui de poids fort peut venir en premier
Disposition des structuresLe bourrage et les décalages des membres peuvent changer

L’ordre petit-boutiste place l’octet de poids faible en premier. L’ordre gros-boutiste place l’octet de poids fort en premier. La valeur 258, soit 0x00000102 en hexadécimal, est représentée par 02 01 00 00 en ordre petit-boutiste et par 00 00 01 02 en ordre gros-boutiste.

Une disposition courante pour une structure contenant un char suivi d’un int occupe huit octets : un octet pour le caractère, trois octets de bourrage et quatre pour l’entier. Les valeurs des octets de bourrage ne sont pas spécifiées. Les types entiers de largeur fixe de <stdint.h>, comme uint32_t, fixent le nombre de bits lorsqu’ils sont disponibles ; ils ne fixent pas l’ordre des octets et ne suppriment pas le bourrage. Les exemples de formats de fichiers courants supposent des octets de huit bits et la disponibilité de ces types.

Décoder explicitement les champs

Lis entièrement quatre octets avant de les interpréter. Pour un champ petit-boutiste :

uint32_t v = (uint32_t)b[0]
           | (uint32_t)b[1] << 8
           | (uint32_t)b[2] << 16
           | (uint32_t)b[3] << 24;

Ici, b est un tableau d’octets non signés. Chaque conversion explicite a lieu avant le décalage, de sorte que le calcul utilise la largeur non signée nécessaire. Pour encoder la valeur, extrais les huit bits de poids faible, puis les huit suivants, et ainsi de suite. Vérifie chaque opération de sortie. Écrire directement v avec fwrite réintroduirait l’ordre des octets de la machine dans le format.

Définir un petit format complet

Un format de scores peut utiliser "SCR1" comme signature de quatre octets, un nombre d’enregistrements petit-boutiste de quatre octets, puis des enregistrements composés de 20 octets pour le nom et de quatre octets petit-boutistes pour le score. La signature indique aussi la version. L’en-tête mesure huit octets ; chaque enregistrement mesure exactement 24 octets.

Cinq enregistrements occupent 128 octets. L’enregistrement d’indice 3 commence à l’octet 80 : il faut sauter les huit octets de l’en-tête et trois enregistrements complets. Dans un format réel, précise l’encodage du champ de nom et la convention de terminaison ou de remplissage ; un champ de nom fixe de 20 octets n’est pas automatiquement une chaîne C.

Vérifie la longueur complète et la signature d’un en-tête avant d’utiliser ses champs. Vérifie ensuite les nombres et les longueurs par rapport à des limites raisonnables et aux données disponibles avant d’allouer de la mémoire. Un nombre plausible ne suffit pas si des enregistrements ultérieurs manquent.

Lire la structure réelle du format

Certains fichiers PCM WAV ont un en-tête simple de 44 octets, avec la fréquence d’échantillonnage aux octets 24 à 27. Les octets 44 AC 00 00 se décodent en 44 100 échantillons par seconde. D’autres fichiers WAV contiennent des blocs supplémentaires : un lecteur général suit donc les identifiants et les tailles des blocs pour trouver le bloc fmt . Supposer que tous les fichiers ont la même disposition d’en-tête fixe peut conduire à lire un champ sans rapport comme fréquence.

Un format texte comme CSV évite les problèmes de taille des entiers, de boutisme et de bourrage des structures et se vérifie facilement. Il exige néanmoins une disposition des champs définie, des règles de mise entre guillemets et une analyse vérifiée. Choisis-le lorsque la lisibilité et l’échange comptent plus qu’une représentation binaire compacte.

Points clés

  • Un format binaire portable définit la largeur, l’ordre des octets et le décalage de chaque champ.
  • Les types entiers de largeur fixe ne suffisent pas à rendre portable une copie brute de structure.
  • Convertis explicitement les octets vers la largeur non signée voulue avant le décalage de bits.
  • Valide l’en-tête entier et limite les nombres avant de t’y fier.
  • Suis la structure des blocs lorsqu’un format autorise des blocs supplémentaires.
  • Un champ texte de largeur fixe exige un encodage et une convention de longueur explicites.
4

Se déplacer à une position quelconque dans un fichier

Les positions dans un fichier rendent l’accès direct possible lorsque l’emplacement des enregistrements est connu. Pour calculer ces emplacements, il faut tenir compte des en-têtes, de la largeur des enregistrements et des indices commençant à zéro. Les positions dans les flux texte suivent des règles plus strictes, car la traduction peut modifier leur sens. Lorsqu’un flux permet à la fois la lecture et l’écriture, le positionnement contrôle aussi la validité du passage de l’une à l’autre. Mettre à jour un enregistrement combine donc un calcul d’emplacement et une suite d’opérations vérifiées qui préserve les données environnantes.

4.1 Obtenir et modifier la position dans le fichier avec ftell, fseek et rewind

Les lectures séquentielles parcourent les données dans l’ordre. L’accès direct permet à un programme d’atteindre un emplacement connu, comme un enregistrement dans un fichier volumineux. Le positionnement ne fonctionne que si le flux sous-jacent le prend en charge, et le sens d’une position diffère entre les flux binaires et les flux texte.

Obtenir et modifier la position

ftell(fp) renvoie une position de type long ou -1L en cas d’échec. Pour un fichier binaire, une position compte les octets depuis son début. fseek(fp, offset, whence) déplace la position par rapport à un point de référence choisi et renvoie 0 si la fonction réussit, une valeur non nulle en cas d’échec.

RéférenceSens du décalage
SEEK_SETDepuis le début
SEEK_CURDepuis la position courante
SEEK_ENDDepuis la fin

Par exemple, fseek(fp, -4L, SEEK_END) demande à se placer aux quatre derniers octets d’un fichier binaire sur un système qui prend en charge les déplacements relatifs à la fin. Vérifie le résultat avant de lire. Une demande sur un tube ou un terminal, ou une demande de position avant le début d’un fichier ordinaire, peut échouer.

Calculer l’emplacement d’un enregistrement

Pour des enregistrements binaires de taille fixe sans en-tête, l’indice commençant à zéro multiplié par la largeur d’un enregistrement donne son décalage de départ. Avec des enregistrements de 24 octets, l’indice 3 commence à l’octet 72. Après la lecture de cet enregistrement entier, la position est 96.

long offset = 3L * (long)sizeof(struct record);
if (fseek(fp, offset, SEEK_SET) != 0) {
    /* Handle the failed move before reading. */
}

Cet exemple suppose que le fichier a été écrit avec la même disposition de structure. Un format portable spécifié utilise sa largeur d’enregistrement définie. Tiens compte des octets d’en-tête lorsqu’ils sont présents. Le troisième enregistrement compté à partir de 1 a l’indice 2, et non 3. Pour les grands indices, valide la multiplication et assure-toi que le résultat est représentable dans un long avant le déplacement.

Sur les systèmes courants, se déplacer à la fin puis utiliser ftell permet de mesurer un fichier binaire : un fichier de 120 octets avec des enregistrements de 24 octets contient cinq enregistrements. Vérifie les deux appels et restaure ensuite la position. Le C standard n’exige pas que SEEK_END fonctionne de manière pertinente sur tous les flux binaires ; la méthode dépend donc de la prise en charge par le flux concerné.

Enregistrer les positions dans les flux texte sans calcul

Le mode texte peut traduire les fins de ligne. Une position renvoyée pour un flux texte sert à revenir à un endroit ; ce n’est pas un nombre auquel ajouter ou soustraire des valeurs. Un déplacement non nul dans un flux texte utilise une valeur renvoyée auparavant par ftell pour le même fichier, avec SEEK_SET. Un décalage nul est autorisé avec n’importe quel point de référence.

rewind(fp) revient au début et réinitialise les indicateurs de fin de fichier et d’erreur. La fonction ne renvoie aucun résultat ; utilise donc fseek(fp, 0L, SEEK_SET) et vérifie son résultat lorsque la réussite du déplacement doit être confirmée. Un fseek réussi réinitialise l’indicateur de fin de fichier et annule la remise de caractère par ungetc, mais laisse activé un indicateur d’erreur existant.

Conserver un jeton de position

fgetpos(fp, &pos) enregistre une position dans un fpos_t ; fsetpos(fp, &pos) la restaure. Vérifie si chaque fonction renvoie une valeur non nulle indiquant un échec. Traite fpos_t comme un jeton opaque, sans effectuer de calcul dessus. Il peut contenir des informations de position qui dépassent un simple nombre d’octets.

fseek et ftell utilisent long, qui est généralement sur 32 bits sous Windows et limite les positions représentables à environ 2 GiB. Un jeton de position évite de lier l’interface à long, mais la taille de fichier prise en charge dépend toujours de l’implémentation et de la configuration.

Points clés

  • Vérifie si fseek renvoie une valeur non nulle et si ftell renvoie -1L.
  • Le décalage d’un enregistrement tient compte de l’en-tête et de tous les enregistrements précédents.
  • Utilise systématiquement des indices commençant à zéro.
  • Restaure les positions dans les flux texte avec des jetons enregistrés, sans calcul de décalage.
  • rewind réinitialise les deux indicateurs ; un fseek réussi réinitialise celui de fin de fichier.
  • Le positionnement exige un flux qui le prend en charge et des décalages représentables.

4.2 Mettre à jour en place des enregistrements de taille fixe

Un flux en lecture et écriture lit et écrit dans le même fichier. Utilise "r+b" pour préserver un fichier binaire existant tout en modifiant ses enregistrements. "w+b" efface immédiatement l’ancien fichier et les modes ajout imposent chaque écriture à la fin.

Rendre valides les passages entre lecture et écriture

Un flux en lecture et écriture suit deux règles de passage :

PassageAction nécessaire entre les opérations
De l’écriture à la lectureVider le tampon de sortie ou effectuer une opération de positionnement
De la lecture à l’écritureEffectuer une opération de positionnement, sauf si la lecture a rencontré la fin du fichier

Les fonctions de positionnement comprennent fseek, fsetpos et rewind. Vérifie les fonctions qui renvoient un résultat avant de continuer. Un passage sans l’action nécessaire constitue un comportement indéfini ; tu ne peux pas prévoir qu’il affectera seulement l’enregistrement suivant.

Mettre à jour un enregistrement complet

Supposons qu’un fichier sans en-tête contienne cinq enregistrements de 24 octets dans la même représentation que struct record. Pour modifier l’indice 3 :

  1. Ouvre avec "r+b" et vérifie le pointeur.
  2. Déplace-toi à l’octet 72 et vérifie que l’opération a réussi.
  3. Lis exactement un enregistrement complet.
  4. Fixe son membre score à 99.
  5. Reviens au début de l’enregistrement.
  6. Écris exactement un enregistrement et vérifie le résultat de la fermeture.

Les principales opérations intermédiaires sont :

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;
}

La fonction englobante fournit une étiquette cleanup qui ferme son flux et renvoie un échec sur ces branches. Après la lecture, la position est 96. Reculer de 24 octets sélectionne l’enregistrement d’origine et permet le passage de la lecture à l’écriture. Un remplacement réussi laisse le fichier à 120 octets avec cinq enregistrements. Restaurer une position de départ enregistrée est une autre manière valide d’effectuer la mise à jour.

Décider quand réécrire

Ajouter un enregistrement de 24 octets fait passer ce fichier à 144 octets. Se placer au début d’un flux "ab" ou "a+b" ne permet pas de transformer l’écriture suivante en écrasement : elle a toujours lieu à la fin courante.

Un remplacement en place doit tenir dans l’espace qu’il remplace. Passer de "Bo,7" à "Bo,9" convient. Écrire "Bo,12" sur "Bo,7\n" absorbe le caractère de fin de ligne et rattache le texte suivant à l’enregistrement. Pour agrandir ou réduire des enregistrements, écris un nouveau fichier corrigé. Le C standard ne possède pas de fonction de flux qui retire une plage d’octets du milieu d’un fichier. Pour supprimer un enregistrement, on peut aussi copier tous les enregistrements conservés ou marquer comme supprimé un enregistrement de taille fixe.

Préserver la possibilité de récupération lors du remplacement

Vérifie remove et rename ; les deux fonctions renvoient 0 si elles réussissent. Le fait que rename remplace ou non une destination existante est défini par l’implémentation. Linux et macOS prennent en charge le remplacement ; la bibliothèque C de Microsoft échoue lorsque la destination existe.

Une procédure de récupération portable écrit et ferme le nouveau fichier, renomme l’original sous un nom de sauvegarde distinct, puis renomme le nouveau fichier avec le nom d’origine. Si la mise en place échoue, conserve la sauvegarde pour pouvoir la restaurer et indique où elle se trouve. Ne supprime la sauvegarde qu’après la réussite de la mise en place. Cette suite d’opérations permet la récupération ; elle ne constitue ni une transaction atomique portable ni une garantie contre une coupure de courant.

Points clés

  • Utilise "r+b" pour mettre à jour un fichier existant sans le tronquer.
  • Respecte la règle de passage entre lecture et écriture même lorsque la position voulue semble évidente.
  • Reviens au début d’un enregistrement après l’avoir lu et avant de le remplacer.
  • Les écritures en mode ajout ont toujours lieu à la fin.
  • Réécris les données lorsque leur longueur change.
  • Vérifie chaque étape du remplacement et conserve un original récupérable.
5

Détecter et signaler les erreurs de fichiers, puis rétablir le fonctionnement

Une opération sur un fichier peut échouer après une ouverture réussie, et une sortie mise en tampon peut échouer seulement à la fermeture. Les programmes fiables lisent les valeurs de retour, distinguent les données invalides des problèmes d’accès, préservent les informations d’erreur utiles et libèrent les ressources sur tous les chemins. Ces tâches vont ensemble : le diagnostic doit expliquer l’échec initial, tandis que le code de retour final doit aussi refléter les échecs détectés pendant la libération des ressources. Un programme de copie complet montre comment ces décisions s’intègrent au déroulement habituel d’un programme C.

5.1 Vérifier chaque résultat et distinguer la fin de fichier des erreurs

Un fichier peut s’ouvrir correctement et subir malgré tout un échec pendant la lecture, l’écriture, le positionnement ou la fermeture. Un programme robuste vérifie le résultat de l’opération réellement tentée. Il distingue une lecture achevée d’une entrée interrompue et des données mal formées d’un flux défaillant.

Connaître chaque signal d’échec
OpérationSignal à traiter
fopen, fgetsNULL
fgetc, fputc, fflush, fcloseEOF
fprintfUn résultat négatif
fputsEOF
fread, fwriteMoins d’éléments complets que demandé
fscanfMoins d’affectations que prévu, ou EOF
fseek, fgetpos, fsetposUn résultat non nul
ftell-1L

rewind, clearerr et perror ne renvoient aucune valeur. Si la réussite d’un retour au début doit être vérifiée, utilise fseek au lieu de compter sur un résultat de rewind.

Examiner le flux après l’arrêt d’une lecture

Le flux conserve des indicateurs distincts de fin de fichier et d’erreur. feof(fp) teste le premier ; ferror(fp) teste le second. Chaque fonction renvoie une valeur non nulle si son indicateur est activé.

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. */
}

La valeur renvoyée par fgets ne suffit pas à distinguer les deux causes d’arrêt. De même, fgetc renvoie EOF et fread renvoie un nombre inférieur à celui demandé aussi bien en fin de fichier qu’en cas d’échec de lecture. Ne teste pas feof avant la lecture pour prédire s’il reste des données.

Les indicateurs restent activés. clearerr les réinitialise tous les deux, tout comme rewind. Un fseek ou un fsetpos réussi réinitialise l’indicateur de fin de fichier, mais pas un indicateur d’erreur existant. Réinitialiser un indicateur revient à en prendre acte ; cela ne reconnecte pas un périphérique retiré et ne répare pas un fichier endommagé. Ne réessaie qu’après avoir traité la cause.

Distinguer les données invalides d’un échec d’accès

Si fscanf(fp, "%d", &value) renvoie 0 sur "abc", la conversion a échoué avant toute affectation. Cela n’exige ni fin de fichier ni erreur de lecture. Signale un champ invalide, de préférence avec un numéro de ligne. Un message concernant le disque décrirait le mauvais problème.

Si un en-tête obligatoire est lu comme un seul élément et que fread renvoie 0, l’en-tête est incomplet. N’examine pas cet objet partiellement rempli. Si ferror est activé, signale une erreur d’entrée ; si le fichier s’est terminé, signale des données tronquées. Un fichier vide et un en-tête incomplet peuvent produire le même nombre d’éléments : c’est donc le format attendu qui impose la présence de l’en-tête.

Détecter les échecs tardifs en sortie

Un disque peut se remplir après l’ouverture de la sortie par le programme. Les écritures mises en tampon peuvent sembler réussir jusqu’à ce que fflush ou fclose transfère réellement les octets en attente. Vérifie la fermeture finale et fais en sorte qu’un échec à cette étape transforme en échec une tâche qui aurait autrement réussi. Ne ferme pas à nouveau le même flux ensuite.

Dans un terminal, Ctrl+D au début d’une ligne sous Linux ou macOS, ou Ctrl+Z puis Entrée sous Windows, signale généralement la fin de l’entrée. clearerr(stdin) réinitialise l’indicateur obtenu si le programme doit faire une nouvelle tentative. L’action dans le terminal et une erreur de lecture restent des causes différentes.

Points clés

  • Vérifie chaque fonction selon sa propre valeur de retour documentée.
  • Lis d’abord, puis utilise les indicateurs du flux pour déterminer la raison de l’arrêt.
  • Une incompatibilité de format indique des données invalides, pas nécessairement une erreur de flux.
  • Rejette les en-têtes et les enregistrements obligatoires incomplets.
  • Une erreur tardive à la fermeture de la sortie doit entraîner l’échec de la tâche.

5.2 Expliquer les échecs avec errno, perror et strerror

Une valeur de retour t’indique qu’une opération a échoué. Un code d’erreur peut aider à comprendre pourquoi. Utilise errno uniquement dans le contexte du comportement documenté de l’opération et préserve sa valeur avant que des appels sans rapport à la bibliothèque puissent la modifier.

Laisser l’opération établir l’échec

errno est une expression int modifiable de <errno.h>, dont la durée de stockage est propre à chaque fil d’exécution en C11. Sa valeur initiale est zéro. Les appels à la bibliothèque ne la remettent pas à zéro et un appel réussi peut laisser ou affecter une valeur non nulle. Tester errno après un fopen réussi ne permet donc pas de conclure que l’ouverture a échoué.

Sur les systèmes POSIX, un fopen échoué affecte un code d’erreur. La bibliothèque C de Microsoft fournit aussi des codes d’erreur de fichier. Le C standard n’exige pas que fopen affecte une valeur à errno. Pour signaler l’erreur de façon portable, on peut le remettre à zéro avant l’ouverture et utiliser un message général si aucun code n’a été fourni :

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;
}

Cet extrait exige aussi <stdio.h>, <stdlib.h> et <string.h>. Enregistrer immédiatement le code empêche les appels ultérieurs de signalement ou de libération des ressources de remplacer la raison d’origine. Un code enregistré reste utile même si le message final est affiché bien plus tard.

Choisir une fonction de message

perror(label) écrit dans stderr la chaîne indiquée, deux-points et une espace, le message d’erreur courant, puis un caractère de fin de ligne. Un message typique pour un fichier absent est "missing.txt: No such file or directory" ; sa formulation exacte dépend du système.

strerror(code) renvoie le message de la bibliothèque correspondant au code fourni ; il convient donc à un diagnostic personnalisé qui indique le programme, l’opération et le fichier. Ne modifie pas la chaîne renvoyée. Utilise strerror(saved) après la libération des ressources au lieu de demander la nouvelle valeur de errno.

Le C définit EDOM, ERANGE et EILSEQ. Les codes de fichier courants comme ENOENT, EACCES, EEXIST et ENOSPC sont fournis par POSIX et par des bibliothèques C largement disponibles. Adapte ton code à l’interface et à la plateforme visées. Ne suppose pas que les valeurs numériques des codes ou le texte anglais exact des messages sont universels.

Gérer une valeur de retour ambiguë

Pour certaines fonctions, le résultat seul ne permet pas de distinguer une réussite d’une erreur de dépassement de plage. strtol peut renvoyer LONG_MAX comme nombre valide ou comme résultat d’un débordement. Remets errno à zéro avant la conversion, puis examine le code de dépassement de plage documenté :

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. */
}

Un diagnostic utile donne à l’utilisateur des éléments pour agir. Indique le fichier et l’action tentée ; conserve la raison donnée par le système lorsqu’elle est disponible. Envoie le diagnostic vers stderr, en laissant stdout disponible pour les données de résultat valides.

Points clés

  • Utilise le résultat de la fonction pour établir l’échec avant d’interpréter errno.
  • Enregistre errno immédiatement avant les appels de signalement ou de libération des ressources.
  • perror utilise la valeur courante de errno ; strerror accepte un code enregistré.
  • Remets errno à zéro avant strtol et vérifie ERANGE.
  • Indique le fichier et l’action tentée dans un diagnostic.

5.3 Libérer les ressources et indiquer le résultat de l’opération

La gestion des erreurs n’est complète que lorsque les ressources sont libérées et que le programme indique le résultat réel. Prévois la propriété des ressources lorsque tu ouvres un flux et fais en sorte que tous les chemins ultérieurs respectent cette répartition.

Associer une fermeture à chaque ouverture

Si l’ouverture de l’entrée échoue, il n’y a aucun flux à fermer. Si l’ouverture de la sortie échoue après la réussite de celle de l’entrée, ferme l’entrée. Si la copie échoue après l’ouverture des deux flux, ferme-les tous les deux, vérifie la fermeture de la sortie et conserve un code de retour indiquant l’échec. Fermer deux fois un pointeur n’apporte aucune protection supplémentaire : cela utilise un flux déjà fermé.

Le code avec retours anticipés ferme généralement les ressources dans l’ordre inverse de leur acquisition. Une étiquette commune de libération des ressources est utile lorsque plusieurs chemins d’échec exigent les mêmes opérations. Initialise les pointeurs à NULL, conserve un code d’échec par défaut et ne fixe la réussite qu’une fois la phase de traitement terminée.

Le programme complet suivant copie des fichiers d’entrée et de sortie distincts en mode binaire. Il utilise des messages généraux, car le C standard n’exige pas que chaque échec d’une fonction de stdio fournisse une valeur dans errno.

#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;
}

La boucle d’entrée ne traite que les octets effectivement lus. Une écriture incomplète entraîne un saut vers la libération des ressources ; une erreur d’entrée est vérifiée après la boucle. La fermeture de la sortie peut encore transformer une réussite en échec. La suppression n’est tentée que si cette exécution a réussi à ouvrir la sortie : un échec d’ouverture ne déclenche donc pas la suppression d’un fichier sans rapport. L’exigence de fichiers distincts compte : "wb" effacerait l’entrée si les deux noms désignaient le même fichier.

Préserver les données existantes importantes

L’ouverture directe de la sortie ne permet pas d’annuler une troncature. Pour remplacer des données importantes, écris d’abord sous un nom temporaire distinct. Vérifie chaque écriture et la fermeture finale avant de renommer les fichiers. Conserve l’original sous un nom de sauvegarde jusqu’à la mise en place du nouveau fichier ; vérifie chaque renommage et chaque suppression. Si la mise en place échoue, indique l’emplacement du fichier récupérable au lieu de le supprimer.

Une fonction auxiliaire doit fermer ce qu’elle a ouvert et renvoyer un résultat de réussite ou d’échec à sa fonction appelante. Celle-ci peut décider de réessayer ou de quitter le programme. Renvoyer EXIT_FAILURE après toute étape incomplète permet à un script de s’arrêter avant une action qui en dépend, comme la suppression de l’original après une sauvegarde.

Points clés

  • Initialise la gestion de la propriété des flux et conserve un code d’échec par défaut.
  • Ferme chaque flux ouvert avec succès exactement une fois sur chaque chemin.
  • Fais en sorte qu’un échec de fermeture modifie le code de retour final.
  • Ne supprime une sortie incomplète que si cette exécution l’a créée ou ouverte.
  • Écris les fichiers de remplacement séparément et préserve l’original jusqu’à la réussite de leur mise en place.
  • Les fonctions auxiliaires signalent les échecs à leurs fonctions appelantes pour qu’elles puissent choisir comment rétablir le fonctionnement.

Entrées et sorties de fichiers en C : exemples

5 Leçons Interactives

1

Ouvrir un flux sans perdre les données existantes

12 étapes
Choisir un mode de fichier, suivre la traduction du texte et relier le même code de traitement aux flux standard.
  1. 1
    IntroductionDéterminer ce qui doit être conservé

    Un programme s’apprête à enregistrer des résultats. Avant toute ouverture, détermine si les anciennes données doivent être conservées et si le fichier contient des lignes lisibles ou des octets à préserver exactement. Ces choix déterminent le mode, et un mauvais choix peut effacer les anciennes données.

    Tu vas suivre une écriture depuis le texte du programme jusqu’au flux (le canal de la bibliothèque vers un fichier ou un terminal), puis choisir où envoyer les résultats normaux et les diagnostics (les flux standard). L’ouverture est déjà une opération sur le fichier : cette décision précède donc la première écriture.

  2. 2
    ExplicationAccéder aux données par un flux

    Un flux est le canal de la bibliothèque standard vers un fichier, un terminal ou un tube. Conserve son pointeur sous forme d’un FILE * de <stdio.h> et passe-le à des fonctions comme fprintf.

    L’objet de la bibliothèque mémorise le tampon, la position et les indicateurs d’état. Tu ne modifies pas ses champs. La position dans un fichier avance à mesure que les données sont traitées ; un terminal ou un tube ne peut pas être traité comme un fichier ordinaire pour le positionnement.

  3. 3
    ExempleSuivre les fins de ligne jusqu’aux octets stockés

    Écrire "a\nb\nc\n" fournit trois lettres et trois caractères de fin de ligne. Sous Linux ou macOS, le fichier texte contient six octets. Sous Windows, chaque caractère de fin de ligne devient CR LF, ce qui donne neuf octets stockés.

    La lecture de ce fichier Windows en mode texte sous Windows restitue six caractères. La lecture en mode binaire donne accès aux neuf octets. Le texte du programme et la suite d’octets stockée sont deux représentations des mêmes lignes.

  4. 4
    LienChoisir un mode pour une image

    Une suite d’octets dans une image compressée peut contenir des valeurs que le mode texte traduit. Ajouter b sélectionne le mode binaire et préserve ces valeurs.

    Cela explique pourquoi un lecteur d’images qui fonctionne avec "r" sous Linux peut échouer sous Windows. Il doit utiliser "rb" parce qu’il traite une suite d’octets définie, et non des lignes que la bibliothèque doit traduire.

  5. 5
    DéfiVérifier un nombre de caractères

    Sous Windows, une écriture en mode texte stocke "m\nn\n". Combien d’octets le fichier obtenu contient-il, et pourquoi ?

    ---reveal---

    Il contient six octets : deux lettres et deux paires CR LF. Les quatre caractères fournis par le programme occupent plus d’espace, car chaque caractère de fin de ligne devient deux octets stockés. En mode binaire, ces quatre mêmes octets fournis seraient écrits sans cette traduction.

  6. 6
    ExplicationChoisir ce qui doit être préservé avant l’accès

    Pour un fichier existant, "r" permet la lecture, "w" efface le contenu et permet l’écriture, et "a" conserve les données et ajoute à la fin. Un + permet à la fois l’entrée et la sortie.

    Utilise "r+" pour préserver un fichier existant lors des mises à jour. "w+" l’efface malgré tout. En C11, "wx" échoue si le fichier existe, ce qui est utile lorsqu’une destination existante doit être protégée.

  7. 7
    ConseilAcquérir et libérer un flux

    Après FILE *fp = fopen(name, "r");, teste fp == NULL avant toute opération sur le flux. En cas d’échec, aucun flux n’est fourni à traiter ou à fermer.

    En cas de réussite, le programme possède un flux qui exige un fclose. Vérifie aussi cette fermeture. Le flux est terminé après l’appel, même si la fermeture échoue ; tenter de le fermer à nouveau utilise un flux qui n’est plus valide.

  8. 8
    DéfiProtéger le journal existant

    Un journal existant doit conserver ses entrées et une nouvelle ligne doit y être ajoutée. Une personne choisit "w" parce que la tâche consiste à écrire. Quel mode faut-il utiliser, et à quel moment "w" détruirait-il les anciennes entrées ?

    ---reveal---

    Utilise "a". Le mode "w" détruit le contenu existant dès que l’ouverture réussit, avant même une écriture. Le choix doit protéger le journal dès l’ouverture, et pas seulement pendant le code de traitement ultérieur.

  9. 9
    ExplicationSéparer les résultats des explications

    Les flux déjà ouverts sont stdin pour l’entrée, stdout pour les résultats et stderr pour les diagnostics. Une fonction qui accepte FILE *out peut écrire la même présentation dans un fichier ou dans stdout.

    Un interpréteur de commandes peut rediriger la sortie standard vers un fichier ou l’envoyer par un tube à un autre programme. Garde les diagnostics sur stderr pour qu’un résultat lisible par une machine ne reçoive pas de messages d’erreur entre ses lignes de données.

  10. 10
    Point cléFaire apparaître le texte mis en tampon

    Les sorties peuvent attendre dans un tampon en mémoire. La sortie standard vers un terminal est généralement mise en tampon par ligne ; la sortie redirigée est généralement entièrement mise en tampon. La sortie d’erreur standard n’est pas entièrement mise en tampon.

    Une invite sans caractère de fin de ligne peut exiger un appel à fflush(stdout) dont le résultat est vérifié avant le début de l’entrée. Vider le tampon n’élimine pas les entrées. Pour ignorer une ligne d’entrée indésirable, lis les caractères jusqu’à sa fin de ligne ou à la fin du fichier.

  11. 11
    DéfiSuivre la sortie d’un filtre

    Un filtre lit l’entrée standard, écrit les résultats sur la sortie standard et écrit les erreurs sur la sortie d’erreur standard. Il est exécuté avec filter < raw.txt > clean.txt. Où un message signalant un enregistrement rejeté doit-il apparaître, et quel code de retour le programme doit-il renvoyer s’il ne peut pas terminer la tâche ?

    ---reveal---

    Le diagnostic va vers la destination d’erreur héritée, généralement le terminal. Renvoie EXIT_FAILURE. Le fichier de résultats reste séparé du message et l’interpréteur de commandes peut détecter l’échec.

  12. 12
    RécapitulatifGérer délibérément le cycle de vie d’un flux

    Avant l’ouverture, choisis ce qui doit être préservé et le mode texte ou binaire. Vérifie que l’ouverture a produit un flux, ne traite les données qu’à l’aide de pointeurs valides et ferme une fois chaque flux dont tu es responsable.

    Les flux standard permettent à l’interpréteur de commandes de fournir les connexions. Sépare les résultats des diagnostics et renvoie un code qui décrit le résultat de la tâche. Ces choix permettent au même petit programme de fonctionner de manière interactive, avec des fichiers ou dans une chaîne de tubes.

2

Lire un enregistrement texte et valider son contenu

15 étapes
Passer de la lecture de caractères aux valeurs formatées et aux lignes complètes, en gérant les frontières qui provoquent des bogues d’entrée courants.

Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.

  1. 1
    IntroductionLire au bon niveau
  2. 2
    ExplicationDistinguer un caractère de EOF
  3. 3
    ExempleNe compter qu’une lecture réussie
  4. 4
    DéfiInclure la dernière ligne non terminée
  5. 5
    ExempleÉcrire selon une présentation contrôlée
  6. 6
    ConseilRéserver le caractère nul de fin de chaîne
  7. 7
    ExplicationUtiliser le nombre d’affectations réussies
  8. 8
    DéfiFormater un nom dans un espace limité
  9. 9
    ExplicationLire une ligne dans une zone de capacité connue
  10. 10
    ExempleSuivre une ligne sur deux lectures
  11. 11
    ConseilSupprimer la fin de ligne sans supprimer de données
  12. 12
    ExplicationAnalyser la ligne déjà lue
  13. 13
    DéfiAccepter une ligne qui tient exactement
  14. 14
    DéfiRejeter un préfixe suivi d’une partie invalide
  15. 15
    RécapitulatifSéparer la lecture de l’interprétation
3

Transférer des octets et définir leur sens

12 étapes
Copier correctement un dernier bloc incomplet, puis décoder des enregistrements selon une disposition binaire explicite.

Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.

  1. 1
    IntroductionDistinguer les octets des valeurs
  2. 2
    ExplicationChoisir ce que compte la valeur de retour
  3. 3
    ExempleSuivre le dernier bloc incomplet
  4. 4
    Point cléDistinguer un enregistrement d’un bloc d’octets
  5. 5
    DéfiUtiliser le nombre effectivement lu
  6. 6
    ExplicationNe pas laisser le compilateur concevoir le format
  7. 7
    ExempleDécoder une valeur petit-boutiste
  8. 8
    ExempleTrouver un enregistrement à partir de sa disposition
  9. 9
    DéfiVérifier la quantité de données déclarée
  10. 10
    LienSuivre la structure autorisée par le format
  11. 11
    DéfiExiger un champ complet avant de le décoder
  12. 12
    RécapitulatifFaire correspondre le transfert et le format
4

Trouver un enregistrement et le remplacer en toute sécurité

13 étapes
Calculer un décalage, conserver une position enregistrée et respecter les règles des flux en lecture et écriture lors du remplacement d’un enregistrement.

Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.

  1. 1
    IntroductionAccéder directement aux données nécessaires
  2. 2
    ExplicationChoisir un point de référence
  3. 3
    ExempleTrouver l’enregistrement d’indice 3
  4. 4
    ConseilRestaurer les positions dans les flux texte à l’aide de jetons
  5. 5
    DéfiTrouver le troisième enregistrement
  6. 6
    ExplicationPréserver le fichier existant
  7. 7
    ExempleRevenir au début de l’enregistrement
  8. 8
    Point cléTerminer le remplacement
  9. 9
    DéfiChoisir à la fois le mode et le déplacement
  10. 10
    LienRéécrire lorsqu’un enregistrement s’allonge
  11. 11
    ConseilPréserver la possibilité de récupération lors de la mise en place
  12. 12
    DéfiInterpréter un ajout effectué
  13. 13
    RécapitulatifLe positionnement fait partie d’une mise à jour valide
5

Prendre en compte l’échec lors du signalement et de la libération des ressources

12 étapes
Diagnostiquer la fin de l’entrée, préserver l’erreur d’origine et faire en sorte que les échecs tardifs de sortie modifient le résultat final.

Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.

  1. 1
    IntroductionLa réussite comprend la fermeture finale
  2. 2
    ExplicationLire le résultat de chaque fonction
  3. 3
    ExplicationExpliquer pourquoi l’entrée s’est arrêtée
  4. 4
    DéfiRejeter un en-tête tronqué
  5. 5
    ExplicationEnregistrer la raison d’origine
  6. 6
    ExempleAfficher un message à partir du code enregistré
  7. 7
    DéfiChoisir la bonne source pour le diagnostic
  8. 8
    ExplicationSuivre les flux acquis
  9. 9
    ExempleLaisser la libération des ressources modifier le résultat
  10. 10
    DéfiDéterminer la prochaine action d’un script
  11. 11
    Point cléConserver l’original après un échec de renommage
  12. 12
    RécapitulatifRenvoyer le résultat de toute la tâche

Entrées et sorties de fichiers en C : QCM

40 Questions Pratiques en 4 séries

Série 1 : Gérer les frontières du texte et les dispositions binaires10 Questions Pratiques

Question 1 sur 10

Un programme est lancé dans /work/run et ouvre "data/items.txt". Quel chemin est résolu ?
  1. /work/data/items.txt
  2. /work/run/items.txt
  3. /work/run/data/items.txt
  4. Le chemin à côté du fichier source
Afficher la réponseMasquer la réponse
Réponse : C
Un chemin relatif part du répertoire de travail courant du programme en cours d’exécution. Ajouter data/items.txt à /work/run donne /work/run/data/items.txt. L’emplacement du fichier source ou de l’exécutable ne détermine pas cette recherche : changer le dossier depuis lequel le programme est lancé peut donc changer le fichier qu’il ouvre.

Question 2 sur 10

Dans la commande producer | consumer > result.txt d’un interpréteur de commandes, où va par défaut la sortie d’erreur standard du producteur ?
  1. Vers l’entrée standard du consommateur
  2. Vers sa destination d’erreur héritée
  3. Vers result.txt avec les résultats
  4. Vers l’entrée standard du producteur
Afficher la réponseMasquer la réponse
Réponse : B
Le tube ne relie que la sortie standard du producteur à l’entrée standard du consommateur. Sa sortie d’erreur standard conserve la destination héritée, généralement le terminal, sauf si elle est redirigée séparément. Rediriger la sortie du consommateur vers result.txt ne redirige pas aussi les diagnostics du producteur.

Question 3 sur 10

Un fichier de 1 540 octets est copié avec un tampon de 600 octets et fread(buf, 1, 600, in). Quelles tailles les écritures réussies doivent-elles utiliser ?
  1. 600, 600, 340
  2. 600, 600, 600
  3. 600, 340, 600
  4. 600, 600, 0
Afficher la réponseMasquer la réponse
Réponse : A
Deux lectures complètes transfèrent 1 200 octets, ce qui laisse 340 octets pour la dernière lecture non vide. Chaque écriture doit utiliser le nombre renvoyé par cette lecture, soit 600, 600 et 340. Écrire les 600 octets complets au dernier passage ajouterait 260 octets qui n’ont pas été fournis par cette dernière lecture.

Question 4 sur 10

char name[8]; reçoit snprintf(name, sizeof name, "part-%d", 2468), qui renvoie 9. Que doit conclure le programme ?
  1. La destination contient maintenant le nom complet.
  2. Le nom de neuf caractères tient dans le tampon.
  3. Neuf octets stockés comprennent le caractère nul de fin de chaîne.
  4. Le résultat a été tronqué et doit être traité.
Afficher la réponseMasquer la réponse
Réponse : D
Le texte complet "part-2468" exige neuf caractères plus un caractère nul de fin de chaîne, ce qui dépasse la capacité de huit octets. snprintf limite ce qu’il stocke et indique la longueur complète nécessaire ; une valeur de retour non négative au moins égale à la capacité signale donc une troncature. Le 9 indiqué n’est pas le nombre de caractères réellement stockés.

Question 5 sur 10

Un outil de validation autorise au plus sept caractères de contenu par ligne. Un fichier sans erreur contient "cedars\nmaplesx". Il appelle à plusieurs reprises fgets(buf, 8, fp). Quelles frontières entre les enregistrements doit-il reconnaître ?
  1. Un enregistrement valide formé en réunissant les deux morceaux.
  2. Un enregistrement valide : « cedars » ; rejeter « maplesx ».
  3. Deux enregistrements valides : « cedars » et « maples ».
  4. Deux enregistrements valides : « cedars » et « maplesx ».
Afficher la réponseMasquer la réponse
Réponse : D
La première lecture contient six lettres et LF : c’est donc une ligne complète valide. La seconde contient exactement sept lettres sans LF ; une lecture supplémentaire confirme une fin de fichier normale, ce qui en fait un dernier enregistrement valide. Rejeter cette seconde lecture uniquement parce que son tampon est plein reviendrait à confondre une dernière ligne qui tient exactement avec un enregistrement trop long.

Question 6 sur 10

Une entrée sans erreur contient "north\nsouth\neast". Combien de lignes et de caractères de fin de ligne contient-elle ?
  1. 3 lignes et 3 caractères de fin de ligne
  2. 2 lignes et 2 caractères de fin de ligne
  3. 3 lignes et 2 caractères de fin de ligne
  4. 2 lignes et 3 caractères de fin de ligne
Afficher la réponseMasquer la réponse
Réponse : C
Il y a deux caractères de fin de ligne explicites et le texte final non vide "east" forme une troisième ligne. Un compteur qui ne s’incrémente que pour les caractères de fin de ligne indiquerait 2 et manquerait cette dernière ligne. Un compteur de lignes robuste ajoute un lorsque le dernier caractère lu avec succès n’est pas un caractère de fin de ligne.

Question 7 sur 10

Deux systèmes fournissent tous deux uint32_t, mais diffèrent par l’ordre des octets et le bourrage des structures. Un programme écrit struct { char kind; uint32_t count; } à l’état brut avec un seul fwrite. Quelle modification rend la représentation indépendante de ces deux différences ?
  1. Encoder séparément les champs définis dans un ordre d’octets choisi.
  2. Remplacer le membre par un simple unsigned int.
  3. Écrire sizeof dans l’en-tête et conserver l’écriture brute.
  4. Conserver la structure et ouvrir le fichier avec "wb".
Afficher la réponseMasquer la réponse
Réponse : A
Encoder les champs séparément donne au fichier une largeur, un ordre et une disposition définis, indépendants de la représentation de la structure. Le mode binaire empêche les traductions de texte, mais écrit toujours sans changement le bourrage et l’ordre des octets du système producteur. Un membre de largeur fixe ne règle que la largeur de l’entier et laisse sans solution les deux différences de la question.

Question 8 sur 10

La position d’un flux texte a été enregistrée par long p = ftell(fp); et vérifiée. Après d’autres lectures, quel appel valide permet d’y revenir ?
  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)
Afficher la réponseMasquer la réponse
Réponse : B
Une position enregistrée dans un flux texte peut être passée sans modification avec SEEK_SET pour le même fichier. La traiter comme une distance en octets depuis l’emplacement courant peut conduire à mal interpréter la valeur enregistrée, surtout lorsqu’une traduction du texte a lieu. Ajouter un à cette valeur crée de même une position calculée que les règles des flux texte n’autorisent pas.

Question 9 sur 10

Un fichier binaire possède un en-tête de 8 octets et cinq enregistrements de 16 octets. Un flux "r+b" se place à l’octet 40 et lit un enregistrement complet sans atteindre EOF. Un champ est modifié. Quelle opération avant l’écriture permet à la fois le passage à l’écriture et la sélection du même enregistrement ?
  1. fseek(fp, -16L, SEEK_CUR), qui revient à l’octet 40.
  2. fseek(fp, 0L, SEEK_CUR), qui reste à l’octet 56.
  3. fseek(fp, 24L, SEEK_SET), qui va à l’octet 24.
  4. fflush(fp), qui laisse la position à l’octet 56.
Afficher la réponseMasquer la réponse
Réponse : A
L’enregistrement d’indice 2 commence après l’en-tête de huit octets et deux enregistrements de 16 octets, à l’octet 40. La lecture avance jusqu’à 56 ; le déplacement relatif de 16 octets en arrière rétablit donc le début et rend valide le passage de l’entrée à la sortie. Un déplacement de distance nulle permet le passage, mais dirige le remplacement vers l’enregistrement suivant.

Question 10 sur 10

Un appel de bibliothèque échoué affecte une valeur à errno. Le programme doit fermer un autre flux avant de signaler la cause d’origine. Que doit-il d’abord préserver ?
  1. La longueur courante du tampon de sortie
  2. Le prochain pointeur de flux disponible
  3. La valeur courante de errno
  4. Le résultat d’un nouvel appel réussi
Afficher la réponseMasquer la réponse
Réponse : C
Enregistre errno immédiatement après l’échec signalé, avant que la libération des ressources puisse le modifier. Le message ultérieur peut utiliser strerror(saved) pour décrire le code d’origine. Lire la valeur courante de errno seulement après la fermeture d’un autre flux peut conduire à signaler l’état de cette opération ultérieure au lieu de l’échec que l’utilisateur doit comprendre.
Série 2 : Choisir les flux et traiter une entrée complète10 Questions Pratiques

Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.

Question 1 sur 10

Quel mode lit un fichier compressé sans traduction de texte ?
  1. "r"
  2. "rb"
  3. "wb"
  4. "ab"
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 2 sur 10

Un programme doit ajouter une entrée à un journal existant tout en conservant les entrées précédentes. Quel mode d’ouverture convient ?
  1. "w"
  2. "r"
  3. "w+"
  4. "a"
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 3 sur 10

fgets réussit à lire la dernière ligne, qui contient "maple" sans caractère de fin de ligne. Que laisse line[strlen(line) - 1] = '\0'; ?
  1. "mapl"
  2. "maple"
  3. "maple\n"
  4. "map"
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 4 sur 10

Sur un système où char est signé et compte huit bits, un fichier binaire commence par l’octet 0xFF et contient d’autres données. Pourquoi char c; while ((c = fgetc(fp)) != EOF) peut-il s’arrêter immédiatement ?
  1. Le mode binaire convertit 0xFF en fin de fichier.
  2. L’affectation compare avant de stocker l’octet.
  3. L’octet peut devenir -1 lorsqu’il est converti en char.
  4. La première lecture réussie active l’indicateur EOF.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 5 sur 10

Étant donné char tag[14]; int count;, quel appel lit sans danger un mot et un entier dans ces objets ?
  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)
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 6 sur 10

Un rapport est redirigé vers un fichier. Quel flux doit transmettre un message expliquant qu’un enregistrement d’entrée est invalide ?
  1. stderr
  2. stdout
  3. stdin
  4. Le flux de sortie du rapport
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 7 sur 10

Un appel fwrite(a, 4, 7, fp) réussi écrit tous les éléments demandés. Que renvoie-t-il ?
  1. 4
  2. 28
  3. 7
  4. 1
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 8 sur 10

Un champ petit-boutiste de 32 bits contient les octets de huit bits 07 02 00 00. Quelle valeur représente-t-il ?
  1. 1794
  2. 7
  3. 131079
  4. 519
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 9 sur 10

Un fichier binaire possède un en-tête de 12 octets et des enregistrements de 20 octets. Après s’être placé à l’octet 12, un programme lit deux enregistrements complets. Quel appel relatif à fseek sélectionne maintenant le quatrième enregistrement compté à partir de 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)
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 10 sur 10

Un en-tête obligatoire de 16 octets est lu avec fread(&h, 16, 1, fp). La fonction renvoie 0, ferror(fp) vaut zéro et feof(fp) est non nul. Comment le lecteur doit-il procéder ?
  1. Rejeter l’en-tête incomplet sans interpréter ses champs.
  2. Interpréter l’en-tête puisqu’aucune erreur de flux n’a eu lieu.
  3. Retenter la lecture de l’en-tête sans changer l’état du flux.
  4. Réinitialiser les indicateurs et accepter les champs mis à zéro.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Série 3 : Mettre à jour des enregistrements et préserver des résultats fiables10 Questions Pratiques

Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.

Question 1 sur 10

Sous Windows avec des octets de huit bits, "x\ny\n" est écrit en mode texte. Combien d’octets sont stockés ?
  1. 4
  2. 6
  3. 8
  4. 5
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 2 sur 10

Un programme C11 doit créer une nouvelle exportation binaire tout en préservant tout fichier existant portant ce nom. Quel mode convient ?
  1. "wb"
  2. "ab"
  3. "r+b"
  4. "wbx"
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 3 sur 10

Un format possède un en-tête de 8 octets et des enregistrements d’exactement 18 octets. Son champ de comptage indique 6 enregistrements, mais le fichier complet ne mesure que 98 octets. Que doit conclure un lecteur avant d’allouer de la mémoire pour ces enregistrements ?
  1. Le fichier ne peut pas fournir les six enregistrements déclarés.
  2. Le nombre est valide puisque l’en-tête est complet.
  3. Le fichier contient six enregistrements après arrondi de la longueur.
  4. Le dernier enregistrement peut être complété par des octets nuls.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 4 sur 10

Une ligne complète "64km" est passée à strtol après la remise à zéro de errno. La valeur renvoyée est 64, errno reste à 0 et end pointe sur k. Le champ doit contenir un entier seul, éventuellement suivi de caractères blancs. Quelle décision convient ?
  1. Accepter puisque errno est resté à zéro.
  2. Accepter après avoir ignoré les deux lettres.
  3. Rejeter car il reste du texte autre que des caractères blancs.
  4. Rejeter car aucun préfixe numérique n’a été trouvé.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 5 sur 10

Un fichier binaire sans erreur contient 27 octets. Deux appels utilisent fread(b, 16, 1, fp) avec un tableau de 16 octets. Que renvoie le second appel et comment son élément peut-il être utilisé ?
  1. 11 ; ses onze octets forment un élément complet.
  2. 1 ; les cinq octets manquants sont remplis de zéros.
  3. 0 ; sa valeur partielle peut servir d’enregistrement suivant.
  4. 0 ; l’élément incomplet ne doit pas être interprété.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 6 sur 10

L’entrée est "18 pear 26". Après un appel réussi à fscanf(fp, "%d", &n), le même appel renvoie deux fois 0. Pourquoi n’a-t-il pas atteint 26 ?
  1. La première lecture a automatiquement consommé les trois champs.
  2. Le mot qui ne correspond pas reste non lu à la conversion suivante.
  3. La deuxième lecture a atteint EOF et supprimé le mot.
  4. La destination entière doit être remplacée par un double.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 7 sur 10

Quelle valeur indique que fseek a échoué ?
  1. Un résultat non nul
  2. Le nouveau décalage en octets
  3. Toujours exactement EOF
  4. Toujours exactement -1L
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 8 sur 10

Un fichier binaire permettant le positionnement contient un en-tête de 10 octets et quatre enregistrements de 14 octets. Un lecteur ouvre un flux en mode "r+b", se place à l’octet 38 et lit un enregistrement complet sans atteindre EOF. Il modifie un champ. Quelle opération sélectionne ensuite le même enregistrement et permet de l’écrire pour le remplacer ?
  1. fflush(fp)
  2. fseek(fp, 0L, SEEK_CUR)
  3. fseek(fp, 38L, SEEK_SET)
  4. fseek(fp, 52L, SEEK_SET)
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 9 sur 10

Toutes les écritures en sortie ont renvoyé les nombres attendus, mais fclose(out) renvoie EOF. Quel résultat le programme doit-il signaler ?
  1. Une réussite, car toutes les données ont atteint le tampon d’écriture.
  2. Une réussite, après un nouvel appel à fclose sur le même flux.
  3. Un échec, uniquement si feof(out) est aussi activé.
  4. Un échec, car la sortie mise en tampon peut être incomplète.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 10 sur 10

L’entrée s’ouvre avec succès, puis l’ouverture de la sortie renvoie NULL. Que doit faire ce chemin avant de renvoyer un échec ?
  1. Fermer l’entrée une fois et signaler l’échec d’ouverture de la sortie.
  2. Fermer les deux pointeurs, y compris le pointeur de sortie nul.
  3. Supprimer le chemin de sortie même si son ouverture a échoué.
  4. Quitter immédiatement en laissant l’entrée ouverte pour la fonction appelante.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Série 4 : Diagnostiquer les échecs dans un traitement complet de fichiers10 Questions Pratiques

Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.

Question 1 sur 10

Un programme lit sous Linux une ligne créée sous Windows et ne supprime que son LF. La comparaison du résultat avec "ready" échoue alors que les lettres visibles correspondent. Quel caractère conservé explique cela ?
  1. Une tabulation avant les lettres
  2. Un CR avant le LF supprimé
  3. Un second caractère nul de fin de chaîne
  4. Un octet EOF stocké par fgets
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 2 sur 10

Un programme doit ajouter sa sortie normale à un fichier nommé totals.txt à l’aide d’un interpréteur de commandes. Quel opérateur de redirection faut-il utiliser ?
  1. < totals.txt
  2. 2> totals.txt
  3. > totals.txt
  4. >> totals.txt
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 3 sur 10

Un fichier contient trois enregistrements de 12 octets avec les scores 10, 20 et 30. En mode "a+b", un programme se place à l’octet 12, lit un enregistrement, remplace son score par 25, revient à l’octet 12 et écrit l’enregistrement. Tous les appels réussissent. Quels scores contiennent ensuite le deuxième et le dernier enregistrement ?
  1. Deuxième : 20 ; dernier : 25 ; quatre enregistrements au total.
  2. Deuxième : 25 ; dernier : 30 ; trois enregistrements au total.
  3. Deuxième : 25 ; dernier : 25 ; quatre enregistrements au total.
  4. Deuxième : 20 ; dernier : 30 ; trois enregistrements au total.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 4 sur 10

Les enregistrements contenant un entier seul peuvent avoir au plus sept caractères de contenu. Avec char buf[8], les lectures renvoient "8642kg\n", puis "1234567" ; la lecture suivante, dont le résultat est vérifié, confirme une fin de fichier normale. strtol trouve 8642 suivi de kg dans le premier enregistrement et convertit tous les chiffres du second, sans erreur de dépassement de plage. Quels enregistrements sont valides ?
  1. Accepter les deux préfixes numériques complets.
  2. Rejeter les deux, trop longs pour le tampon.
  3. Rejeter le premier ; accepter le second.
  4. Accepter seulement le premier, qui se termine par LF.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 5 sur 10

Un fichier de remplacement a été entièrement écrit et fermé. Le renommage de l’original en fichier de sauvegarde réussit ; la mise en place du remplacement échoue parce que sa destination est maintenant occupée. Quelle action préserve l’original sans écraser le nouveau fichier présent à cet emplacement ?
  1. Supprimer la sauvegarde, conserver le remplacement et renvoyer un échec.
  2. Conserver la sauvegarde et le remplacement, signaler l’échec et résoudre le conflit de nom avant de réessayer.
  3. Renommer la sauvegarde vers le nom occupé sans vérifier le résultat.
  4. Supprimer immédiatement la destination occupée, puis réessayer la mise en place.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 6 sur 10

Un fichier sans erreur contient "a\nb". Un programme utilise int c; size_t n = 0, lines = 0; while (c = fgetc(in) != EOF) { ++n; if (c == '\n') ++lines; }. Quels totaux obtient-on, et quelle correction permet de compter correctement les lignes ?
  1. n = 4, lines = 0 ; tester feof avant la lecture et compter la dernière ligne.
  2. n = 3, lines = 1 ; corriger les parenthèses de l’affectation sans traiter la dernière ligne.
  3. n = 3, lines = 0 ; corriger les parenthèses de l’affectation et compter la dernière ligne non terminée.
  4. n = 2, lines = 1 ; conserver la condition et remplacer le type int de c par char.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 7 sur 10

Une lecture avec une taille d’élément de 1 demande un en-tête obligatoire de 12 octets mais obtient 9 octets. feof est activé et ferror est nul. Un fseek réussi revient ensuite au début. Que reste-t-il vrai et que doit faire l’analyseur ?
  1. EOF est réinitialisé ; rejeter l’en-tête incomplet sauf si une nouvelle lecture complète réussit.
  2. EOF reste activé ; réinitialiser les deux indicateurs et décoder les neuf octets.
  3. L’en-tête est devenu complet ; le décoder après une nouvelle vérification de feof.
  4. L’indicateur d’erreur est activé ; signaler un nouvel échec de positionnement.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 8 sur 10

Une ouverture en entrée échoue et sa valeur errno est enregistrée dans open_error. La fermeture d’une sortie ouverte auparavant échoue ensuite et fournit un autre code, enregistré dans close_error. Comment un diagnostic doit-il préserver les deux échecs ?
  1. Décrire les deux opérations avec strerror(close_error) ; le dernier code remplace les causes précédentes.
  2. Décrire l’ouverture avec perror après la libération des ressources et supprimer le diagnostic de fermeture jugé redondant.
  3. Décrire l’erreur d’ouverture enregistrée, mais renvoyer une réussite si la libération des ressources atteint sa dernière instruction.
  4. Décrire l’ouverture avec strerror(open_error) et la fermeture avec strerror(close_error) ; renvoyer un échec.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 9 sur 10

fopen renvoie un flux valide, mais errno contient toujours une ancienne valeur non nulle. Que démontre cette valeur au sujet de cette ouverture ?
  1. Le flux doit être fermé comme après un échec d’ouverture.
  2. Elle ne démontre aucun échec de cette ouverture réussie.
  3. La prochaine lecture échouera immédiatement à coup sûr.
  4. Le fichier a été ouvert avec un mode d’accès incorrect.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 10 sur 10

Une copie a acquis des flux d’entrée et de sortie distincts. Son entrée signale une erreur de lecture ; la libération des ressources ferme ensuite la sortie, qui renvoie EOF. L’entrée est toujours ouverte et la sortie est incomplète. Quelles actions restantes et quel résultat protègent la source et libèrent les ressources ?
  1. Retenter la fermeture de la sortie, fermer l’entrée une fois, supprimer la sortie incomplète, préserver la source et renvoyer un échec.
  2. Fermer l’entrée une fois, supprimer la sortie incomplète, préserver la source et renvoyer une réussite si la libération des ressources réussit.
  3. Réinitialiser l’erreur de lecture, fermer l’entrée une fois, garder les deux fichiers et renvoyer une réussite si la dernière fermeture réussit.
  4. Fermer l’entrée une fois, tenter de supprimer la sortie incomplète en vérifiant le résultat, préserver la source et renvoyer un échec.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Tu bloques sur ton propre exercice ? Aide aux Devoirs

Mots-Clés

61 Mots-Clés, chacun avec sa définition

A

Accès direct
L’accès direct consiste à se déplacer directement vers un emplacement choisi dans un fichier permettant le positionnement, sans lire toutes les données précédentes.
Un en-tête et une largeur d’enregistrement fixe permettent à un programme de calculer où commence un enregistrement choisi.
Analyse d’une ligne
L’analyse d’une ligne consiste à interpréter les champs d’une ligne d’entrée complète après sa lecture en mémoire.
Une quantité incorrecte peut être signalée avec son numéro de ligne avant la lecture de l’enregistrement suivant.

B

Bourrage de structure
Le bourrage de structure est constitué d’octets supplémentaires qu’un compilateur peut insérer entre les membres ou après le dernier membre pour respecter l’alignement.
Un char suivi d’un int peut occuper huit octets même si leurs tailles totalisent cinq octets.
Boutisme
Le boutisme est l’ordre dans lequel les octets d’une valeur de plusieurs octets sont disposés en mémoire ou dans un fichier.
Un format de fichier spécifie son ordre des octets indépendamment de la machine qui le lit.

C

Caractère nul de fin de chaîne
Un caractère nul de fin de chaîne est le caractère de valeur zéro qui marque la fin d’une chaîne C sans compter dans son texte visible.
Un tampon a besoin d’un octet supplémentaire au-delà des caractères que fgets est autorisé à lire.
Chemin de nettoyage
Un chemin de nettoyage est le parcours du programme qui libère les ressources acquises avant que la réussite ou l’échec ne soit déterminé.
Une même étiquette peut fermer les flux ouverts après plusieurs échecs de traitement différents.
clearerr
clearerr réinitialise les indicateurs de fin de fichier et d’erreur d’un flux sans corriger la cause d’un éventuel échec.
Après avoir résolu un problème d’entrée, un programme peut réinitialiser les indicateurs avant une nouvelle tentative contrôlée.
Code de retour
Un code de retour est le résultat qu’un programme renvoie à son environnement afin que les scripts puissent distinguer une exécution réussie d’une exécution échouée.
Une commande dépendante après && ne doit pas s’exécuter après une copie de fichier incomplète.
Copie par blocs
Une copie par blocs transfère un fichier à travers un tampon réutilisable en n’écrivant que les octets fournis par chaque lecture réussie.
Une dernière lecture incomplète doit produire une dernière écriture de même longueur afin de ne pas ajouter de données restantes du tampon.
Création exclusive
La création exclusive consiste à ouvrir un nouveau fichier uniquement si son nom ne désigne pas déjà un fichier existant.
Le mode C11 "wx" empêche qu’un rapport existant soit effacé lors de l’ouverture.

E

En-tête de fichier
Un en-tête de fichier est une partie initiale d’un fichier qui contient les champs d’identification et de structure nécessaires à l’interprétation des données suivantes.
Vérifie la signature complète et le nombre avant de te fier aux enregistrements indiqués par un en-tête.
Entrée standard
L’entrée standard est le flux déjà ouvert stdin, par lequel un programme reçoit normalement son entrée principale.
Avec < values.txt, un filtre lit les données d’un fichier par stdin au lieu du clavier.
EOF
EOF est une constante entière négative utilisée par les fonctions de lecture de caractères lorsque l’entrée se termine ou qu’une erreur de lecture se produit.
Conserve la valeur renvoyée dans un int pour qu’un octet réel ne soit pas confondu avec ce marqueur.
ERANGE
ERANGE est le code standard d’erreur de dépassement de plage utilisé lorsqu’une conversion ou un calcul produit une valeur hors de la plage prise en charge.
Remets errno à zéro avant strtol pour ne pas confondre une ancienne erreur de dépassement de plage avec une nouvelle.
errno
errno est une expression entière modifiable contenant un code d’erreur, dont le sens dépend du comportement d’erreur documenté d’une fonction.
Enregistre sa valeur immédiatement après un échec signalé, avant qu’un autre appel à la bibliothèque puisse la modifier.

É

Échec de correspondance
Un échec de correspondance se produit lorsque les caractères d’entrée ne conviennent pas à une conversion formatée demandée, sans qu’une erreur d’entrée-sortie soit nécessaire.
Lire un entier depuis "oak" peut renvoyer 0 alors que les indicateurs de fin de fichier et d’erreur du flux restent désactivés.
Élément incomplet
Un élément incomplet est une unité de la taille d’un objet obtenue partiellement lors d’une lecture et omise du nombre d’éléments complets renvoyé par fread.
Lors d’une lecture par éléments de la taille d’un enregistrement, les octets restants à la fin ne peuvent pas être traités comme un enregistrement valide.

F

fclose
fclose est la fonction qui vide les sorties en attente, ferme un flux et signale une réussite par 0 ou un échec par EOF.
Après le retour de fclose(fp), même en cas d’erreur, ce flux ne peut plus être utilisé.
fgetc
fgetc est la fonction qui lit un caractère sous forme d’une valeur de caractère non signé convertie en int, ou renvoie EOF.
Une boucle ne compte un séparateur qu’après que fgetc a renvoyé un caractère avec succès.
fgets
fgets lit une suite de caractères de longueur limitée, conserve un caractère de fin de ligne rencontré et ajoute un caractère nul de fin de chaîne en cas de réussite.
Une ligne plus longue qu’un tampon de 32 octets est renvoyée en plusieurs lectures réussies.
Flux
Un flux est un canal de la bibliothèque standard qui relie un programme à une suite de caractères en entrée ou en sortie.
Un lecteur peut traiter un fichier sur disque ou une entrée de terminal par la même interface de flux.
Flux en lecture et écriture
Un flux en lecture et écriture est un flux ouvert avec un mode contenant +, qui autorise l’entrée et la sortie selon des règles de passage de l’une à l’autre.
Ouvrir un fichier existant d’enregistrements binaires avec "r+b" le préserve pour un accès en lecture et écriture.
fopen
fopen est la fonction de la bibliothèque standard qui ouvre un fichier nommé dans un mode choisi et renvoie un pointeur de flux ou NULL.
Une ouverture en lecture échouée ne laisse aucun flux valide pour un appel ultérieur à fgetc ou à fclose.
fpos_t
fpos_t est le type opaque utilisé par fgetpos et fsetpos pour enregistrer et restaurer toutes les informations nécessaires sur la position dans un fichier.
Un jeton enregistré est restauré par les fonctions de la bibliothèque, sans calcul sur sa valeur.
fputc
fputc est la fonction qui écrit un caractère dans un flux et renvoie sa valeur ou EOF en cas d’échec.
Un filtre de mise en majuscules vérifie chaque appel à fputc après avoir transformé le caractère lu.
fread
fread transfère des octets d’entrée en mémoire et renvoie le nombre d’éléments complets obtenus pour la taille et le nombre demandés.
Une lecture d’enregistrement incomplète doit être vérifiée avant d’en interpréter les champs.
fseek
fseek demande une nouvelle position dans le fichier par rapport au début, à la position courante ou à la fin et renvoie une valeur non nulle en cas d’échec.
Un programme vérifie le déplacement avant de lire l’enregistrement attendu au décalage demandé.
ftell
ftell indique la position d’un flux sous forme de long et renvoie -1L en cas d’échec ; une position dans un flux texte doit être traitée comme un emplacement enregistré.
Un lecteur d’enregistrements binaires peut vérifier sa position après avoir lu un enregistrement complet de taille fixe.
fwrite
fwrite transfère les octets d’objets depuis la mémoire vers un flux et renvoie le nombre d’éléments complets écrits.
Écrire un tableau exige l’adresse de ses données, la taille d’un élément et le nombre d’éléments voulu.

I

Indicateur d’erreur
L’indicateur d’erreur enregistre un échec de lecture ou d’écriture sur un flux et reste activé jusqu’à sa réinitialisation explicite.
Après le retour de NULL par fgets, ferror peut distinguer un échec d’accès d’une fin normale.
Indicateur de fin de fichier
L’indicateur de fin de fichier enregistre le fait qu’une opération d’entrée précédente a rencontré la fin du flux.
Il est vérifié après la lecture ; le tester avant ne peut pas prédire si l’octet suivant existe.

L

Largeur de champ
Une largeur de champ fixe une largeur minimale en sortie ou une largeur maximale en entrée, selon la fonction d’entrée-sortie formatée.
En entrée, %23s laisse de la place pour un caractère nul de fin de chaîne dans un tableau de caractères de 24 octets.

M

Mise à jour en place
Une mise à jour en place remplace des données dans leur espace existant du fichier sans décaler ni réécrire les enregistrements suivants.
Un enregistrement modifié de taille fixe doit être réécrit à sa position de départ d’origine.
Mise en tampon
La mise en tampon consiste à rassembler temporairement des données en mémoire afin que de nombreuses petites demandes d’entrée-sortie puissent partager des transferts plus importants.
Un rapport peut rester dans le tampon de sortie jusqu’au vidage du tampon ou à la fermeture du flux.
Mise entre guillemets des champs CSV
La mise entre guillemets des champs CSV consiste à entourer les champs texte contenant des virgules, des guillemets ou des caractères de fin de ligne et à doubler les guillemets internes.
Un nom de ville contenant une virgule doit rester un seul champ lorsque l’enregistrement est relu.
Mode ajout
Le mode ajout est un mode d’accès qui impose chaque opération de sortie à la fin courante du fichier, quels que soient les déplacements précédents.
Se placer à l’octet zéro dans un flux "a+b" peut sélectionner une position de lecture, mais ne peut pas rediriger l’écriture suivante.
Mode binaire
Le mode binaire est un mode d’accès aux flux qui préserve les valeurs des octets sans les traductions de caractères utilisées pour le texte.
Ouvre une image avec "rb" pour que la traduction de texte ne puisse pas modifier ses octets compressés.
Mode d’ouverture
Un mode d’ouverture est une chaîne qui indique à fopen les accès autorisés et précise si le contenu existant est conservé ou effacé.
Choisir "a" préserve les entrées précédentes d’un journal et place chaque nouvelle écriture à la fin.
Mode texte
Le mode texte est un mode d’accès aux flux pour les lignes lisibles, dans lequel la bibliothèque peut traduire les caractères selon les conventions du système.
Sous Windows, la sortie en mode texte transforme un caractère de fin de ligne du programme en séquence CR LF stockée.

N

Nombre d’affectations réussies
Le nombre d’affectations réussies est le nombre de valeurs qu’un appel d’entrée formatée a effectivement stockées dans leurs destinations.
Un enregistrement à deux champs lu par fscanf n’est complet que si le nombre d’affectations réussies renvoyé est 2.

O

Ordre gros-boutiste
L’ordre gros-boutiste place l’octet de poids fort d’une valeur de plusieurs octets en premier dans la suite d’octets.
La valeur 258 apparaît sous la forme 00 00 01 02 dans un champ gros-boutiste de 32 bits.
Ordre petit-boutiste
L’ordre petit-boutiste place l’octet de poids faible d’une valeur de plusieurs octets en premier dans la suite d’octets.
Les quatre octets 02 01 00 00 représentent 258 dans un champ petit-boutiste de 32 bits.

P

perror
perror écrit une chaîne et le message correspondant à la valeur courante de errno sur la sortie d’erreur standard, suivis d’un caractère de fin de ligne.
Une chaîne indiquant le fichier rend un diagnostic de fichier absent plus utile qu’un message sans précision.
Pointeur de fin de conversion
Un pointeur de fin de conversion fourni par strtol indique le premier caractère après le préfixe numérique accepté par la conversion.
Pour "86cm", le pointeur atteint le c, ce qui permet de rejeter les caractères restants dans un champ entier.
Pointeur FILE
Un pointeur FILE est une valeur de type FILE * qui donne aux fonctions de la bibliothèque accès à un flux et à son état enregistré.
Passe fp à fgetc après avoir vérifié que l’ouverture a renvoyé un pointeur de flux valide.
Position dans le fichier
Une position dans le fichier est l’emplacement d’un flux où commence la prochaine lecture ou écriture, lorsque ce flux permet le positionnement.
Lire un enregistrement binaire de 24 octets fait avancer la position au-delà de ses 24 octets.
Propriété des ressources
La propriété des ressources indique quelle partie d’un programme est responsable de la libération d’un flux ou d’une autre ressource acquise.
Une fonction auxiliaire ferme un flux qu’elle a ouvert et laisse un flux emprunté à sa fonction appelante.

R

Redirection
La redirection est une opération de l’interpréteur de commandes qui relie les flux standard d’un programme à des fichiers sans modifier son code d’entrée-sortie.
L’opérateur 2> peut enregistrer les messages de diagnostic d’un programme dans un fichier distinct.
Remise d’un caractère dans le flux
La remise d’un caractère dans le flux consiste à replacer un caractère dans un flux d’entrée afin que la lecture suivante le récupère à nouveau.
Un analyseur peut examiner un séparateur et utiliser ungetc pour le laisser à l’étape suivante.
Remplacement avec possibilité de récupération
Un remplacement avec possibilité de récupération garde un fichier existant disponible jusqu’à la mise en place réussie d’une nouvelle version complète.
Si le renommage qui met en place la nouvelle version échoue, préserve la sauvegarde et indique son emplacement pour la restauration.
rename
rename change le nom d’un fichier et renvoie 0 en cas de réussite ; le traitement d’une destination existante dépend de l’implémentation.
Garde une sauvegarde disponible si la mise en place d’un fichier de remplacement terminé sous le nom d’origine échoue.
Répertoire de travail courant
Le répertoire de travail courant est le dossier de référence du programme en cours d’exécution pour résoudre les chemins relatifs.
Lancer un programme dans un autre dossier change le fichier désigné par "data/input.txt".

S

Signature de fichier
Une signature de fichier est une suite définie d’octets initiaux qui aide un lecteur à identifier un format ou une version de fichier.
Le marqueur de début JPEG aide à repérer des images candidates avant la vérification du reste de leur structure.
snprintf
snprintf formate du texte dans un tableau de capacité limitée et renvoie la longueur complète nécessaire, sans le caractère nul de fin de chaîne, ou une valeur négative en cas d’erreur.
Une valeur de retour égale à la capacité indique déjà que le texte complet ne tenait pas.
Sortie d’erreur standard
La sortie d’erreur standard est le flux déjà ouvert stderr, utilisé pour les diagnostics indépendamment du flux des résultats normaux d’un programme.
Un échec de conversion peut être décrit sur stderr tandis que les données valides vont vers stdout.
Sortie standard
La sortie standard est le flux déjà ouvert stdout, utilisé pour les résultats normaux et les autres sorties principales d’un programme.
Un tube envoie stdout d’un convertisseur vers l’entrée standard d’un autre programme.
Spécification de conversion
Une spécification de conversion est une instruction commençant par un signe pourcentage qui détermine comment une entrée-sortie formatée affiche ou convertit une valeur.
En entrée, %lf exige une destination double *, tandis que %f exige une destination float *.
strcspn
strcspn trouve la longueur de la partie initiale d’une chaîne avant tout caractère d’un ensemble à exclure, ou sa longueur complète si aucun n’est présent.
Utiliser "\r\n" comme ensemble à exclure permet de trouver une fin de ligne sans supprimer de données utiles à la fin.
strerror
strerror renvoie le texte d’un message appartenant à la bibliothèque pour un code d’erreur fourni, ce qui permet à un programme de composer son propre diagnostic.
Utilise un code enregistré lorsque la libération des ressources risque d’avoir remplacé la valeur courante de errno.

T

Tube
Un tube relie la sortie standard d’un programme à l’entrée standard d’un autre sous forme d’un flux de données.
Un générateur peut alimenter directement un programme de tri sans enregistrer d’abord un fichier intermédiaire.
Type entier de largeur fixe
Un type entier de largeur fixe possède, lorsqu’il est disponible, une largeur exacte spécifiée en bits, sans déterminer l’ordre des octets externe.
Un champ uint32_t exige toujours un encodage explicite des octets pour rendre portable un fichier d’échange.

À propos de cette unité

Par StudeliSignaler une erreur

Crée ton propre Matériel d'Étude

Transforme tes notes, diapos et documents en Guides d'Étude, Questions Pratiques, Flashcards et 6 autres outils.

Commencer