[fsstream]

Le
hackervalley
Bonjour,
je code un petit example de partitionneur/ reconstituant de fichiers.

Mais j'ai des pertes d'octets lors de ces manipulations. Faut - il que
je considère en mode binaire le EOF ?

Si quelqu'un peut m'orienter ?

Merci

Voici le code, pour illustrer.

A++

http://hackervalley.free.fr

[split]

#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <string>

using namespace std;

int main (int argc, char **argv)
{
string filename;

// If there is no command-line filename, have the user enter one
if(argc < 2)
{
cout << "Enter a filename: ";
cin >> filename;
cout << "Filename is: " << filename << endl;
cout << endl;
}
else
{
filename = argv[1];
}

ifstream infile;
infile.open(filename.c_str(), ios::in|ios::binary);

if(infile.good() == false)
{
cerr << "Error opening file: " << filename << endl;
return 1;
}

// Determine length of file
long beginPos, endPos, lengthFile;

infile.seekg(0,ios::beg);
beginPos = infile.tellg();
cout << beginPos << endl;
infile.seekg(0,ios::end);
endPos = infile.tellg();
cout << endPos << endl;

lengthFile = endPos - beginPos;

cout << "size of " << filename;
cout << " is " << lengthFile << " bytes." << endl;
cout << endl;

infile.close();

// Set number of parts of file and offset
unsigned int part;
long offset;

offset = 0;

do {
part = 1;
cout << "Enter Number of parts into file is split (Enter 1 to exit)
[1-99] : ";
cin >> part;
}
while (part < 1 || part > 99);
if ( part == 1 ) return 1;
cout << "Number of parts is: " << setfill('0') << setw(2) << part << endl;
offset = lengthFile / part;
cout << "Length part of file is: " << offset << " bytes." << endl;
cout << endl;

// Create parts of file
stringstream ss;
string partfilename;

beginPos = 0;
endPos = 0;

for (unsigned int counter = 0; counter < part; counter++)
{
ss.clear();
ss << filename << setfill('0') << setw(2) << counter + 1.0 ;
ss >> partfilename;
cout << "Create " << partfilename;

ofstream outfile;
outfile.open(partfilename.c_str(), ios::binary);
outfile.seekp(0,ios::beg);

char * buffer;
long size;

// Prevent to increase size of file with last buffer
if ((beginPos + offset) > lengthFile) endPos = lengthFile;
else endPos = beginPos + offset;

size = endPos - beginPos;

cout << " beginPos : " << beginPos ;
cout << " endPos : " << endPos << endl;

infile.open(filename.c_str(),ios::binary);
infile.seekg(beginPos,ios::beg);

buffer = new char [size];
infile.read (buffer,size);
outfile.write (buffer,size);

delete[] buffer;

outfile.close();
infile.close();

beginPos = ++endPos;

}

cout << "End of fltkSplit" << endl;
return 0;
}


[Merge]

#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <string>

using namespace std;

int main (int argc, char **argv)
{
string baseFilename;

// If there is no command-line filename, have the user enter one
if(argc < 2)
{
cout << "Enter base of split filename ( foo for foo01): ";
cin >> baseFilename;
cout << "BaseFilename is: " << baseFilename << endl;
cout << endl;
}
else
{
baseFilename = argv[1];
}

unsigned int part;

do {
part = 1;
cout << "Enter Number of parts file is split (Enter 1 to exit) [1-99] : ";
cin >> part;
}
while (part < 1 || part > 99);
if ( part == 1 ) return 1;

// Test all part filename
stringstream ss;
string partfilename;

for (unsigned int counter = 0; counter < part; counter++)
{
ss.clear();
ss << baseFilename << setfill('0') << setw(2) << counter + 1.0 ;
ss >> partfilename;

ifstream infile(partfilename.c_str());

if(infile.good() == false)
{
cerr << "Error opening file: " << partfilename << endl;
return 1;
}
else
cout << "Test " << partfilename << " OK." << endl;

infile.close();
}

// Merge split filename

long beginPos, endPos;

beginPos =0;
endPos = 0;

for (unsigned int counter = 0; counter < part; counter++)
{
ss.clear();
ss << baseFilename << setfill('0') << setw(2) << counter + 1.0 ;
ss >> partfilename;

cout << "Read " << partfilename << " OK." << endl;

ifstream infile;

infile.open(partfilename.c_str(), ios::binary);
infile.seekg(0,ios::beg);

char * buffer;
long size;

infile.seekg(0,ios::end);
endPos = infile.tellg();
cout << endPos << endl;

size = endPos;

infile.seekg(0,ios::beg);

buffer = new char [size];
infile.read (buffer,size);

ofstream outfile;
// ios::app open for writing,add to end of file(append).
outfile.open(baseFilename.c_str(), ios::app | ios::binary);
beginPos = outfile.tellp();
cout << beginPos << endl;

outfile.write (buffer,size);

cout << "Merge " << partfilename << " OK." << endl;

delete[] buffer;

outfile.close();
infile.close();
}

cout << "End of fltkMerge" << endl;
return 0;
}
Vos réponses Page 1 / 2
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
James Kanze
Le #229042
hackervalley wrote:

