transformer un tampon en lignes
Le
Benoit Izac
Bonjour,
Je cherche à faire une fonction qui me permette de transformer un tampon
en lignes de façon à traiter ces dernières par la suite.
J'ai un bout de code qui fonctionne :
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define BS 10
#define LM 100
int get_line(const char *buffer, size_t *bp, size_t bs,
char *line, size_t *lp, size_t ls)
{
while (*bp < bs) {
/* not enough space in line */
if (!(*lp < ls)) {
(*bp)--;
line[(*lp)-1] = 0;
(*lp) = 0;
return -1;
}
/* end of file? */
if (buffer[*bp] == EOF) {
printf("end of file?");
line[*lp] = 0;
return 0;
}
/* end of line */
if (buffer[*bp] == '') {
line[*lp] = 0;
(*lp) = 0;
(*bp)++;
return 1;
}
line[*lp] = buffer[*bp];
(*lp)++;
(*bp)++;
}
/* end of buffer */
(*bp) = 0;
line[*lp] = 0;
return 0;
}
int main(int argc, char *argv[])
{
int fd;
size_t r;
char buf[BS];
char line[LM];
size_t bp = 0;
size_t lp = 0;
if (argc != 2)
exit(1);
fd = open(argv[1], O_RDONLY);
if (fd < 0)
exit(2);
while((r = read(fd, buf, BS))) {
while (get_line(buf, &bp, r, line, &lp, LM))
printf("%s", line);
}
close(fd);
return 0;
}
PS : Désolé pour ceux qui ne sont pas sous Unix, j'ai bien essayé de
faire la même chose avec fread() mais cette dernière ne me renvoie pas
la taille lu, ce qui pose un problème à la dernière lecture où r est la
plupart du temps différent de BS.
Mon choix lorsque la taille de line est trop petite est discutable
(vaut-il mieux tronquer les lignes ?) mais ce n'est pas vraiment ce qui
me pose problème.
Les questions :
* Ce code est-il correct (fonctionne-t-il dans tous les cas) ?
* Est-il possible d'alléger le code (que get_line est moins
d'arguments) en déclarant bp et lp static ?
* Existe-t-il une autre solution (fonction toute faite, autre
algorithme) ?
Merci.
--
Benoit Izac
Je cherche à faire une fonction qui me permette de transformer un tampon
en lignes de façon à traiter ces dernières par la suite.
J'ai un bout de code qui fonctionne :
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define BS 10
#define LM 100
int get_line(const char *buffer, size_t *bp, size_t bs,
char *line, size_t *lp, size_t ls)
{
while (*bp < bs) {
/* not enough space in line */
if (!(*lp < ls)) {
(*bp)--;
line[(*lp)-1] = 0;
(*lp) = 0;
return -1;
}
/* end of file? */
if (buffer[*bp] == EOF) {
printf("end of file?");
line[*lp] = 0;
return 0;
}
/* end of line */
if (buffer[*bp] == '') {
line[*lp] = 0;
(*lp) = 0;
(*bp)++;
return 1;
}
line[*lp] = buffer[*bp];
(*lp)++;
(*bp)++;
}
/* end of buffer */
(*bp) = 0;
line[*lp] = 0;
return 0;
}
int main(int argc, char *argv[])
{
int fd;
size_t r;
char buf[BS];
char line[LM];
size_t bp = 0;
size_t lp = 0;
if (argc != 2)
exit(1);
fd = open(argv[1], O_RDONLY);
if (fd < 0)
exit(2);
while((r = read(fd, buf, BS))) {
while (get_line(buf, &bp, r, line, &lp, LM))
printf("%s", line);
}
close(fd);
return 0;
}
PS : Désolé pour ceux qui ne sont pas sous Unix, j'ai bien essayé de
faire la même chose avec fread() mais cette dernière ne me renvoie pas
la taille lu, ce qui pose un problème à la dernière lecture où r est la
plupart du temps différent de BS.
Mon choix lorsque la taille de line est trop petite est discutable
(vaut-il mieux tronquer les lignes ?) mais ce n'est pas vraiment ce qui
me pose problème.
Les questions :
* Ce code est-il correct (fonctionne-t-il dans tous les cas) ?
* Est-il possible d'alléger le code (que get_line est moins
d'arguments) en déclarant bp et lp static ?
* Existe-t-il une autre solution (fonction toute faite, autre
algorithme) ?
Merci.
--
Benoit Izac

Poser une question


