Skip to content

Fichiers Binaires – différence C++ / Fortran

13 décembre 2009

On m’a donné récemment un programme en FORTRAN, qui écrit des bases de données mathématiques dans des fichiers binaires. La génération de ces bases sont très longues, donc le code est parallélisé. Or sur le cluster sur lequel je travaille, les codes parallèles doivent être en C/C++(pour le moment). Je ne connais pas trop le FORTRAN, mais travaille un peu en C++, je décide donc de traduire le code qui est simple (mais coûteux).

Je lance ce code en local en FORTRAN pour générer une base et pour avoir un exemple de fichier pour être sûr que j’ai bien traduit le code. Horreur , malheur, quoique je fasse mes fichiers binaires sont différents!! J’écris dans la console les nombres qui sont mis dans ces fichiers binaires par les deux programmes. Ce sont les mêmes. Je vérifie que le float C++ et le real FORTRAN sont bien codées sur quatre octets, c’est le cas. Quel peut être cette différence. Je fais un programme en C++ qui lit les deux fichiers, la base en FORTRAN m’affiche n’importe quoi. Je commence mes recherche et trouve que FORTRAN ajoute des paramètres de contrôle dans ces fichiers binaires. Mais je n’en apprends pas plus.

Sur un autre site, il donne le fait que ces paramètres de contrôle font 4 octets. Je mets donc dans mon code C++ de lecture des décalage de 4 octets au début et à la fin de chaque lecture de nombre. Je retrouve certains des bons chiffres mais pas tous et uils sont décalés. Cette explication n’est pas convenable. Finalement, je trouve un explication plus complète ici.

Je vais donner des explications telles que je les comprends. Ça peut toujours aider.

Écrire un fichier binaire en C++, c’est très simple à comprendre. Le fichier recevra exactement ce qu’il lui a été envoyé tout mis bout-à-bout. Si j’écris un float, suivi d’un double. Mon fichier contient le code du float et du double collé soit 4 octet et 8 octet collé. Le fichier fera donc 12 octet. Le même fichier en ASCII sera plus lourd et donc plus long à lire et à écrire. Pour des codes nécessitant beaucoup d’écritures et lectures, le binaire a un net avantage. Par contre côté lecture par un homme, ceci devient impossible. De plus, en binaire, il faut mettre les données avec un ordre précis. Lorsque je relis le fichier que j’ai écrit précédemment, il faut bien que je lise un float puis un double. Car si je lit un double puis un float, je vais lire n’importe quoi.

Pour écrire et lire en binaire en C++ :

#include <fstream>
#include <iostream>

using namespace std;

int main( int argc, char *argv[] ) {

float a = 1.2;

double b = 12.3;

// ECRITURE DE a et b RESPECTIVEMENT float ET double

ofstream sortie (« toto_bin », ios::out | ios::binary);

sortie.write((char *)&a, sizeof(float));

sortie.write((char *)&b, sizeof(double));

sortie.close();

a = 0;

b = 0;

// LECTURE DE a et b RESPECTIVEMENT float ET double

ifstream entree (« toto_bin », ios::out | ios::binary);

entree.read((char *)&a, sizeof(float));

entree.read((char *)&b, sizeof(double));

entree.close();

// AFFICHAGE

cout <<  » a  =  » << a <<  »  et b =  » << b << endl;

}

Maintenant, comment fonctionne le binaire en FORTRAN?

Le fichier binaire en FORTRAN a le même principe de base que le fichier en C/C++. Seulement, comme dit plus haut, il rajoute des balises de contrôles. Ces balises de contrôle sont placées entre chaque paquets de nombres écrit par la commande write. La commande write peut écrire plusieurs nombres d’un coup donc ces nombres de contrôle ne seront pas placés entre chaque nombre écrit comme il est indiqué sur certains sites (que je remercie tout de même, car c’est ces sites qui m’ont montrés par où chercher, quand je m’arrachais les cheveux).

Maintenant que contienne ses « balises de contrôle »?

L’entête et la queue sont identiques. elles contiennent chacune le nombre 0 et x, où x est le nombre d’octets que fait la séquence à lire. Si la commande write va écrire 20 float. La séquence sera :

0 80 float1 float2 …. float20 0 80