je code un petit example de partitionneur/ reconstituant de
fichiers.


Mais j'ai des pertes d'octets lors de ces manipulations. Faut
- il que je considère en mode binaire le EOF ?


Certainement. Que le fichier soit binaire ou non, il a bien une
fin.

Si quelqu'un peut m'orienter ?


Voici le code, pour illustrer.


J'en vois quelque problèmes potentiels.

[split]


#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <string>


Sans parler de <istream> et de <ostream>.

using namespace std;


int main (int argc, char **argv)
{
string filename;


// If there is no command-line filename, have the user enter one
if(argc < 2)
{
cout << "Enter a filename: ";
cin >> filename;
cout << "Filename is: " << filename << endl;
cout << endl;
}
else
{
filename = argv[1];
}


ifstream infile;
infile.open(filename.c_str(), ios::in|ios::binary);


if(infile.good() == false)


if ( ! infile ) ...

serait plus idiomatique. (En fait, si le fichier est vide, le
resultat de infile.good() n'est pas précisé.)

{
cerr << "Error opening file: " << filename << endl;
return 1;
}


// Determine length of file
long beginPos, endPos, lengthFile;


Ici commence mes doutes. Qu'est-ce qui te fait penser que la
taille d'un fichier peut se tenir sur un long. (Ce n'est le cas
sur aucun système que moi, je connais. Mais peut-être
Windows...)

Mais enfin, ça ne doit poser de problème en fait que si tu as
des fichiers plus grand que 2Go.

infile.seekg(0,ios::beg);


Tu y est déjà. Pas besoin d'un seek.

beginPos = infile.tellg();


Ici, c'est plus grave. Le type renvoyait par tellg() n'est pas
un type numérique. Selon la norme, il n'y a même pas de garantie
qu'on peut le convertir en type numérique. C'est donc possible
que cette ligne ne passe même pas le compilateur.

Même si la conversion est légale, rien ne prouve que la valeur
numérique qu'on en obtient signifie quelque chose.

En fait, dans toutes les implémentations Unix et Windows que je
connais, ça va marcher. Mais si on veut restreindre la
portabilité, dans toutes les implémentations que je connais où
ça marche, le résultat est 0.

cout << beginPos << endl;
infile.seekg(0,ios::end);
endPos = infile.tellg();


Sous Solaris, j'y aurais -1 si le fichier a plus de 4 Go. Et une
valeur négative si le fichier a entre 2 Go et 4 Go.

cout << endPos << endl;


lengthFile = endPos - beginPos;


Ici surtout, tu suppose que les « étiquettes » de position
correspond au nombre d'octets. Rien ne l'impose, et il existe
bien des implémentations où ce n'est pas le cas.

Mais enfin, sous Windows ou sous Unix, pour des fichiers pas
trop grands... Seulement, si tu n'insistes pas sur la
portabilité, il existe une fonction stat sous les deux (ou
peut-être _stat ou __stat sous Windows) qui donne le résultat
que tu veux, correctement, et sans tout ces soucis. Quand on
n'est pas portable, ce n'est pas la peine d'en donner l'air.

(Note bien que je ne critique pas la manque de portabilité en
soi. Tous les programmes n'ont pas besoin d'être 100% portables,
et j'ai prèsque toujours des choses non portable dans les
applications que j'écris.)

cout << "size of " << filename;
cout << " is " << lengthFile << " bytes." << endl;
cout << endl;


infile.close();


Pourqoui ? On ne va plus le lire.

// Set number of parts of file and offset
unsigned int part;
long offset;


offset = 0;


do {
part = 1;
cout << "Enter Number of parts into file is split (Enter 1 to exit)
[1-99] : ";
cin >> part;
}
while (part < 1 || part > 99);


C'est un peu dure pour le type qui entre une valeur invalide.
Une boucle sans fin.

if ( part == 1 ) return 1;
cout << "Number of parts is: " << setfill('0') << setw(2) << part <<
endl;
offset = lengthFile / part;


Je crois qu'il y a un véritable problème ici. La division des
entiers tronque. Donc, si j'ai un fichier qui fait 109 octets,
et je dis que je veux 10 parties, chaque partie va faire 10
octets. Et les 9 derniers octets seront perdus. (En supposant
que offset doit signifier la longueur de chaque partie.
Personellement, j'aurais choisi un nom plutôt comme
subfile_length.)

cout << "Length part of file is: " << offset << " bytes." << endl;
cout << endl;


// Create parts of file
stringstream ss;
string partfilename;


beginPos = 0;
endPos = 0;


for (unsigned int counter = 0; counter < part; counter++)
{
ss.clear();


Franchement, j'aurais défini ss ici, pour en avoir une nouvelle
instance à chaque passage dans la boucle. C'est la façon la plus
sûre d'avoir un objet dans son état initial.

Et même, je me démande si tu sais ce que signifie ios::clear().
Il remet à zéro l'état d'erreur, c'est tout.

ss << filename << setfill('0') << setw(2) << counter + 1.0 ;


Pourquoi ajout d'une valeur flottante ? Est-ce que tu veux
réelement que le nom de fichier soit en format flottant ?

ss >> partfilename;
cout << "Create " << partfilename;


Pourquoi faire simple quand on peut faire compliqué, n'est-ce
pas ?

std::ostringstream partfilename ;
partfilename.fill( '0' ) ;
partfilename << filename << std::setw( 2 ) << counter ;

ofstream outfile;
outfile.open(partfilename.c_str(), ios::binary);


Et tu ne teste pas si ça a réussi ?

outfile.seekp(0,ios::beg);


Encore, pas besoin de seekp.

char * buffer;
long size;


// Prevent to increase size of file with last buffer
if ((beginPos + offset) > lengthFile) endPos = lengthFile;
else endPos = beginPos + offset;


endPos = std::min( beginPos + offset, lengthFile ) ;

?

C'est la façon idiomatique, je crois, de calculer une valeur
bornée.

size = endPos - beginPos;


cout << " beginPos : " << beginPos ;
cout << " endPos : " << endPos << endl;


infile.open(filename.c_str(),ios::binary);
infile.seekg(beginPos,ios::beg);


Personnelement...

J'aurais laissé infile ouvert. J'aurais simplement fait un
seekg() au début après avoir détermié la longueur. Et une fois
la longueur déterminée, je n'en aurais lu autant chaque fois
dans la boucle, sans m'occuper du positionnement. Jusqu'à la fin
de fichier, sans faire d'autres calculs.

buffer = new char [size];


Aussi, j'aurais fait simplement :
std::vector< char > buffer( size ) ;

Sans souci de delete à la fin de la boucle. (Mais ça, c'est
moins évident, et je pourrais bien t'excuser de ne pas avoir su
que les éléments d'un std::vector sont garanti contigus. Ça ne
me serait jamais venu à l'esprit non plus en lisant la norme.)

infile.read (buffer,size);
outfile.write (buffer,size);


Personellement, je me serais plutôt servi de quelque chose
comme :

infile.read( &buffer[ 0 ], buffer.size() ) ) ;
outfile.write( &buffer[ 0 ], infile.gcount() ) ;

delete[] buffer;


Pas nécessaire avec std::vector. Mais voir ci-dessus.

outfile.close();
infile.close();


beginPos = ++endPos;


beginPos += size ;

? Et il n'y a pas besoin d'un endPos de tout.

}


cout << "End of fltkSplit" << endl;
return 0;
}


En fait, je me serais plutôt servi des streambuf derrière des
iostream ; l'interface des istream et des ostream est
extrèmement mal conçue pour des entrées/sorties binaires. Ce qui
donnerait. Ce qui donnerait quelque chose du genre :

long size = getFileSize( filename ) ;
int segmentCount = getSegmentCount() ;
long segmentSize = size / segmentCount
+ (size % segmentCount != 0) ;
std::vector< char > buffer( segmentSize ) ;

filebuf in( filename.c_str(), ios::in | ios::binary ) ;
streamsize lu = in.sgetn( &buffer[ 0 ], buffer.size() ) ;
int segmentId = 0 ;
while ( lu != 0 ) {
++ segmentId ;
ostringstream segmentName ;
segmentName.fill( '0' ) ;
segmentName << filename << setw( 2 ) << segmentId ;
filebuf out( segmentName.c_str(),
ios::out | ios::binary ) ;
if ( ! out.is_open() ) {
// Erreur fatale, n'a pas pu créer segmentName.
}
if ( out.sputn( &buffer[ 0 ], lu ) != lu
|| ! out.close() ) {
// Erreur fatale, erreur d'écriture.
}
}

Je n'ai pas le temps de faire un analyse pareil sur merge, mais
tu dois avoir déjà pas mal de choses à faire.

--
James Kanze home: www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34

drkm
Le #229038
James Kanze writes:

long segmentSize = size / segmentCount
+ (size % segmentCount != 0) ;


Tiens. C'est idiomatique, ça, la conversion de booléen en entier ?
Personnellement, j'aurais tendance à expliciter au moyen de
l'opérateur ternaire.

--drkm

Horst Kraemer
Le #228936
drkm
James Kanze writes:

long segmentSize = size / segmentCount
+ (size % segmentCount != 0) ;


Tiens. C'est idiomatique, ça, la conversion de booléen en entier ?
Personnellement, j'aurais tendance à expliciter au moyen de
l'opérateur ternaire.


C'est une question de religion. Pour beaucoup de programmeurs qui ont
leurs racines dans C c'est idiomatique parce que TRUE|FALSE y sont
représentés littéralement par 1|0 et la conversion automatique
true->1|false->0 existe en C++ justement pour des raisons de
compatibilité avec C. (C++ is a better C ;-)

Enfin, c'est une question de gout. Quand tu utilises l'opérateur
ternaire il faut introduire des expressions redondantes.

Ou bien

long segmentSize = size % Count ? size / Count + 1:
size / Count;

ou bien

long segmentSize = size / Count + size % Count ? 1 : 0;


--
Horst

--
Lâche pas la patate!


Fabien LE LEZ
Le #228935
On Sun, 16 Jan 2005 10:37:39 +0100, Horst Kraemer

Quand tu utilises l'opérateur
ternaire il faut introduire des expressions redondantes.
[...]

long segmentSize = size / Count + size % Count ? 1 : 0;


Ben... C'est, il me semble, tout aussi court (peu redondant) que

long segmentSize = size / segmentCount
+ (size % segmentCount != 0) ;



PS : perso, je rajouterais une ou deux parenthèses, pour améliorer la
lisibilité de ta formule.

--
;-)