======================================================================= #include #include
#include #include #include
#define BS 10
#define LM 100
char *get_line(const char *buffer, size_t bs)
{
static char l[LM] = { 0 };
static size_t bp = 0; /* buffer position */
static size_t lp = 0; /* line position */
while (bp < bs) {
/* line too short */
if (lp == LM) {
fprintf(stderr, "line too shortn");
exit(1);
}
/* end of line */
if (buffer[bp] == 'n') {
l[lp] = 0;
lp = 0;
bp++;
return l;
}
l[lp] = buffer[bp];
lp++;
bp++;
}
/* end of buffer */
bp = 0;
return NULL;
}
int main(int argc, char *argv[])
{
int fd;
size_t r;
char buf[BS];
char *line = NULL;
if (argc != 2)
exit(1);
fd = open(argv[1], O_RDONLY);
if (fd < 0)
exit(2);
while((r = read(fd, buf, BS))) {
while ((line = get_line(buf, r)))
printf("%sn", line);
}
close(fd);
return 0;
}
=======================================================================
C'est nettement plus lisible comme ça. Par contre j'ai un gros doute sur
le « static char l[LM] » : est-ce que sa durée de vie est garantie
pendant toute la durée du programme ?
--
Benoit Izac
Inutiles à priori ici.
Le code est un poil compliqué, et de plus il est faux: read() va couper
les lignes en plein milieu, chose qui n'est pas gérée ici.
Le "buffer[*bp] == EOF" est faux aussi: EOF n'est pas un caractère, il
est retourné de manière occasionnelle pour signaler "fin de fichier" en
tant que valeur de retour, mais en aucun cas comme caractère: EOF n'est
PAS un "caractère", il n'a aucune existence physique [je laisse tomber
les subtilités du mode texte ancestral qui acceptait de tels délimiteurs
dans des cas précis - tout cela date de la préhistoire et peut être oublié]
Typiquement fgetc() retourne un nombre entre 0 et 255 (inclus) quand
tout se passe bien, et -1 en cas d'erreur. Evidemment, caster le retour
en (char) est une erreur, car on ne peut alors plus faire la différence
entre le caractère 0xff et EOF.
Attention aussi, read() retourne un résultat non nul en cas d'erreur.
(boucle infinie en cas d'erreur, donc). Il retourne 0 en cas de .. end
of file.
while((r = read(fd, buf, BS)) > 0) {
Euh, fread(buffer_dest, 1, nombre_octets, file) renvoi le nombre
d'octets (le "1" est la taille des record à lire, càd ici 1 octet).
char *fgets(char *s, int size, FILE *stream);
me semble une bonne piste. (stdio.h)
le 30/07/2010 à 19:02, Xavier Roche a écrit dans le message
read() est dans unistd.h, open() demande les deux autres.
Il me semble justement que si (en tout cas ça fonctionne quelque soit le
taille BS).
C'est noté.
Tout à fait (c'était juste un rapide exemple pour remplir buf, je
n'utilise pas read() dans mon programme).
Effectivement, je l'utilisais dans l'autre sens :
fread(buffer_dest, nombre_octets, 1, file) et là, à la dernière lecture,
il me renvoyait 0 si elle n'était pas complète.
D'ailleurs, ça change quelque chose au niveau des performances de dire
je veux faire une lecture de N octets ou je veux N lectures de 1 octet ?
C'est justement là qu'est mon problème, je ne peux pas l'utiliser car je
n'ai pas un FILE* mais un tampon qui est mis à jour par une fonction
(exactement comme le read() ici).
En fait, avec le read(), j'ai trouvé une astuce avec fdopen() mais elle
n'est pas utilisable avec la fonction que j'utilise (archive_read_data()
de la libarchive si ça en intéresse).
--
Benoit Izac
Unix ou pas, read() est une mauvaise idee dans ce contexte, lire les
donnees 10 octets par 10 octets, c'est tres inefficace.
Si tu es sous Unix, il y a un getline() dans posix 2008.
Sinon, ben en standard, on ecrit ta fonction autour de fgets(), mais
c'est un peu casse-gueule a ecrire au debut.
Vu que tu n'es pas arrive a voir comment marche fread() (en particulier,
trouver la taille lue), c'est inquietant pour fgets().
Cote choix des identifiants, je remplacerais BS par autre chose de plus
explicite (oui, BUFSIZE c'est plus clair), parce que la, mon cerveau
oscille entre "Bullshit" et "Bjarne Stroustrup" sans passer par la bonne
case...
Bof. Les choses "static" cassent la re-entrance (appels concurrentiels
dans le même espace mémoire: thread, interruptions, etc).
Une solution possible pour réduire les params est d'utiliser un pointeur
sur struct en argument. Si de plus ta fonction a des variables d'états
tu peux les déclarer dans la struct et avoir un code ré-entrant du coup.
Tu peux même regrouper les champs de la structure pour avoir tous les
paramètres d'entrée dans une sous-structure "in" et ceux de sortie dans
"out" (et les états dans un "state").
DIGRESSION:
Contrairement aux fonctions où les paramètres sont anonymes à
l'extérieur, avec une structure tu peux accèder aux paramètres de ta
fonction via leur nom en plus de leur position. Ca rend les choses plus
claires. Compare par exemple:
plot(3,128,12);
// heuuu 128 c'est quoi déjà? la couleur ou l'abscisse ?
// ..
// Attends ctrl-f3 que je retrouve la definition de la
// fonction dans le fichier H
// ..
extern void plot(int color, int y, int x);
// Ah ouais ok c'est donc un "y8" en fait.
avec:
struct plot arg;
arg.color = 3; arg.x = 12; arg.y = 128;
plot(&arg);
Tu peux même pousser le luxe jusqu'à écrire:
#define plot2(arg) do {struct plot tmp = arg; plot(&tmp);} while(0)
Et utiliser une écriture où les paramètres sont carrément nommés et où
l'ordre n'intervient plus:
plot2({.x = 12, .y = 128, .color = 3});
On peut même aller plus loin et prendre la convention que la struct et
la fonction ont le même nom et utiliser la macro:
#define call(fcn, arg) {struct fcn tmp = arg; fcn(&tmp);} while(0)
Après on aime ou pas, on utilise ou pas c'est comme on veut. Mais c'est
une possibilité permise.
sam.