Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

ecriture et lecture de std::string

33 réponses
Avatar
Marc G
je ne sais pas trop quelle méthode choisir pour écrire/lire des chaînes dans
un fichier binaire

1) La première solution :

// pour écrire
// output est un std::fstream&

std::string UneChaine="coucou";
std::size_t len=UneChaine.size();
char const* contents=UneChaine.data();
output.write((char*)&len,sizeof(std::size_t));
output.write((char*)&contents,len);

// pour lire
// input est un std::fstream&

std::size_t len;
input.read((char*)&len,sizeof(std::size_t));
char *contents=new char[len+1];;
input.read((char*)&contents,len);
contents[len]='\0';
UneChaine=std::string(contents);
delete[] contents;

je trouve ça très lourd juste pour récupérer le contenu de la string
(j'ai l'impression de réserver 2 fois inutilement l'espace pour contenir la
chaîne !)

2) la deuxième solution

output << UneChaine

input >> UneChaine

c'est-à-dire que j'écris en mode texte dans mon fichier binaire (a priori,
ça devrait pas poser de problème ?)

Quels sont vos conseils ?
Quels sont les avantages/inconvénients de chaque méthode et est-il possible
d'améliorer la première ?

Merci à vous.
Marc

10 réponses

1 2 3 4
Avatar
James Kanze
On Nov 6, 2:47 pm, Jean-Marc Bourguet wrote:
Fabien LE LEZ writes:

On Tue, 6 Nov 2007 14:18:00 +0100, "Marc G" :

char const* contents=UneChaine.data();
output.write((char*)&len,sizeof(std::size_t));


Résultat indéfini : les octets seront bien écrits, mais on
ne sait pas dans quel ordre.


J'ai l'impression que tu te trompes. Tu peux m'indiquer quel
est ton raisonnement?


Sans doute il se base sur la norme C++, qui dit que le taille
d'un type entier dépend de l'implémentation. Strictement
parlant, évidemment, c'est donc défini par l'implémentation,
mais dans la pratique : je n'ai jamais réussi à trouver la
documentation requise pour une implémentation donnée, et je
constate que ça varie d'une version à l'autre, et selon les
options de compilation, même pour une implémentation donnée.

Comme disait Fabien, il faut avoir le goût du risque. En gros,
ça marche pour des fichiers temporaires, qui sont rélus par le
même binaire exécutable. Mais dès qu'on exige que les données
soient lisibles par un autre programme, ou par le même
programme, mais après récompilation, il vaut mieux s'en passer,
et faire les choses correctement.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34



Avatar
James Kanze
On Nov 6, 3:00 pm, Fabien LE LEZ wrote:
On 06 Nov 2007 14:47:26 +0100, Jean-Marc Bourguet :

char const* contents=UneChaine.data();
output.write((char*)&len,sizeof(std::size_t));
Résultat indéfini : les octets seront bien écrits, mais on ne sa it pas

dans quel ordre.


J'ai l'impression que tu te trompes.


Avec un compilateur précis sur une plate-forme précise,


Avec une version précise d'un compilateur précis, sur une
plate-forme précise, et avec les options de compilation précise,
...

on peut savoir dans quel ordre les octets seront écrits.


Il n'y a pas que l'ordre. Il y a aussi la taille de size_t (qui
sur mes machines, dépend des options de compilation).

Et sur plate-forme i386, je serais bien tenté d'affirmer que
les octets seront toujours écrits en commençant par l'octet de
poids faible.


Le premier compilateur dont je me suis servi sur i386 écrivait
les octets dans l'ordre 2301 (ou le chiffre répresent le poids
de l'octet en base 256). Mais j'avoue que c'était un compilateur
8086, qui compilait en mode native 16 bits:-).

Par ailleurs, sizeof(std::size_t) vaudra
probablement 4 sur un système 32 bits.


En dehors de Windows, il dépend prèsque toujours d'une option de
compilation, et peut-être soit 4, soit 8. Et il est assez
courant de commencer une application en mode 32 bits, et de le
récompiler en mode 64 bits lorsque la quantité de données
augment.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34




Avatar
James Kanze
On Nov 6, 7:08 pm, Fabien LE LEZ wrote:
On Tue, 6 Nov 2007 17:10:00 +0100, "Marc G" :