drkm
Le #228932
Fabien LE LEZ
On Sun, 16 Jan 2005 10:37:39 +0100, Horst Kraemer

Quand tu utilises l'opérateur
ternaire il faut introduire des expressions redondantes.
[...]

long segmentSize = size / Count + size % Count ? 1 : 0;


Ben... C'est, il me semble, tout aussi court (peu redondant) que

long segmentSize = size / segmentCount
+ (size % segmentCount != 0) ;




En fait, dans le premier cas, on profite de la conversion implicite
entier -> booléen, mais on est explicite sur la valeur de
l'expression. Dans le second, on doit explicitement convertir le test
en un booléen, et on profite de sa conversion implicite vers un
entier.

Mais ce à quoi je pensais était même :

long segmentSize = size / Count + ( size % Count != 0 ? 1 : 0 ) ;

où l'on voit au premier coup d'oeil, même endormi, que l'on réalise un
test d'égalité avec 0, dont dépend le fait d'ajouter l'entier 1 ou 0.

--drkm



Matthieu Moy
Le #228883
drkm
long segmentSize = size / Count + ( size % Count != 0 ? 1 : 0 ) ;


A vrai dire, j'aurais même écrit

long segmentSize = size / Count;
if (size % Count != 0) {
segmentSize++;
}

