OVH Cloud OVH Cloud

transformer un tampon en lignes

56 réponses
Avatar
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?\n");
line[*lp] = 0;
return 0;
}
/* end of line */
if (buffer[*bp] == '\n') {
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\n", 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

10 réponses

2 3 4 5 6
Avatar
espie
In article <4c542a31$0$24090$,
Samuel DEVULDER wrote:
Beaucoup oublient que parfois un bout de code ecrit sur le NG n'est pas
le code reel mais qu'il n'est la que pour fixer les idées. Dans la vraie
vie les codes C sont beaucoup plus gros que les 10-15 lignes qu'on ne
peut décemment poster sur une newsgroup et il est normal voir obligé de
shunter des détails important pour l'appli complète (includes par
exemple) mais pas pour le pb spécifique qui coince.



Question de point de vue. Moi, en pratique, les problemes que je
rencontre dans du code C se focalisent tres souvent sur quelques erreurs
courantes. Typiquement quand je corrige du code soit-disant "portable"
pour le faire marcher.

A force de passer du temps sur des milliers de lignes pour toujours
(ou presque) converger sur les memes erreurs classiques, bizarrement, tu
n'as plus du tout envie de laisser passer ces erreurs classiques.

De toute façon à voir les fils les plus long, donc les points les plus
typiquement discutés sur le NG j'aurais l'impression que faire du C
revient super souvent à lire des trucs via le flux standard en
environnement unix et effectuer quelques traitement sur les chaines de
caractères. Heureusement que la programmation (et ses bugs) est beaucoup
plus riche et intéressante que cela avec pleins d'api et de traitements
variés. Du reste, quand on prend du recul, le truc qui coince sur des
petits exemples est souvent à trouver dans la logique du prog (var pas
initialisée, break mal placé, etc) que de condition d'execution rares
telles que "fread() n'a pas tout lu",ou "malloc a echoué", il te manque
tel include en environnement XYZ, etc.



Ca ne me pose aucun probleme qu'il y ait un biais dans les discussions de
ce newsgroup. Ca me parait assez normal.

- on n'est pas obliges de repondre a tous les points de tous les posts.
Si quelqu'un pose une question et que je n'ai pas le temps de repondre en
detail, je ne vois pas en quoi ca m'empeche de souligner un point de
detail.