Et la ça me pose un autre problème !
Par exemple j'ai écrit/lu des bool comme ça :
output.write((char*)&missing_,sizeof(missing_)); // missing_ est un bool
input.read((char*)&missing_,sizeof(missing_));
Quelle est la portabilité de ce code ?


Gloups... Je n'en ai pas la moindre idée.


C'est un entier. >= 1. C'est tout ce qu'on peut dire.

Affecte-le à un char, et écrire le char.

La gestion en mémoire du type "bool" est un peu un mystère
pour moi, et je m'en porte très bien.

D'une manière générale, cette façon de faire n'offre aucune
garantie de portabilité, sauf bien sûr pour le type char.


En fait, je dirais sauf pour le type unsigned char (et encore...
il existe au moins une machine où unsigned char a 9 bits).

Tu as pu voir qu'un format de fichier binaire est un peu
rock'n'roll à gérer. S'il n'y a pas trop de données, je
préfère largement un fichier texte ligne-par-ligne. C'est
lisible (et donc vérifiable et modifiable) directement avec un
éditeur de texte, les spécifications sont généralement plus
faciles à écrire, et le format s'avère souvent plus souple.


N'est-ce pas ? Et quand on doit passer en 64 bits, pas de
problème. Et quand on doit changer la structure (ajouter les
champs, etc.), un simple script de shell pourrait souvent faire
l'affaire.

(Le seul point où je prendrais peut-être exception, c'est les
spécifications. C'est difficile de faire plus simple que
d'écrire « le fichier est en format XDR ».)

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Marc G
Attention : tu es en train de confondre deux choses distinctes.
Que tu « écris » en mode texte ou en mode binaire dépend
uniquement de comment le fichier a été ouvert. Quand tu utilises
<< et >>, tu utilises un formattage texte pré-définit, mais si
le fichier a été ouvert en mode binaire, tu écris le résultat de
ce formattage toujours en mode binaire.

En l'occurance, les formats pré-définis ne supportent pas le
« round trip » ; les formats utilisés en sortie ne contient
pas assez d'informations pour être sûr de pouvoir retrouver les
données en entrée. (En particulier, ils ne comportent pas de
délimiteurs. Donc, si tu écris « output << i << j », ou i et j
sont des entiers, « input >> i >> j » ne te permettrait pas de
les rélire.) Le but des <<, c'est un formattage de présentation,
pour consumation humaine, et non la sérialisation.


je te remercie pour cette précision qui comble une grosse lacune de ma part.

Dans l'absence d'autres contraintes, j'aurais une tendance à
utiliser XDR -- c'est un format assez simple, mais qui a
l'avantage d'être déjà défini et spécifié, ce qui m'évite pas
mal de travail. Pour écrire en XDR, je ferais quelque chose du
genre :

oxdrstream&
oxdrstream::operator<<(
std::string const& obj )
{
assert( obj.size() <= 0xFFFFFFFF ) ;
sentry garde( *this ) ;
if ( garde ) {
*this << static_cast< uint32_t >( obj.size() ) ;
if ( obj.size() != 0 ) {
// Wraps streambuf writes with error
// checking, setting badbit if error, and
// doing nothing once error set...
GuardedSBuffer sb( rdbuf() ) ;
sb.puts( &obj[ 0 ], obj.size() ) ;
switch ( obj.size() % 4 ) {
case 1 :
sb.putc( '' ) ;
case 2 :
sb.putc( '' ) ;
case 3 :
sb.putc( '' ) ;
case 0 :
break ;
}
}
return *this ;
}

oxdrstream&
oxdrstream::operator<<(
uint32_t obj )
{
sentry garde( *this ) ;
if ( garde ) {
GuardedSBuffer sb( rdbuf() ) ;
sb.putc( obj >> 24 ) ;
sb.putc( obj >> 16 ) ;
sb.putc( obj >> 8 ) ;
sb.putc( obj ) ;
}
return *this ;
}

(Je ferais oxdrstream dériver de std::basic_ios<char>.)


euh, là c'est trop compliqué pour moi... :-)))

Avatar
Marc G
Qu'est-ce qui se passe avec ta fonction sur un systèrme 64 bits ? seuls
les
32 bits de poids fort sont utilisés pour le codage des char, c'est ça ?


Peu importe ; c'est la cuisine interne du compilo (ou plutôt du
processeur).
J'ai un entier ; j'effectue 4 fois la division euclidienne par 256, et
j'enregistre les 4 restes successifs. C'est des maths ; ça n'a rien à
voir avec l'agencement de la RAM ou autre considération matérielle.


sauf que les entiers pourraient être codés sur 64 bits, avec une valeur max
supérieure, non ?
donc je peux lire dans un sens et pas dans l'autre....

Tu as pu voir qu'un format de fichier binaire est un peu rock'n'roll à
gérer. S'il n'y a pas trop de données, je préfère largement un fichier
texte ligne-par-ligne. C'est lisible (et donc vérifiable et
modifiable) directement avec un éditeur de texte, les spécifications
sont généralement plus faciles à écrire, et le format s'avère souvent
plus souple.