--
Matthieu

Michel Michaud
Le #228879
Dans le message ,
drkm
long segmentSize = size / Count + ( size % Count != 0 ? 1 : 0
) ;


A vrai dire, j'aurais même écrit

long segmentSize = size / Count;
if (size % Count != 0) {
segmentSize++;
}


Vous aimez faire dans le compliqué... Pourquoi pas ceci :

long segmentSize = (size+Count-1) / Count;

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/


hackervalley
Le #229669
Bonjour à tous,

James Kanze wrote:

Je n'ai pas le temps de faire un analyse pareil sur merge, mais
tu dois avoir déjà pas mal de choses à faire.


Effectivement, j'ai pas mal de choses à comprendre.

La proposition d'utiliser la class filebuf conjointement avec la
container vector est beaucoup plus élégante.

Mais je ne suis pas arrivé jusqu'à ce point.

Je me suis attardé sur la détermination de la longueur d'un fichier.

L'indication d'utilisation de stat est une piste.
En regardant boost/filesystem/operation.. il y a la fonction file_size

BOOST_FILESYSTEM_DECL boost::intmax_t file_size( const path & ph )
{
# ifdef BOOST_POSIX
struct stat path_stat;
if ( ::stat( ph.string().c_str(), &path_stat ) != 0 )
boost::throw_exception( filesystem_error(
"boost::filesystem::file_size",
ph, fs::detail::system_error_code() ) );
if ( S_ISDIR( path_stat.st_mode ) )
boost::throw_exception( filesystem_error(
"boost::filesystem::file_size",
ph, "invalid: is a directory",
is_directory_error ) );
return static_cast<boost::intmax_t>(path_stat.st_size);
# else
// by now, intmax_t is 64-bits on all Windows compilers
WIN32_FILE_ATTRIBUTE_DATA fad;
if ( !::GetFileAttributesExA( ph.string().c_str(),
::GetFileExInfoStandard, &fad ) )
boost::throw_exception( filesystem_error(
"boost::filesystem::file_size",
ph, fs::detail::system_error_code() ) );
if ( (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) !=0 )
boost::throw_exception( filesystem_error(
"boost::filesystem::file_size",
ph, "invalid: is a directory",
is_directory_error ) );
return (static_cast<boost::intmax_t>(fad.nFileSizeHigh)
<< (sizeof(fad.nFileSizeLow)*8))
+ fad.nFileSizeLow;
# endif
}