L’entête et la queue font chacun 4 octets, contenant 0 pour la taille de départ et 80 pour la taille d’arriver (20 float à 4 octets font 80 octets). [NB: Le float en FORTRAN se déclare real.]  Sur certaines machines (pas toujours 32 et 64 bits comme évoqué sur le lien que je propose, il y a apparemment des exceptions) l’entête et la queue sont codés sur 8 octets. Dans le cas des machines à 4 octets (32 bits sauf exceptions), il s’agit probablement de unsigned short (et donc de unsigned int pour les 64 bits).

Le FORTRAN rajoute donc une couche de complexification, puisque pour relire un fichier binaire il ne s’agit plus seulement de connaître l’ordre de ses données mais aussi la manière dont elle ont été écrites. Il faut écrire la séquence de lecture exactement comme la séquence d’écriture.

Faisons un exemple en FORTRAN, on va écrire 1 float a et un tableau de 2 doubles b().

implicit none

real a  = 10

double precision b(2)

data b / 12.3d0,14.535d0

! On écrit a et b

open(unit=11,file=’toto_bin’,form=’unformatted’)

write(11)(a)                             ! On écrit a

write(11)(b(k),k=1,2)               ! On ecrit b(k) pour k allant de 1 à 2

close(11)


a = 0

data b /  0d0, 0d0

! On lit a et b exactement comme on les a écrit

open(unit=12,file=’toto_bin’,form=’unformatted’)

read(11)(a)                             ! On lit a

read(11)(b(k),k=1,2)               ! On lit b(k) pour k allant de 1 à 2

close(12)

write(*,*)  » a = « , a

write (*,*)  » b = « , b(1), « ; », « b(2)

end

La séquence écrite dans le programme est :

0 4 a 0 4 0 16 b(1) b(2) 0 16

Là où le même code en C++ aurait écrit :

a b(1) b(2)

Le FORTRAN prend 36 Octets, le C++ prend 20 Octets.


Dans la partie relecture, on n’aurait pas pu faire comme suit :

! On lit a et b exactement comme on les a écrit

open(unit=12,file=’toto_bin’,form=’unformatted’)

read(11)(a,k=1,2*nb_h_enr)    ! On lit a

read(11)(b(1))               ! On lit b(1)

read(11)(b(2))               ! On lit b(2)

close(12)

Les séquences de contrôle ne le permette pas. Il aurait fallut pour ça, faire trois write et nous aurions obtenus la séquence suivante :

0 4 a 0 4 0 8 b(1) 0 8 0 8 b(2) 0 8

Cette séquence prend 44 Octets. On se rend bien compte que plus on appelle write plus le fichier s’alourdit comparativement au binaire C++.

De plus, si on veut faire un code qui génère des binaires lisibles pour des codes FORTRAN ainsi que C++, on est obligé de faire des binaires FORTRAN qui prennent en compte la manière dont le code va écrire le fichier ou le lire en FORTRAN.

Il existe cependant des librairies qui permette d’écrire des binaires qui sont indépendants du type de machine et d’OS et probablement du type de langage choisi. Je n’ai pas vérifier pour le langage.

Maintenant, si je veux relire le fichier binaire écrite en FORTRAN avec du C++ correspondant à la séquence suivante :

0 4 a 0 4 0 16 b(1) b(2) 0 16

Il faut le code suivant :

#include <fstream>
#include <iostream>

using namespace std;

const int RECORD_DELIMITER_LENGTH = 4;

int main( int argc, char *argv[] ) {

float a;

double b[2];

// LECTURE DE a et b RESPECTIVEMENT float ET double

ifstream entree (« toto_bin », ios::out | ios::binary);

entree.seekg(RECORD_DELIMITER_LENGTH, ios::cur); // on zappe l’entête de a

entree.read((char *)&a, sizeof(float));

entree.seekg(RECORD_DELIMITER_LENGTH, ios::cur); // queue de a

entree.seekg(RECORD_DELIMITER_LENGTH, ios::cur); //entête de b

entree.read((char *)&b[1], sizeof(double));

entree.read((char *)&b[2], sizeof(double));

entree.seekg(RECORD_DELIMITER_LENGTH, ios::cur); // queue de b, sert uniquement à atteindre EOF (End Of File)

entree.close();

// AFFICHAGE

cout <<  » a  =  » << a <<  »  et b =  » << b[1] << « , » << b[2] << endl;

}

// ECRITURE DE a et b RESPECTIVEMENT float ET double

ofstream sortie (« toto_bin », ios::out | ios::binary);

sortie.write((char *)&a, sizeof(float));

sortie.write((char *)&b, sizeof(double));

sortie.close();

No comments yet

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

%d blogueurs aiment cette page :