Melchior, je suis le tuto C pendant mes vacances (enfin je ne fais pas que ça ) et je tente des trucs. J'en suis au chapitre sur l'écriture dans les fichiers, donc tout ce qui se trouve après, je ne le connais pas encore (aka "excusez moi si je pose une question dont la réponse se trouve dans un chapitre subséquent). Normalement entre le forum et le net, j'arrive à comprendre mes boulettes, mais là je bloque. Du coup je viens demander de l'aide.
Ce que je cherche à faire :
- j'ai un petit fichier ("fichier1.txt") dans lequel j'ai 4 fois une suite de caractères ou de chiffres (mes tests, c'était avec 1234567890\n1234567890\n etc ou BCDEF\nBCDEF etc). 4 parce que je suis paresseux, mais j'aimerais que ça marche pour "autant de lignes que je veux", donc j'utilise une boucle.
- j'aimerais remplacer à chaque ligne le premier caractère par 'A'. Je ne suis pas compliqué, j'imagine que si je comprends comment le faire, avec un fseek, je saurais remplacer à peu près n'importe quelle position avec un caractère (modulo ce que j'ai lu sur le défaut de scanf et la prise en compte de \n).
Mon idée initiale :
1. à chaque ligne, j'enregistre caractère après caractère, et si j'arrive à \n ou à EOF, je sors de la boucle = la boucle a vocation à amener mon curseur à chaque début de ligne (donc plutôt après chaque \n)
2. Avant chaque boucle, je remplace le premier caractère par 'A' via un fputc()
Mais ça marche pas...
Mon code en l'état :
#include <stdio.h>
#include <stdlib.h>
#include "main.h"
#define SIZE_TEXT 100
int main()
{
FILE *fichier = NULL; //initialisation d'un pointeur fichier à NULL
fichier = fopen("fichier1.txt", "r+"); //ouverture du fichier et récupération du pointeur pour contrôle et utilisation
int caractere = 'Z'; //histoire d'initialiser avec quelque chose que je n'ai pas dans le fichier
if (fichier != NULL) // si ouverture du fichier = OK
{
while (caractere != EOF) //boucle pour passer de ligne en ligne jusqu'à la fin du fichier
{
fputc('A', fichier);
do // boucle qui lit chaque caractère, et qui s'arrête après la lecture d'un \n ou d'un EOF
{
fseek(fichier, 0, SEEK_CUR);
caractere = fgetc(fichier); // caractère prend la valeur du caractère lu
if (caractere != EOF) // pout contrôle, j'affiche chaque caractère lu, sauf EOF
{
printf("%c", caractere);
}
if (caractere == EOF) // pour contrôle, j'affiche "EOF" si j'arrive à cette valeur.
printf("\nEOF");
} while (caractere != '\n' && caractere != EOF); // fin de la boucle do de lecture des caractères
}
}
else //affichage d'erreur à l'ouverture du fichier
{
printf("Erreur à l'ouverture du fichier ou pointeur non alimente.");
}
if (fclose(fichier) == 0) //fermeture du fichier et affichage d'un message de contrôle
{
printf("\nFermeture reussie");
}
else
{
printf("\nFermeture echouee");
}
return 0;
}
Alors :
- initialement, je n'avais pas le fseek :
-- sans le fputc et sans le fseek, mon programme lit le fichier ligne par ligne... pas de souci
-- sans le fseek mais avec le fputc : mon programme ajoute bien 'A' en début de fichier, puis il insère des tonnes d'espaces (j'ai l'impression qu'il se coupe parce qu'il boucle, donc il ferme le fichier proprement). Je ne comprends pas d'où viennent ces espaces.
- j'ai tenté le fseek pour avancer ou reculer d'un caractère, mais alors
-- avec un fseek CUR à +1, le programme n'affiche sur la console que les 2 ou 3 e lettre (j'ai un doute)
-- avec un fseek à -1, le programme boucle en m'affichant A (ça me semble logique)
-- avec un fseek à 0, je pensais qu'il aurait le même comportement que sans fseek, mais non. Et ça je ne le comprends pas
Mes questions :
- d'où proviennent ces fichus espaces ?
- pourquoi ça ne boucle pas éternellement avec le fseek alors que j'avais la croyance aveugle qu'il ferait pareil ?
- A côté de quoi suis-je passé pour arriver à mon résultat (j'avais réussi à faire quelque chose d'intéressant, je crois avec le fseek+1 après le "caractere = fgetc", mais le A était implanté en seconde position des lignes...) ?
Si j'ai un conseil à te donner, lis le fichier d'entrée et génères un fichier de sortie. C'est beaucoup plus propre. Si ton fichier était très gros, cela te prendrait beaucoup de temps. Quand tu auras vu l'utilisation de malloc, tu pourras lire le fichier d'un seul coup. fseek et ftell t'aideront à connaître la grandeur du fichier.
Le Tout est souvent plus grand que la somme de ses parties.
Je suis sûr qu'il y a des manières plus élégantes de faire, et je les utiliserai probablement en avançant dans le cours. Là, j'essaie de comprendre ce qui ne fonctionne pas, même si c'est un peu théorique... tu ne vois pas ?
Je constate la même chose que toi, il y a 1024 caractères blancs par ligne en incluant la fin de ligne. La première ligne contient bien un 'a' au début. Je n'ai pas d'explication bien que le 1024 devrait me dire quelque chose. C'est la première fois que je vois ce genre de code. En plus, c'est compliqué de tester car le programme détruit le fichier "fichier1.txt" à chaque exécution. Je te suggère ce que j'ai dit précédemment, c'est à dire d'écrire sur un nouveau fichier. Quand ton test fonctionnera, tu pourras regarder les fonctions rename et remove pour remplacer l'ancien fichier par le nouveau.
Cela m'a agacé de ne pas comprendre. J'ai poussé mes tests plus loin ...
Je suppose que tu es sur Windows comme moi.
Sur Windows, la fin de ligne est codée sur 2 caractères, le return ('\r') et le newline ('\n').
J'ai utilisé des outils personnels et surtout la fonction ftell() qui m'ont aidé à comprendre.
J'ai ajouté un autre fseek() après le while de fin de boucle (ligne 34?).
Le programme fonctionne maintenant.
Je suppose que d'effacer le newline dérange les fonctions de lecture et écriture de Windows.
- Edité par PierrotLeFou 14 août 2021 à 4:14:05
Le Tout est souvent plus grand que la somme de ses parties.
Il y a beaucoup de choses à dire et il faut bien commencer par quelque chose. Donc pour commencer : il faut toujours bien indenter son code, toujours. Ton code bien indenté ressemble à :
#include <stdio.h>
#include <stdlib.h>
#include "main.h"
#define SIZE_TEXT 100
int main()
{
FILE *fichier = NULL; //initialisation d'un pointeur fichier à NULL
fichier = fopen("fichier1.txt", "r+"); //ouverture du fichier et récupération du pointeur pour contrôle et utilisation
int caractere = 'Z'; //histoire d'initialiser avec quelque chose que je n'ai pas dans le fichier
if (fichier != NULL) { // si ouverture du fichier = OK
while (caractere != EOF) { //boucle pour passer de ligne en ligne jusqu'à la fin du fichier
fputc('A', fichier);
do { // boucle qui lit chaque caractère, et qui s'arrête après la lecture d'un \n ou d'un EOF
fseek(fichier, 0, SEEK_CUR);
caractere = fgetc(fichier); // caractère prend la valeur du caractère lu
if (caractere != EOF) { // pout contrôle, j'affiche chaque caractère lu, sauf EOF
printf("%c", caractere);
}
if (caractere == EOF) { // pour contrôle, j'affiche "EOF" si j'arrive à cette valeur.
printf("\nEOF");
}
} while (caractere != '\n' && caractere != EOF); // fin de la boucle do de lecture des caractères
}
} else { //affichage d'erreur à l'ouverture du fichier
printf("Erreur à l'ouverture du fichier ou pointeur non alimente.");
}
if (fclose(fichier) == 0) { //fermeture du fichier et affichage d'un message de contrôle
printf("\nFermeture reussie");
} else {
printf("\nFermeture echouee");
}
return 0;
}
Je te rassure, j'ai utilisé un outil pour le faire. Ton IDE doit pouvoir le faire ou sinon regarde du côté de astyle par exemple.
Ensuite, c'est toujours bien de lire la doc. Comme je suppose que tu es sous windows on trouve sur la page fseek :
La doc microsoft a écrit:
For streams opened in text mode, fseek and _fseeki64 have limited use, because carriage return-line feed translations can cause fseek and _fseeki64 to produce unexpected results. The only fseek and _fseeki64 operations guaranteed to work on streams opened in text mode are:
Seeking with an offset of 0 relative to any of the origin values.
Seeking from the beginning of the file with an offset value returned from a call to ftell when using fseek or _ftelli64 when using _fseeki64.
Donc si tu ouvres ton fichier en mode texte … mais tu ne spécifies rien, ni b ni t dans ton fopen (et c'est important pour windows, pas pour le reste du monde).
If t or b is not given in mode, the default translation mode is defined by the global variable _fmode. If t or b is prefixed to the argument, the function fails and returns NULL.
Donc : il faut vraiment spécifier le mode d'ouverture de ton fichier, car sous windows il y a une différence et que le mode par défaut n'est pas clairement défini.Et il ne faut pas utiliser fseek et compagnie comme si dans le cas d'une ouverture en mode texte, ou alors comme indiqué dans la doc.
Pour info, il y a eu historiquement des choix différents pour indiquer dans un fichier le «saut à la ligne». Windows a choisi de l'indiquer avec deux caractères que l';on nomme CR et LF, Mac a choisi uniquement CR (sur les vieux OS) et LF sur MacOs, Unix a toujours (pour ce que je sais) utilisé LF. CR signifie cariage return (retour chariot, oui ça date des machines à écrire mécanique) et LF line feed (retour à la ligne). Le souci avec CRLF c'est qu'il s'agit de deux octets (pour simplifier) et qu'en C on note ça avec un char, '\n' en l'occurrence. Du coup quand on ouvre un fichier en mode texte, le double octet CRLF est traduit en un seul char … alors qu'en mode binaire c'en est bien 2. Les autres systèmes n'ont pas ce souci et texte ou binaire c'est la même chose.
Maintenant si on regarde la structure de ton programme de loin :
int main()
{
FILE *fichier = NULL; //initialisation d'un pointeur fichier à NULL
fichier = fopen("fichier1.txt", "r+"); //ouverture
....
if (fichier != NULL) { // si ouverture du fichier = OK
....
} else { //affichage d'erreur à l'ouverture du fichier
....
}
if (fclose(fichier) == 0) { //fermeture du fichier et affichage d'un message de contrôle
....
} else {
....
}
return 0;
}
Tu n'as besoin de fermer le fichier uniquement si tu as réussi à l'ouvrir. Regarde aussi la doc de errno pour déterminer l'erreur exacte avec des fonctions comme perror.
La portion de code :
if (caractere != EOF) { // pout contrôle, j'affiche chaque caractère lu, sauf EOF
printf("%c", caractere);
}
if (caractere == EOF) { // pour contrôle, j'affiche "EOF" si j'arrive à cette valeur.
printf("\nEOF");
}
c'est typiquement un if/else …
Ah oui … les commentaires qui décrivent le code ne servent strictement à rien du tout, que dalle, nada, nothing, nichts … rien. Un commentaire ne doit être utilisé que pour expliquer et non décrire. Et a priori, si tu as besoin d'expliquer ton code c'est que le code est souvent (mais pas toujours) mal écrit. Un code se doit d'être lisible. Les commentaires doivent se limiter à un strict minimum et sont à éviter dans la mesure du possible.
Tu pourrais éventuellement regarder aussi du côté de la fonction ungetc …
Pour info, pour ce genre de tâche il existe d'autres méthodes plus avancées qui seront plus efficaces. Mais bon pour un entraînement c'est cool.
@White Crow: Merci pour astyle, je faisais mon indentation avec un petit programme Python bien imparfait. J'ai connu l'époque des téléscripteurs où le retour de chariot n'était pas qu'une métaphore. Le retour était assez bruyant ... Le line feed était pour faire avancer le papier. On "feed"ait la mitrailleuse qui imprimait ... Pour le problème du PO, je ne me suis pas douté tout de suite de ce que c'était. Une chance que le ftell() est correct. En général, je fais le test d'ouverture et vérification à l'envers: si open ne marche pas: affiche erreur sinon bravo, je peux continuer J'aime bien utiliser perror() et errno.h pour mes erreurs.
Le Tout est souvent plus grand que la somme de ses parties.
Il y aussi le fait qu'après une écriture sur un fichier ouvert en mode '+', un fflush() ou un repositionnement est nécessaire avant une opération de lecture, de même qu'après une lecture pour faire une écriture:
For files open for update (those which include a "+" sign), on which both input and output operations are allowed, the stream shall be flushed (fflush) or repositioned (fseek, fsetpos, rewind) before a reading operation that follows a writing operation. The stream shall be repositioned (fseek, fsetpos, rewind) before a writing operation that follows a reading operation (whenever that operation did not reach the end-of-file).
Ce qui pourrait expliquer ceci
-- sans le fseek mais avec le fputc : mon programme ajoute bien 'A' en début de fichier, puis il insère des tonnes d'espaces
- Edité par edgarjacobs 14 août 2021 à 18:10:56
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
Merci edgarjacobs, je n'avais pas trouvé ce texte. Intuitivement, je me doutais qu'il fallait faire quelque chose entre la lecture et l'écriture, et vice versa. J'ai un petit programme de dump de fichier et ce que je voyais étaient des 0 et non des espaces. Les éditeurs ont de la difficulté avec de très longues lignes remplies de 0 ou autre chose. Je pense que le fichier n'avait qu'une seule ligne de 4096 caractères. Ce qui correspond peut-être à la longueur d'un bloc sur un SSD (?)
Le Tout est souvent plus grand que la somme de ses parties.
merci pour ces explications. Je ne connaissais effectivement pas la doc (j'ose croire que c'est logique à ce stade du cours), mais vos commentaires me permettent de mieux comprendre ce que je fais (et surtout ce que je dois faire).
Outre les questions d'indentement (sic), de commentaires (je les avais ajouté surtout pour poster ici, mais je pense que mon réflexe même pour moi serait plutôt de décrire que d'expliquer effectivement) et de if else, je comprends que le souci vient à la fois :
- d'un comportement que je ne prends pas en compte quand j'utilise une ouverture en +;
- d'un ensemble de caractères liés à mon utilisation de windows qui compliquent la lecture.
Du coup je viens de refaire un mini test (il rejoint d'ailleurs ce que Pierrot disait au début, de plutôt aller écrire dans un nouveau fichier) :
int main()
{
FILE *fichier = NULL;
fichier = fopen("fichier1.txt", "wb");
int caractere = 'Z';
if (fichier != NULL) {
fprintf(fichier, "1234567890\n");
fprintf(fichier, "1234567890\n");
fprintf(fichier, "1234567890\n");
fprintf(fichier, "1234567890");
fseek(fichier, 0, SEEK_SET);
while (caractere != EOF) {
fputc('A', fichier);
do {
caractere = fgetc(fichier);
if (caractere != EOF) {
printf("%c", caractere);
} else {
printf("\nEOF");
}
} while (caractere != '\n' && caractere != EOF);
}
} else {
printf("Erreur à l'ouverture du fichier ou pointeur non alimente.");
}
if (fclose(fichier) == 0) {
printf("\nFermeture reussie");
} else {
printf("\nFermeture echouee");
}
return 0;
}
Je retrouve bien un comportement normal pour la transformation des lignes après leur écriture via le fprintf, et je garde les erreurs de lecture (je n'ai pas regardé ça ce soir). J'imagine que je pourrai les corriger avec des fseek plus intelligents ou alors en faisant simplement une boucle de lecture après l'ensemble des opérations.
Pierrot, tu avais gardé l'exemple de code que tu avais réalisé (je vois que tu es autant agacé de ne pas comprendre que moi ).
Merci à tous une nouvelle fois, je trouve ça très sympa d'avoir des réponses aussi détaillées.
Oui, j'ai gardé le code en question. Je suis encore un peu agacé ... Pour ceux qui connaissent, j'ai Cygwin64 sur mon ordi. Je me suis fait un petit script en bash qui utilise sed pour enlever les CR. Je fais un cat -vn du fichier et je ne vois plus les CR Mais quand je repasse le programme en ouvrant avec "rb+" sans le seek() à la fin de la boucle, ça ne marche pas. Si je le remet, ça fonctionne à nouveau.
Le Tout est souvent plus grand que la somme de ses parties.
When a file is opened with update mode ('+' as the second or third character in the above list of mode argument values), both input and output may be performed on the associated stream. However, output shall not be directly followed by input without an intervening call to the fflush function or to a file positioning function (fseek, fsetpos, or rewind), and input shall not be directly followed by output without an intervening call to a file positioning function, unless the input peration encounters endof-file. Opening (or creating) a text file with update mode may instead open (or create) a binary stream in some implementations.
merci beaucoup, je comprends bcp mieux la source de l'erreur. Et effectivement, avec les fseek après chaque input, on parvient bien à lire l'ensemble du fichier. On peut même revenir à l'objectif initial que je m'étais fixé avec la lecture d'un fichier et sa modif. Petite adaptation dans ton code pour lire chaque ligne depuis le début du coup :
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Le Tout est souvent plus grand que la somme de ses parties.
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
Le Tout est souvent plus grand que la somme de ses parties.
Le Tout est souvent plus grand que la somme de ses parties.
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
Le Tout est souvent plus grand que la somme de ses parties.
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
Le Tout est souvent plus grand que la somme de ses parties.