La portabilité est assuré en prenant les cas POSIX et windows. Mais,
pour mon utilisation , j'ai essayé de touver une autre façon de
déterminer la taille d'un fichier.
je suis arrivé à ce code :

#include <iostream>
#include <fstream>
#include <string>
#include <vector>

using namespace std;

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

string filename;

cout << "Enter a filename: ";
cin >> filename;
cout << "Filename is: " << filename << endl;
cout << endl;

ifstream infile;
infile.open(filename.c_str(), ios::binary);

if(!infile)
{
cerr << "Error opening file: " << filename << endl;
return 1;
}

vector< char > buffer;

char c;

while (infile.get(c))
{
buffer.push_back(c);
}

// Determine length of file
cout << "Number of characters " << buffer.size() << endl;
cout << "char is " << sizeof(char) << " bytes " << endl;
cout << "Size of " << filename << " is " <<
buffer.size()*sizeof(char) << " bytes." << endl;

infile.close();

return 0;
}

Voilà, hormis l'utilisation processeur puisque je n'utilise pas de
buffer , cette façon me donne enfin la bonne taille des fichiers.

J'ai essayé d'imaginer comment utiliser un buffer, mais j'arrive pas à
faire la même chose, et le code devient beaucoup trop complexe pour la
fonctionnalité.

Je pense utiliser le buffer pour réaliser, le split des fichiers de sortie.

A bientôt, lorsque j'aurai assimilé le reste.

http://hackervalley.free.fr

Gabriel Dos Reis
Le #229657
"Michel Michaud"
| Dans le message ,
| > drkm | >
| >> long segmentSize = size / Count + ( size % Count != 0 ? 1 : 0
| >> ) ;
| >
| > A vrai dire, j'aurais même écrit
| >
| > long segmentSize = size / Count;
| > if (size % Count != 0) {
| > segmentSize++;
| > }
|
| Vous aimez faire dans le compliqué... Pourquoi pas ceci :
|
| long segmentSize = (size+Count-1) / Count;

Il manque un commentaire ;-)

-- Gaby
Gabriel Dos Reis
Le #229656
hackervalley

[...]

| vector< char > buffer;
|
| char c;
|
| while (infile.get(c))
| {
| buffer.push_back(c);
| }


Donc, tu veux exploser la mémoire de la mchine avant de donner la
réponse ?

-- Gaby
Publicité
Poster une réponse
Anonyme