mes fichier comportent surtout des nombres, donc le mode binaire s'impose.
Le programme écrit des tables de données. Mon premier soucis est donc que
mes clients ne perdent pas leurs données !
Comme je vois que les fichiers binaires sont très rock'n'roll comme tu dis,
j'ai une idée et
j'aimerais que tu me dises ce que tu en penses : j'écris en début de fichier
la taille des différents types utilisés à l'écriture/lecture...
comme ça ne risque pas de dépasser 255, je peux même l'écrire directement
sur 1 octet.
Ensuite quand je veux relire le fichier, je commence par lire la taille des
types et je m'adapte...
Qu'en penses tu ?


Voir aussi un système de sérialisation, ce qui t'évitera de faire le
boulot toi-même. Celui de Boost par exemple.
http://www.boost.org/libs/serialization/doc/serialization.html


hélas, je n'ai jamais réussi à installer boost...donc j'ai abandonné.


Avatar
Alexandre BACQUART
Marc G wrote:
mes fichier comportent surtout des nombres, donc le mode binaire s'impose.


Pourquoi ? Rien n'empêche d'écrire des nombres en texte :

--- début fichier ---
1678 2460 37479 1669 697
--- fin fichier ---

Le seul inconvénient par rapport au mode binaire, c'est la place que ça
prend, et le temps machine nécessaire pour convertir ça en type entier.

Pour la place, c'est négligeable, mais ça se discute. Cela dépend de la
base de données et aussi de ce que tu ferais en binaire. En général, si
on utilise le binaire, c'est pour concilier place et vitesse, ça suppose
donc qu'on étudie chaque champ, ses valeurs minimum et maximum, et donc
de ne pas se contenter d'écrire des int. Et pour la portabilité,
endianess, encodage et autre joyeusetés seront de la fête tôt ou tard...
tout un poême pour en tirer un avantage notable par rapport au mode texte !

Pour le temps machine sur la conversion, c'est négligeable aussi, et
même quasi-insignifiant si on lit les données sur un flux lent (à
l'échelle d'un programme C++, même les plus rapides des disque dûrs
fournissent des flux lents, une connexion réseau aussi).

Mais tout ça, à l'ère du XML/HTML à outrances avec plus de tags que de
données, c'est du pinaillage...

Dans la pratique, il n'y a en général que des avantages à utiliser le
format texte : on peut écrire et lire avec n'importe quel compilo,
n'importe quelle version, n'importe quelle machine, n'importe quel
système... c'est plus simple à utiliser, les nombres n'ont virtuellement
aucune limite en taille et précision, la base de donnée est
"human-readable" sans avoir recours à un éditeur hexa (ça c'est une
propriété exclusive du mode texte), et elle est elle-même portable (au
pire, on peut facilement convertir les 'n' si besoin après coup, le
reste ne changera pas).

Avec tous ces avantages et ces prises de tête en moins (je ne développe
pas tout !), personne ne t'en voudra de te priver d'une DB en
brut/natif/binaire.

Le programme écrit des tables de données. Mon premier soucis est donc
que mes clients ne perdent pas leurs données !


Mode texte -> universel.

Comme je vois que les fichiers binaires sont très rock'n'roll comme tu
dis, j'ai une idée et
j'aimerais que tu me dises ce que tu en penses : j'écris en début de
fichier la taille des différents types utilisés à l'écriture/lecture...
comme ça ne risque pas de dépasser 255, je peux même l'écrire
directement sur 1 octet.