- tu as toi-meme tes marottes. Pourquoi es-tu venu parler de reentrance et
de static dans ce thread ? tu crois que c'etait reellement au centre du
probleme pose ? (m'etonne d'ailleurs que tu n'aies pas encore commente sur
la suggestion d'utiliser strtok...)

- il y a tellement peu de trafic sur les newsgroups francophones ces
temps-ci qu'il est parfaitement possible de suivre tous les messages,
digressions comprises. Et lire en diagonale quand ca n'interesse plus est
parfaitement possible.
Avatar
espie
In article ,
Benoit Izac wrote:
Concernant la solution au programme qui utilise la libarchive, je l'ai
trouvée avant de poster ce message. Ce qui m'ennuie c'est que le code
ressemble à ça :
{
while (archive_read_next_header(a, &ae) == ARCHIVE_OK) {
[...]
j = 0;
while ((r = archive_read_data(a, buf, bs))) {



Apres avoir zieute un oeil a libarchive, j'ai l'impression
qu'archive_read_data
n'est pas le "meilleur" niveau pour t'interfacer avec celle-ci.

En particulier, tu te bouffes des copies en trop, et tu n'as pas de facon
de connaitre la taille "naturelle" des blocs lus sur ton archive.

Par contre, la doc est un peu pourrie, et l'utilisation precise de
archive_read_data_block() a l'air un peu space, si on regarde par exemple
archive_read_data() lui-meme (on peut pas faire la meme chose dans du code
utilisateur, vu qu'il y a plein de trucs internes.

L'utilisation dans archive_read_data_into_fd est bien plus claire.


En resume, tu peux laisser libarchive gerer son propre tampon.
Il te suffit de declarer quelques variables:

const void *buff;
size_t size;
off_t offset;

et tu peux appeler en boucle:
archive_read_data_block(a, &buff, &size, &offset) == ARCHIVE_OK

ca te positionne buff, size, et offset correctement.

Plus precisement: ca te dit que buff[ 0... size-1] contient les octets
"suivants" dans ton archive, en commencant a la position offset
(offset peut etre plus grand que la derniere valeur connue, typiquement
si tu as des trous dans ton archive).

Note que tu n'as pas le droit de modifier le contenu de buff en place,
comme indique par le const.

Tu peux assez facilement t'en servir pour tenir a jour ta ligne courante,
en t'evitant une copie.

Les questions correspondantes:
- que dois-tu faire si tu rencontres un bloc de 0 ? est-ce que la
reponse "erreur" est acceptable, ou est-ce que tu veux faire plus precis.
- as-tu une limite de taille de ligne, ou dois-tu t'arranger pour que
ca grossisse au vol ?
Avatar
Benoit Izac
Bonjour,

le 31/07/2010 à 16:47, Marc Espie a écrit dans le message
<i31d1k$17hk$ :

Concernant la solution au programme qui utilise la libarchive, je l'ai
trouvée avant de poster ce message. Ce qui m'ennuie c'est que le code
ressemble à ça :
{
while (archive_read_next_header(a, &ae) == ARCHIVE_OK) {
[...]
j = 0;
while ((r = archive_read_data(a, buf, bs))) {



Apres avoir zieute un oeil a libarchive, j'ai l'impression
qu'archive_read_data
n'est pas le "meilleur" niveau pour t'interfacer avec celle-ci.

En particulier, tu te bouffes des copies en trop, et tu n'as pas de
facon de connaitre la taille "naturelle" des blocs lus sur ton
archive.

Par contre, la doc est un peu pourrie



Je ne te le fais pas dire. Je n'ai pas regardé les sources mais la
lecture de archive.h est pratiquement indispensable.

, et l'utilisation precise de archive_read_data_block() a l'air un peu
space, si on regarde par exemple archive_read_data() lui-meme (on peut
pas faire la meme chose dans du code utilisateur, vu qu'il y a plein de
trucs internes.

L'utilisation dans archive_read_data_into_fd est bien plus claire.



Pas pour moi. J'ai bidouillé un peu avec mais je vois pas ce que je
dois lui passer comme fd. J'ai essayé divers trucs avec dup() sans
résultat (chaque lecture se retrouve affichée sur mon term).

En resume, tu peux laisser libarchive gerer son propre tampon.
Il te suffit de declarer quelques variables:

const void *buff;
size_t size;
off_t offset;

et tu peux appeler en boucle:
archive_read_data_block(a, &buff, &size, &offset) == ARCHIVE_OK

ca te positionne buff, size, et offset correctement.

Plus precisement: ca te dit que buff[ 0... size-1] contient les octets
"suivants" dans ton archive, en commencant a la position offset
(offset peut etre plus grand que la derniere valeur connue,
typiquement si tu as des trous dans ton archive).



Donc dans mon cas, une archive de type tar.gz, lorsque le fichier
m'intéresse, je fais un
archive_read_data_block(a, &buff, &size, &offset)
et je me retrouve avec le contenu du fichier en mémoire entre
(buff+offset) et (buff+offset+size-1), c'est bien ça ?

j = 0;
for (i = offset; i < (offset+size); i++) {
if (buff[i] != 'n') {
on_verifie_que_la_taille_de_la_ligne_est_suffisante();
line[j++] = buff[i++];
continue;
}
line[j] = 0;
j = 0;
faire_mon_traitement(line);
}
Ici, j'ai parcouru tout le fichier ?
Dois-je libérer la mémoire ensuite ?

Note que tu n'as pas le droit de modifier le contenu de buff en place,
comme indique par le const.

Tu peux assez facilement t'en servir pour tenir a jour ta ligne
courante, en t'evitant une copie.

Les questions correspondantes:
- que dois-tu faire si tu rencontres un bloc de 0 ? est-ce que la
reponse "erreur" est acceptable, ou est-ce que tu veux faire plus
precis.



Je le zap, et je passe au suivant;

- as-tu une limite de taille de ligne, ou dois-tu t'arranger pour que
ca grossisse au vol ?



Je l'ai fixée à LINE_MAX (de limits.h) soit au minimum 2048 selon POSIX.
Je détecte si ça dépasse mais j'ai la quasi-certitude que ça n'arrivera
pas sauf si l'archive est corrompue.

J'ai essayé des versions avec malloc/realloc mais je trouve que c'est
beaucoup de code (gestion d'erreur, libération) pour pas grand chose. Un
char line[LINE_MAX]; est quand même beaucoup plus simple et n'occupe au
final pas grand chose.

--
Benoit Izac
Avatar
espie
In article ,
Benoit Izac wrote:
Bonjour,

le 31/07/2010 à 16:47, Marc Espie a écrit dans le message
<i31d1k$17hk$ :

Concernant la solution au programme qui utilise la libarchive, je l'ai
trouvée avant de poster ce message. Ce qui m'ennuie c'est que le code
ressemble à ça :
{
while (archive_read_next_header(a, &ae) == ARCHIVE_OK) {
[...]
j = 0;
while ((r = archive_read_data(a, buf, bs))) {



Apres avoir zieute un oeil a libarchive, j'ai l'impression
qu'archive_read_data
n'est pas le "meilleur" niveau pour t'interfacer avec celle-ci.

En particulier, tu te bouffes des copies en trop, et tu n'as pas de
facon de connaitre la taille "naturelle" des blocs lus sur ton
archive.

Par contre, la doc est un peu pourrie



Je ne te le fais pas dire. Je n'ai pas regardé les sources mais la
lecture de archive.h est pratiquement indispensable.

, et l'utilisation precise de archive_read_data_block() a l'air un peu
space, si on regarde par exemple archive_read_data() lui-meme (on peut
pas faire la meme chose dans du code utilisateur, vu qu'il y a plein de
trucs internes.

L'utilisation dans archive_read_data_into_fd est bien plus claire.



Pas pour moi. J'ai bidouillé un peu avec mais je vois pas ce que je
dois lui passer comme fd. J'ai essayé divers trucs avec dup() sans
résultat (chaque lecture se retrouve affichée sur mon term).



Je te parlais de regarder le *source* qui donne une utilisation
d'archive_read_data_block.

En resume, tu peux laisser libarchive gerer son propre tampon.
Il te suffit de declarer quelques variables:

const void *buff;
size_t size;
off_t offset;

et tu peux appeler en boucle:
archive_read_data_block(a, &buff, &size, &offset) == ARCHIVE_OK

ca te positionne buff, size, et offset correctement.

Plus precisement: ca te dit que buff[ 0... size-1] contient les octets
"suivants" dans ton archive, en commencant a la position offset
(offset peut etre plus grand que la derniere valeur connue,
typiquement si tu as des trous dans ton archive).



Donc dans mon cas, une archive de type tar.gz, lorsque le fichier
m'intéresse, je fais un
archive_read_data_block(a, &buff, &size, &offset)
et je me retrouve avec le contenu du fichier en mémoire entre
(buff+offset) et (buff+offset+size-1), c'est bien ça ?



Non.
Tu te retrouves avec un bout de contenu de fichier entre
buff et buff+size-1 (inclus).

offset te dit ou ca demarre dans le fichier -> d'un appel a l'autre,
offset progresse naturellement de size, sauf si tu as une archive
"a trou", auquel cas il progressera de plus, parce qu'il y a un bloc
de zeros au milieu.

Ici, j'ai parcouru tout le fichier ?
Dois-je libérer la mémoire ensuite ?



De quelle memoire parles-tu ? si c'est ta ligne, c'est toi qui geres (et si
c'est un tampon local, pas de souci).
Si c'est buff, c'est clairement gere par libarchive elle-meme...
Avatar
Samuel DEVULDER
Marc Espie a écrit :
In article <4c542a31$0$24090$,
Samuel DEVULDER wrote:
Beaucoup oublient que parfois un bout de code ecrit sur le NG n'est pas
le code reel mais qu'il n'est la que pour fixer les idées. Dans la vraie
vie les codes C sont beaucoup plus gros que les 10-15 lignes qu'on ne
peut décemment poster sur une newsgroup et il est normal voir obligé de
shunter des détails important pour l'appli complète (includes par
exemple) mais pas pour le pb spécifique qui coince.



Question de point de vue. Moi, en pratique, les problemes que je
rencontre dans du code C se focalisent tres souvent sur quelques erreurs
courantes. Typiquement quand je corrige du code soit-disant "portable"
pour le faire marcher.

A force de passer du temps sur des milliers de lignes pour toujours
(ou presque) converger sur les memes erreurs classiques, bizarrement, tu
n'as plus du tout envie de laisser passer ces erreurs classiques.



C'est pas faux effectivement, ca dépend de la nature des fonction que
l'on code. Si on écrit un truc de traitement numérique pour dalle TV, un
décodeur DVB ou un os temps reel on a pas les mêmes soucis avec le C que
si on fait des utilitaires portables entre OS et hardware. C'est presque
pas la même informatique en fait. En tout cas c'est pas complètement les
mêmes soucis.

- tu as toi-meme tes marottes.



hélas oui. Et je ne parle pas des mar(m)ottes, c'est un autre sujet ;-)

Pourquoi es-tu venu parler de reentrance et
de static dans ce thread ?



C'est l'un de mes pbs du moment justement.

tu crois que c'etait reellement au centre du
probleme pose ? (m'etonne d'ailleurs que tu n'aies pas encore commente sur
la suggestion d'utiliser strtok...)



Bof.. la gestion des chaines n'est pas mon passe temps favori à vrai
dire. Au sujet de strtok et de re-entrance, lire:
http://publib.boulder.ibm.com/infocenter/aix/v6r1/index.jsp?topic=/com.ibm.aix.genprogc/doc/genprogc/writing_reentrant_thread_safe_code.htm

strtok maintient un buffer interne, c'est evidemment pas super. Autant
utiliser strtok_r ou re-écrire cette fonction soi-même (c'est pas
difficile) et utiliser une structure passée en argument pour y stocker
les choses persistantes.

Je pense que vu la tournure de l'informatique actuelle, il va falloir
apprendre à programmer thread-safe de plus en plus souvent (fini les
Ghz, bonjour les multi-coeurs).

- il y a tellement peu de trafic sur les newsgroups francophones ces
temps-ci qu'il est parfaitement possible de suivre tous les messages,
digressions comprises. Et lire en diagonale quand ca n'interesse plus est
parfaitement possible.



oui probablement. En tout cas c'est parfois hyper trop chaud sur
certains trucs pas important à mes yeux et carrément pas détaillé sur
les trucs qui le mériteraient. Mais bon.. évidement ce qui intéresse les
uns ennuie profondément les autres. Il faut trouver un équilibre pour
convenir au max de personnes.

sam.
Avatar
Mickaël Wolff
Le 30/07/2010 22:05, Alexandre Bacquart a écrit :
...n'est qu'une vulgaire blague pour n'importe qui a déjà mis les pieds
dans une bibliothèque graphique. Toutes celles que je connais ont la
convention d'ordre x,y



man getmaxyx (curses)
De toute façon, je n'ai jamais compris pourquoi en programmation
système les gens s'évertuent à utiliser x,y dans un contexte qui n'est
pas le système orthonormé qu'on apprend à l'école. Ça implique des
gotchas énormes quand on écrit dans un canvas graphique (je ne m'y suis
jamais fait, la première des choses que je fais dans un tel contexte,
c'est de créer une couche de conversion ou de translation).

--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org
Avatar
espie
In article <4c544fa1$0$17434$,
Samuel DEVULDER wrote:
strtok maintient un buffer interne, c'est evidemment pas super. Autant
utiliser strtok_r ou re-écrire cette fonction soi-même (c'est pas
difficile) et utiliser une structure passée en argument pour y stocker
les choses persistantes.

Je pense que vu la tournure de l'informatique actuelle, il va falloir
apprendre à programmer thread-safe de plus en plus souvent (fini les
Ghz, bonjour les multi-coeurs).



Vu que le programmeur moyen ne sait meme pas programmer sans bugs sur
du mono-thread, c'est pas gagne.

Que ce soit la gestion des semaphores ou l'apprentissage des algo
"lockless", c'est de la vraie informatique.

Et on a de moins en moins de vrais informaticiens.

Mais Joel Spolski a pondu le texte ultime sur le sujet:

http://www.joelonsoftware.com/articles/ThePerilsofJavaSchools.html
Avatar
Benoit Izac
Bonjour,

le 31/07/2010 à 17:49, Marc Espie a écrit dans le message
<i31glo$1buq$ :

Donc dans mon cas, une archive de type tar.gz, lorsque le fichier
m'intéresse, je fais un
archive_read_data_block(a, &buff, &size, &offset)
et je me retrouve avec le contenu du fichier en mémoire entre
(buff+offset) et (buff+offset+size-1), c'est bien ça ?



Non.
Tu te retrouves avec un bout de contenu de fichier entre
buff et buff+size-1 (inclus).

offset te dit ou ca demarre dans le fichier -> d'un appel a l'autre,
offset progresse naturellement de size, sauf si tu as une archive
"a trou", auquel cas il progressera de plus, parce qu'il y a un bloc
de zeros au milieu.



J'ai fait quelques tests et offset est différent de zéro lorsque le
buffer était trop petit pour contenir tout le fichier (peut-être qu'il
y a un trou au milieu, je ne sais pas le voir). Donc si il est
égal à 0 : début d'un fichier sinon toujours dans le même fichier.
Mais en fait, je n'ai pas à m'occuper d'offset puisque la fonction me
renvoie ARCHIVE_EOF à la fin du fichier.

Ici, j'ai parcouru tout le fichier ?
Dois-je libérer la mémoire ensuite ?



De quelle memoire parles-tu ? si c'est ta ligne, c'est toi qui geres (et si
c'est un tampon local, pas de souci).
Si c'est buff, c'est clairement gere par libarchive elle-meme...



Oui, je parle de ce qui est en mémoire auquel j'accède via buff[i], tout
ceci est libéré à quel moment ? À chaque appel de
archive_read_data_block() puis lorsque je fais archive_read_finish() ?

--
Benoit Izac
Avatar
xtof pernod
Le 31/07/2010 12:40, Marc Espie a fait rien qu'à écrire:
In article <4c53e956$0$10221$,
xtof pernod wrote:
Je dirais que c'est au programmeur. L'appli, elle, est programmée pour en
avoir rien à foutre. Ce qu'elle fait avec le plus grand bonheur..

mais a l'utilisateur. Un utilisateur



Euh.. Si tu peux parler à tes utilisateurs de perm. en octal ,
pas de doute, c'est de la balle^W^W^W. l'idéal.
Mais c'est plutôt rare dans le cas général...



J'ai pas besoin de lui en parler, c'est la partie "generale" du systeme
qui positionne les choses pour lui. Si un systeme Unix loggue tes
utilisateurs avec un umask 0 par defaut, il est completement buggue.



Je connais pas de tels Unices. En même temps, je n'ai pas la science
totale.


normal aura 022 comme umask. S'il a autre chose, c'est son probleme, et
c'est pas a ton programme de decider a sa place !





Bon.. On va peut-être pas trétrasectionner pour 022 euroctals, si ?
Ou alors au comptoir. fu2 pré-postionné, il y a plein de gens inspirés.



Je risque pas de te suivre la-bas, ca fait jamais qu'une 2e erreur, hein,
Les API dont on parle sont POSIX, donc *Unix* en general.



Bon, t'as raison -- complètement. Tu risques trop gros, sur ce coup-là.

(snip)
On est dans un groupe technique, on y parle de choses techniques. Tu
ecris des trucs qui sont des erreurs graves de programmation systeme.
J'essaie de t'expliquer pourquoi gentiement, mais tu ne veux pas comprendre.



Hein ?!


Je le redis donc avec plus de conviction:



Comment est-ce seulement possible..

ouvrir un fichier en ecriture dans
un programme Unix, ca se fait le plus souvent avec 0666. Utiliser autre
chose sans raison particuliere *liee au programme* est une erreur, provenant
le plus souvent d'une profonde incomprehension des regles et usages lies
a open(2) et umask(2).



T'as bien raison, j'avoue, j'y pane que pouic à tout ce bazard octal.
Heureusement, tu es là, et tu réponds merveilleusement au problème
posé par l'OP.

Juste, pourquoi t'as pas pris la peine de lui répondre ?

--
christophe.
Avatar
espie
In article ,
Benoit Izac wrote:
J'ai fait quelques tests et offset est différent de zéro lorsque le
buffer était trop petit pour contenir tout le fichier (peut-être qu'il
y a un trou au milieu, je ne sais pas le voir). Donc si il est
égal à 0 : début d'un fichier sinon toujours dans le même fichier.
Mais en fait, je n'ai pas à m'occuper d'offset puisque la fonction me
renvoie ARCHIVE_EOF à la fin du fichier.



Essaie d'archiver avec tar un gros blocs de zeros, tu devrais voir un trou
si tout va bien.

Tu doit t'occuper d'offset de facon minimale, ou sinon, tu risques de ne pas
voir ce genre de probleme.

C'est tres con a faire. Suffit de mettre qq chose genre

predict_offset = 0;

while (archive_read_data_block( ... &size, &offset, ) == ARCHIVE_OK) {
if (offset == predict_offset) {
/* tout va bien */
} else {
/* tiens, un trou */
}

predict_offset = offset + size;
}
2 3 4 5 6