J'ai écrit cette fonction pour lire une ligne d'un fichier:
const char *readLine(FILE *file) {
if (file == NULL) {
printf("Error: file pointer is null.");
exit(1);
}
int maximumLineLength = 128;
char *lineBuffer = (char *)malloc(sizeof(char) * maximumLineLength);
if (lineBuffer == NULL) {
printf("Error allocating memory for line buffer.");
exit(1);
}
char ch = getc(file);
int count = 0;
while ((ch != '\n') && (ch != EOF)) {
if (count == maximumLineLength) {
maximumLineLength += 128;
lineBuffer = realloc(lineBuffer, maximumLineLength);
if (lineBuffer == NULL) {
printf("Error reallocating space for line buffer.");
exit(1);
}
}
lineBuffer[count] = ch;
count++;
ch = getc(file);
}
lineBuffer[count] = '\0';
char line[count + 1];
strncpy(line, lineBuffer, (count + 1));
free(lineBuffer);
const char *constLine = line;
return constLine;
}
La fonction lit correctement le fichier, et en utilisant printf, je vois que la chaîne constLine a également été lue correctement.
Cependant, si j'utilise la fonction par exemple comme ceci:
while (!feof(myFile)) {
const char *line = readLine(myFile);
printf("%s\n", line);
}
printf produit du charabia. Pourquoi?
fgets
place defgetc
. Vous lisez caractère par caractère au lieu de ligne par ligne.getline()
fait partie de POSIX 2008. Il peut y avoir des plates-formes de type POSIX sans elle, surtout si elles ne prennent pas en charge le reste de POSIX 2008, mais dans le monde des systèmes POSIX,getline()
c'est assez portable de nos jours.Réponses:
Si votre tâche n'est pas d'inventer la fonction de lecture ligne par ligne, mais simplement de lire le fichier ligne par ligne, vous pouvez utiliser un extrait de code typique impliquant la
getline()
fonction (voir la page de manuel ici ):la source
getline
c'est spécifique à GNU libc, c'est-à-dire à Linux. Cependant, si l'intention est d'avoir une fonction de lecture de ligne (par opposition à l'apprentissage C), il existe plusieurs fonctions de lecture de ligne du domaine public disponibles sur le Web.if(line)
chèque est superflu. L'appelfree(NULL)
est essentiellement un non-op.la source
(FILE*) fp
? N'est-ce pasfp
déjà unFILE *
etfopen()
retourne également unFILE *
?getline
est une bonne alternative. Je suis d'accord que leFILE *
casting est inutile.fp
enfilePointer
pour plus de clarté.Dans votre
readLine
fonction, vous renvoyez un pointeur vers leline
tableau (à proprement parler, un pointeur vers son premier caractère, mais la différence n'est pas pertinente ici). Puisqu'il s'agit d'une variable automatique (c'est-à-dire qu'elle est «sur la pile»), la mémoire est récupérée lorsque la fonction retourne. Vous voyez du charabia parce qu'ilprintf
a mis ses propres trucs sur la pile.Vous devez renvoyer un tampon alloué dynamiquement à partir de la fonction. Vous en avez déjà un, c'est
lineBuffer
; tout ce que vous avez à faire est de le tronquer à la longueur souhaitée.ADDED (réponse à la question de suivi dans le commentaire):
readLine
renvoie un pointeur vers les caractères qui composent la ligne. Ce pointeur est ce dont vous avez besoin pour travailler avec le contenu de la ligne. C'est aussi ce à quoi vous devez passerfree
lorsque vous avez fini d'utiliser la mémoire occupée par ces personnages. Voici comment vous pouvez utiliser lareadLine
fonction:la source
la source
fopen_s
rend le code non portable.printf
recherchera les spécificateurs de format et n'imprimera pas les signes de pourcentage et le ou les caractères suivants tels quels . Les octets nuls feront disparaître tous les caractères du reste de la ligne. (Ne me dites pas que les octets nuls ne peuvent pas arriver!)readLine()
renvoie le pointeur vers la variable locale, ce qui provoque un comportement indéfini.Pour vous déplacer, vous pouvez:
readLine()
line
utilisationmalloc()
- dans ce casline
sera persistantla source
Permet
fgets()
de lire une ligne à partir d'un descripteur de fichier.la source
Certaines choses ne vont pas avec l'exemple:
fprintf(stderr, ....
fgetc()
plutôt quegetc()
.getc()
est une macro,fgetc()
est une fonction propregetc()
renvoie unint
soch
doit être déclaré comme unint
. Ceci est important car la comparaison avecEOF
sera gérée correctement. Certains jeux de caractères 8 bits sont utilisés0xFF
comme caractère valide (ISO-LATIN-1 serait un exemple) etEOF
qui vaut -1, sera0xFF
s'il est attribué à unchar
.Il y a un débordement de tampon potentiel sur la ligne
Si la ligne est exactement de 128 caractères,
count
est de 128 au point qui est exécuté.Comme d'autres l'ont souligné,
line
est un tableau déclaré localement. Vous ne pouvez pas y renvoyer de pointeur.strncpy(count + 1)
copiera au maximum lescount + 1
caractères mais se terminera s'il frappe'\0'
Parce que vous avez définilineBuffer[count]
sur'\0'
vous savez que cela n'arrivera jamaiscount + 1
. Cependant, si c'était le cas, cela ne mettrait pas de terminaison'\0'
, vous devez donc le faire. Vous voyez souvent quelque chose comme ce qui suit:si vous avez
malloc()
une ligne à renvoyer (à la place de votrechar
tableau local ), votre type de retour devrait êtrechar*
- drop theconst
.la source
et celui-là?
la source
Voici mes quelques heures ... Lire tout le fichier ligne par ligne.
la source
fgetc
au lieu defgets
?notez que la variable 'line' est déclarée dans la fonction d'appel puis transmise, donc votre
readLine
fonction remplit le tampon prédéfini et la renvoie simplement. C'est ainsi que fonctionnent la plupart des bibliothèques C.Il existe d'autres moyens dont je suis conscient:
char line[]
comme statique (static char line[MAX_LINE_LENGTH]
-> il conservera sa valeur APRÈS le retour de la fonction). -> mauvais, la fonction n'est pas réentrante, et une condition de concurrence peut se produire -> si vous l'appelez deux fois à partir de deux threads, elle écrasera ses résultatsmalloc()
la ligne char [], et la libérer dans les fonctions d'appel -> trop demalloc
s coûteux , et, déléguer la responsabilité de libérer le tampon à une autre fonction (la solution la plus élégante est d'appelermalloc
etfree
sur tous les tampons dans la même fonction)btw, casting 'explicite' de
char*
àconst char*
est redondant.btw2, il n'y a pas besoin de
malloc()
lineBuffer, il suffit de le définirchar lineBuffer[128]
, vous n'avez donc pas besoin de le libérerbtw3 n'utilise pas de 'tableaux de pile de taille dynamique' (définissant le tableau comme
char arrayName[some_nonconstant_variable]
), si vous ne savez pas exactement ce que vous faites, cela ne fonctionne qu'en C99.la source
Vous devez utiliser les fonctions ANSI pour lire une ligne, par exemple. fgets. Après avoir appelé, vous avez besoin de free () dans le contexte d'appel, par exemple:
la source
Implémenter une méthode pour lire et obtenir le contenu d'un fichier (input1.txt)
J'espère que cette aide. Bon codage!
la source
Vous faites l'erreur de renvoyer un pointeur vers une variable automatique. La ligne variable est allouée dans la pile et ne dure que tant que la fonction est active. Vous n'êtes pas autorisé à y renvoyer un pointeur, car dès qu'il revient, la mémoire sera donnée ailleurs.
Pour éviter cela, vous retournez soit un pointeur vers la mémoire qui réside sur le tas, par exemple. lineBuffer et il devrait être de la responsabilité de l'utilisateur d'appeler free () lorsqu'il en a fini avec lui. Vous pouvez également demander à l'utilisateur de vous transmettre comme argument une adresse mémoire sur laquelle écrire le contenu de la ligne.
la source
Je veux un code à partir du sol 0, alors je l'ai fait pour lire le contenu du mot du dictionnaire ligne par ligne.
char temp_str [20]; // vous pouvez modifier la taille de la mémoire tampon en fonction de vos besoins et la longueur d'une seule ligne dans un fichier.
Remarque J'ai initialisé le tampon avec le caractère Null à chaque fois que je lis la ligne.Cette fonction peut être automatisée mais depuis j'ai besoin d'une preuve de concept et que je veux concevoir un programme octet par octet
la source
int main() {
code
char temp_str [20] = {'\ 0'};code
c remplira automatiquement chaque emplacement avec un terminateur nul puisque la façon dont les déclarations de tableau fonctionnent est que si un tableau est initialisé avec moins d'éléments que le tableau contient, le dernier élément remplira les éléments restants.char temp_str[20] = {0}
remplit également tout le tableau de caractères avec des terminateurs nuls.Mon outil à partir de zéro:
la source
fgets
qui pourrait être utilisée.Fournit une fonction portable et générique
getdelim
, test passé via msvc, clang, gcc.la source
fgets
existe?getdelim
permet des délimiteurs personnalisés. Je remarque également qu'il n'y a pas de limite de longueur de ligne - dans ce cas, vous pouvez utiliser la pile avecgetline
. (Les deux décrits ici: man7.org/linux/man-pages/man3/getline.3.html )getdelim
et agetline
été standardisé dans POSIX.1-2008, quelqu'un d'autre mentionne sur cette page).fgets
est également c standard, et non spécifique à Linux