La taille d'un type peut changer d'un compilo à l'autre, d'une machine à
l'autre, d'une version à l'autre,...

Ensuite quand je veux relire le fichier, je commence par lire la taille
des types et je m'adapte...
Qu'en penses tu ?


Ca finira par marcher à coup sûr tant que tu resteras sur le même
compilo, même version, même machine, même système... sinon, il n'y a
aucune garantie, à part utiliser une bibliothèque dédiée pour le binaire
ou bien avoir une expérience solide du réel problème en cause. Dans tous
les cas, ça complique le code simplement pour avoir le beurre et
l'argent du beurre, comme si la portabilité ne valait pas quelques
malheureux octets et cycles machines.


--
Alex

Avatar
Fabien LE LEZ
On Wed, 7 Nov 2007 11:00:18 +0100, "Marc G" :

sauf que les entiers pourraient être codés sur 64 bits, avec une valeur max
supérieure, non ?


La question ici était d'enregistrer la taille d'une chaîne. Est-ce
qu'une chaîne de plus de 4 milliards de caractères a un sens dans ton
programme ?
M'enfin bon, comme je l'ai dit plus haut, commence par écrire les
spécifications de ton format de fichier. Ce sont elles qui
t'indiqueront la valeur de "NB_OCTETS".

Avatar
Fabien LE LEZ
On Wed, 7 Nov 2007 10:40:35 +0100, "Marc G" :

euh, là c'est trop compliqué pour moi... :-)))


C'est pourtant plus simple que réfléchir toi-même à des
spécifications, puisqu'elles sont déjà écrites, il n'y a plus qu'à les
implémenter.

Tu comprends maintenant pourquoi je préfère un format texte ?

Avatar
Jean-Marc Bourguet
Fabien LE LEZ writes:

On Wed, 7 Nov 2007 11:00:18 +0100, "Marc G" :

sauf que les entiers pourraient être codés sur 64 bits, avec une valeur max
supérieure, non ?


La question ici était d'enregistrer la taille d'une chaîne. Est-ce
qu'une chaîne de plus de 4 milliards de caractères a un sens dans ton
programme ?
M'enfin bon, comme je l'ai dit plus haut, commence par écrire les
spécifications de ton format de fichier. Ce sont elles qui
t'indiqueront la valeur de "NB_OCTETS".


Et si des longues chaines sont possibles mais que les courtes sont
frequentes, un format binaire de taille variable peut etre utilise (genre
UTF-8, ou simplement compose d'element sur 7 bits, le 8ieme marquant le
dernier) pour eviter de perdre de la place pour les courtes (si ca risque
d'etre significatif).

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Avatar
Loïc Joly

Pour la place, c'est négligeable, mais ça se discute. Cela dépend de la
base de données et aussi de ce que tu ferais en binaire.


Si tu dois gérer des giga octets ou des terra octets de données, la
place n'est pas négligeable...

En général, si
on utilise le binaire, c'est pour concilier place et vitesse, ça suppose
donc qu'on étudie chaque champ, ses valeurs minimum et maximum, et donc
de ne pas se contenter d'écrire des int. Et pour la portabilité,
endianess, encodage et autre joyeusetés seront de la fête tôt ou tard...
tout un poême pour en tirer un avantage notable par rapport au mode texte !

Pour le temps machine sur la conversion, c'est négligeable aussi, et
même quasi-insignifiant si on lit les données sur un flux lent (à
l'échelle d'un programme C++, même les plus rapides des disque dûrs
fournissent des flux lents, une connexion réseau aussi).

Mais tout ça, à l'ère du XML/HTML à outrances avec plus de tags que de
données, c'est du pinaillage...


Je dirais plutôt qu'utiliser de l'XML là où les performances comptent
est une ânerie.


Je vois un avantage non cité à un format binaire, c'est qu'il est facile
d'avoir un jeu de donnée où la taille est homogène, ce qui permet de se
déplacer rapidement dans le fichier sans avoir à tout en lire ou à
l'indexer.

Il n'empêche que j'utilise de préférence des formats textes, mais le
binaire a quelques avantages.

--
Loïc

1 2 3 4