C: la Lecture d'un fichier texte (avec la variable de la longueur des lignes), ligne par ligne, à l'aide de fread()/fgets() au lieu de fgetc() (bloc d'e/S et de caractère I/O)
Est-il un getline
fonction qui utilise fread
(bloc I/O) au lieu de fgetc
(caractère I/O)?
Il y a une perte de performance pour la lecture d'un fichier caractère par caractère par fgetc
. Nous pensons que pour améliorer les performances, nous pouvons utiliser le bloc lit via fread
dans la boucle intérieure de la getline
. Toutefois, cela introduit potentiellement indésirable effet de lire après la fin d'une ligne. Au moins, cela nécessiterait la mise en œuvre de getline
de garder une trace de la "non-lu" une partie du fichier, ce qui nécessite une couche d'abstraction au-delà de la norme ANSI C sémantique de FICHIER. Ce n'est pas quelque chose que nous voulons mettre en œuvre nous-mêmes!
Nous avons profilé de notre application, et le ralentissement des performances est isolé sur le fait que nous sommes de consommer de gros fichiers, caractère par caractère par fgetc
. Le reste de la surcharge a fait un trivial coût par comparaison. Nous sommes toujours à la séquentiellement la lecture de chaque ligne du fichier, du début à la fin, et on peut verrouiller le dossier complet de la durée de la lecture. C'est probablement ce qui rend une fread
à base de getline
plus facile à mettre en œuvre.
Oui, une getline
fonction qui utilise fread
(bloc I/O) au lieu de fgetc
(caractère I/O) existent? Nous sommes à peu près sûr qu'il fait, mais si non, comment devrions-nous mettre en œuvre?
Mise à jour Trouvé un article utile, Manipulation de la Saisie de l'Utilisateur en C, par Paul Hsieh. C'est un fgetc
approche, mais il a une discussion intéressante sur les solutions de rechange (à commencer par la mauvaise gets
est, puis discuter fgets
):
D'autre part, la commune de la cornue de programmeurs C (même ceux qui sont considérés comme de l'expérience) est-à-dire que fgets() devrait être utilisé comme une alternative. Bien sûr, par lui-même, fgets() n'est pas vraiment gérer la saisie de l'utilisateur en soi. En plus d'avoir un drôle de fin de chaîne condition (lors de la rencontre \n ou expressions du FOLKLORE, mais pas \0), le mécanisme choisi pour la résiliation lorsque la mémoire tampon a atteint la capacité maximale est de simplement brusquement stopper la fgets() de fonctionnement et d' \0 y mettre fin. Donc, si la saisie de l'utilisateur dépasse la longueur de la préaffectés tampon, fgets() retourne un résultat partiel. Pour faire face à cette programmeurs ont deux choix; 1), il suffit de traiter avec tronquée de l'utilisateur (il n'y a aucun moyen de feed-back à l'utilisateur que l'entrée a été tronqué, alors qu'ils sont à fournir en entrée) 2) Simuler une cultivables tableau de caractères et de le remplir avec des appels successifs à fgets(). La première solution, est presque toujours une mauvaise solution pour la variable de la longueur de la saisie de l'utilisateur parce que la mémoire tampon sera inévitablement trop grand la plupart du temps parce que sa en essayant de capturer trop nombreux sont les cas ordinaires, et trop petit pour les cas inhabituels. La deuxième solution est bien sauf qu'il peut être compliqué à mettre en œuvre correctement. Ne traite pas fgets' étrange comportement à l'égard de '\0'.
Exercice laissé au lecteur: afin de déterminer le nombre d'octets a été vraiment lu par un appel à fgets(), on pourrait, par la numérisation, tout comme il le fait, pour un '\n' et sauter par-dessus tout '\0' en ne dépassant pas la taille transmis à fgets(). Expliquer pourquoi ce qui est insuffisant pour la dernière ligne d'un cours d'eau. Quelle faiblesse de ftell() l'empêche de s'attaquer à ce problème complètement?
Exercice laissé au lecteur: Résoudre le problème de la détermination de la longueur des données consommées par fgets() en remplacement de la totalité de la mémoire tampon avec une valeur différente de zéro entre chaque appel à fgets().
Donc, avec fgets() qui nous a laissé le choix d'écrire beaucoup de code et de vivre avec une terminaison de ligne condition qui est en contradiction avec le reste de la bibliothèque C, ou avoir une limite arbitraire. Si ce n'est pas assez bon, alors, que nous reste-il? scanf() mélanges de l'analyse à lire d'une manière qui ne peuvent être séparées, et fread() lira au-delà de la fin de la chaîne. En bref, la bibliothèque C, nous laisse avec rien. Nous sommes obligés de rouler nos propres fondées sur le dessus de fgetc() directement. Donc permet de donner un coup de feu.
Oui, une getline
fonction qui est basé sur fgets
(et de ne pas tronquer l'entrée) existent?
- Pour votre nouvelle question à la fin, oui, ça existe. Je l'ai décrit dans ma réponse. L'article que vous avez cité mentionne un problème avec un final de non-retour à la ligne à terminaison de ligne; j'ai fait un non-problème en pré-remplissage de la mémoire tampon avec
'\n'
et de fournir un moyen de détecter l'état. - Notez également que Paul Hsieh est la solution à utiliser
fgetc
est très mauvais. Sur les implémentations modernes, en raison de l'exigence à l'appui de verrouillage dans le cas où plusieurs threads accèdent à la mêmeFILE
objet, à l'aide defgetc
sera très lent. Vous pouvez utilisergetc_unlocked
(mais c'est une fonction POSIX, pas un standard C de la fonction), mais même avec une optimale macro extension degetc_unlocked
, la façonfgets
recherches de la mémoire tampon pour'\n'
(c'est à dire à l'aide dememchr
) sera beaucoup plus rapide que ce que vous pouvez faire sans accès à la mémoire tampon interne. Notez également que si vous avez POSIX (2008), vous avezgetline
déjà.
Vous devez vous connecter pour publier un commentaire.
Ne pas utiliser
fread
. Utilisationfgets
. Je le prends c'est un devoirs/classproject problème, donc je ne suis pas de fournir une réponse complète, mais si tu dis que c'est pas, je vais vous donner plus de conseils. Il est certainement possible de fournir 100% de la sémantique de type GNUgetline
, y compris intégrés des octets nuls, en utilisant uniquementfgets
, mais il nécessite quelques petits malins de la pensée.OK, mise à jour puisque ce n'est pas de devoirs:
memset
votre tampon à'\n'
.fgets
.memchr
pour trouver la première'\n'
.'\n'
est trouvé, la ligne est plus longue que votre tampon. Englarge le tampon, remplissez la nouvelle partie avec'\n'
, etfgets
dans la nouvelle partie, à répéter si nécessaire.'\n'
est'\0'
, puisfgets
résilié en raison de parvenir à la fin d'une ligne.fgets
résilié pour atteindre les expressions du FOLKLORE, la'\n'
est laissé au cours de votrememset
, le caractère précédent est la valeur null quifgets
écrit, et le personnage avant que c'est le dernier caractère de données réelles lire.Vous pouvez éliminer les
memset
et l'utilisationstrlen
en place dememchr
si vous n'avez pas de soins sur l'appui des lignes avec des valeurs null incorporées (de toute façon, le nul de ne pas interrompre la lecture; il va juste être une partie de votre lire en ligne).Il y a aussi un moyen de faire la même chose avec
fscanf
et la"%123[^\n]"
spécificateur (où123
est votre limite de tampon), qui vous donne la souplesse nécessaire pour s'arrêter au non-caractères de saut de ligne (ala GNUgetdelim
). Cependant, il est sans doute ralentir à moins que votre système a une très chicsscanf
mise en œuvre.fgets
? À l'aide d'un grossissement de la mesure tableau de caractères et de le remplir avec des appels successifs àfgets
semble compliqué à mettre en œuvre correctement. Aussi, je comprends quefgets
se termine lors de la rencontre '\n' ou expressions du FOLKLORE, mais pas de '\0'. Ce n'est pas un problème pour nos fichiers, mais.char s[5]; memset(s, '\n', sizeof s); fgets(s, sizeof s, ...);
sur un fichier avec 3 octets "xyz" conduit à "xyz\0\n" danss
. Trouver le premier'\n'
est OK, mais de vérifier le caractère suivant est UB. Suggérons d'ajouter "Si le '\n' à la dernière place, alorsfgets
résilié en raison d'atteindre la dernière ligne dans le fichier.", puis cliquez sur "Si le caractère suivant ..."strcat
etfgets
sera souvent nécessaire de trouver le dernier caractère écrit--quelque chose que le code de ces fonctions ont déjà connu. Je ne vois pas d'utilité pour la valeur de retour de ces fonctions mises en œuvre.Il n'y a pas une grosse différence de performances entre fgets et fgetc/setvbuf.
Essayez: