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

[fsstream]

18 réponses
Avatar
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;
}

8 réponses

1 2
Avatar
hackervalley
Gabriel Dos Reis wrote:

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

-- Gaby


Non, bien sur. Mais comme je travaille sur des buffers pour découper des
fichiers , j'utilise aussi fortement la mémoire de la machine. Donc je
me suis dis que cela ne pose pas de problème. Mais en testant avec un
fichier de 650Mo, effectivement le programme ne me donne pas le résultat.

En conclusion, l'utilisation de buffer sur de gros fichiers est interdite ?

Là, je vois plus trop quelle méthode utiliser pour non plus seulement
obtenir la taille d'un fichier mais aussi le subdiviser en plusieurs.

A++

http://hackervalley.free.fr

Avatar
Gabriel Dos Reis
hackervalley writes:

| Gabriel Dos Reis wrote:
|
| > Donc, tu veux exploser la mémoire de la mchine avant de donner la
| > réponse ?
| > -- Gaby
|
| Non, bien sur. Mais comme je travaille sur des buffers pour découper des
| fichiers , j'utilise aussi fortement la mémoire de la machine. Donc je
| me suis dis que cela ne pose pas de problème. Mais en testant avec un
| fichier de 650Mo, effectivement le programme ne me donne pas le
| résultat.
|
| En conclusion, l'utilisation de buffer sur de gros fichiers est interdite ?

Je ne sais pas. Mais ce que je sais, c'est que ta stratégie ne fait
pas une bonne utilisation des resources. Pourquoi les solutions
existantes ne te conviennent pas ?

-- Gaby
Avatar
kanze
drkm wrote:
James Kanze writes:

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


Tiens. C'est idiomatique, ça, la conversion de booléen en
entier ?


Non. Je m'en suis servi ici uniquement pour faire plus court,
dans la mésure où l'expression était sécondaire à ce que
j'expliquais. Je ne m'en servirais jamais dans du code
production.

Personnellement, j'aurais tendance à expliciter au moyen de
l'opérateur ternaire.


Tout à fait. Ce qui aurait fait ici une ligne trop longue pour
mon lecteur de news:-).

--
James Kanze GABI Software http://www.gabi-soft.fr
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
kanze
Michel Michaud wrote:
Dans le message ,
drkm writes:

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




Juste pour être clair : c'est celle-ci qui me sert d'habitude.
Sauf avec la règle que les ?: sont toujours réparti sur trois
lignes, ce qui donnerait quelque chose comme :

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

Mais la solution avec l'if me paraît aussi acceptable. Et je ne
râlerai pas sur la solution avec conversion implicite de bool,
même si j'imagine que la plupart des programmeurs auraient
besoin de y régarder deux fois.

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

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



Ce que je trouve bien aussi.

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

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


C'est la solution des vieux de l'assembleur. Dans le temps, je
m'en servais beaucoup, surtout quand count était une puissance
de deux (et l'arithmétique ce faisait au coup de décalage).

Mais j'imagine qu'il y aura encore moins de programmeurs à le
comprendre au premier coup d'oeil que la solution avec la
conversion implicite bool->int.

--
James Kanze GABI Software http://www.gabi-soft.fr
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
kanze
hackervalley wrote:

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.


Plus qu'élégant. J'ai cherché un moment à trouver une bonne
solution avec istream. Je n'en ai pas réelement trouvé.

Il faut dire que les flux, en général, ne sont pas très bien
adaptés aux entrées/sorties binaires. Vue que tu postais un
programme entier, et que je n'ai pas voulu trop compliquer, j'ai
laissé à côté des aspects importants qu'il faut en général
considérer quand il s'agit de lire dans une application plus
complexe. Comme les locale, par exemple -- la lecture binaire
n'est garantie qu'en locale "C", ce qui n'est pas le locale qui
sert par défaut.

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.


Et pourrait être étendu à tout système qui supporte l'opération.

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);
}


En effet, la seule solution 100% portable pour déterminer la
taille d'un fichier, c'est de le lire, et de compter les octets.

N'empèche qu'ici, comme a dit Gaby, tu risques fort d'avoir un
bad_alloc. Après tout, quel est l'intérêt de découper les
fichiers s'ils ne sont pas très grands. (Et note bien que quand
tu remplis un vector à coup de push_back, il faut typiquement
deux ou trois fois plus de mémoire que ne ce qu'occupe la partie
utile du vector à la fin.)

Surtout que par la suite, tu vas en jeter le contenu. Si tout ce
qui t'intéresse, c'est le nombre d'octets :

char ch ;
unsigned long long count = 0 ;
while ( infile.get( ch ) ) {
++ count ;
}

Voire même :

unsigned long long count = 0 ;
while ( infile.get() != EOF ) {
++ count ;
}

Évidemment, long long n'est pas 100% portable -- j'ai du mal à
imaginer un compilateur C++ moderne qui ne le supporte pas, mais
parmi les compilateurs plus anciens, ce n'est pas si évident. En
particulier, avec VC++ 6.0, il faut plutôt __uint64, ou quelque
chose de ce genre.

Mais quoiqu'il en soit, il faut bien un type de 64 bits si tu
veux être portable.

// 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.


Tout à fait. Seulement, si tu ne les utilises pas, ce n'est
vraiment pas la peine de stocker les caractères. D'autant plus
que c'est plutôt rare des systèmes où il est garantie qu'un
fichier tient en mémoire.

Aussi, évidemment, lire un fichier entier pour obtenir une
information que le système peut te fournir directement, c'est
loin d'être une solution efficace. Que ça plaise ou non,
parfois, il faut utiliser des fonctions spécifiques au système.
(En fait, quand je lis et j'écris du binaire, je travaille
prèsque toujours avec read et write -- des fonctions Posix, non
C++.)

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.


En fait, la solution classique, c'est de démander à
l'utilisateur la taille des fichiers cibles, et non le nombre.
Comme ça, pas besoin de connaître la taille du fichier source ;
en le lit en blocs de n, en écrivant chaque bloc dans un nouveau
fichier. C'est la solution adoptée par split, par exemple ; voir
http://www.unix.org/single_unix_specification/ ou
http://www.fsf.org/software/textutils/manual/textutils/html_chapter/textuti ls_toc.html.

--
James Kanze GABI Software http://www.gabi-soft.fr
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
kanze
hackervalley wrote:
Gabriel Dos Reis wrote:

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


Non, bien sur. Mais comme je travaille sur des buffers pour
découper des fichiers , j'utilise aussi fortement la mémoire
de la machine. Donc je me suis dis que cela ne pose pas de
problème. Mais en testant avec un fichier de 650Mo,
effectivement le programme ne me donne pas le résultat.


Alors, imaginer ce qui se passera si le fichier fait des
tetraoctets (tout à fait possible sur un système moderne).

En conclusion, l'utilisation de buffer sur de gros fichiers
est interdite ?


Si la taille du fichier est plus que la taille adressable en
mémoire, absolument, dans tous les cas. Sinon, si la taille du
buffer dépasse ce qui est disponible en mémoire vive, on risque
de ramer pas mal. En général, il existe plusieurs solutions :

-- La plus évidente et la plus générale, c'est ce que fait les
filebuf déjà. On ne met qu'une partie du fichier en buffer.
Je ne sais pas la situation actuelle, mais dans le temps,
aller au délà des 16 Ko n'apportait pas grand chose.

-- Si on sait d'avance que les fichiers vont être rélativement
petits, et on veut lire la plus vite et la plus simplement
possible, sans trop s'occuper d'être portable, on utilise
mmap ou son équivalent, pour mapper le fichier directement
comme mémoire virtuelle. Du coup, l'accès au fichier se fait
au moyen des char const*, qui peuvent servir comme
itérateurs dans les algorithmes de la STL. C'est assez
proche du métal, mais c'est facile à mettre en oeuvre, et ça
va vite.

-- Sinon, il existe la possibilité de déterminer la taille d'un
fichier, puis de le lire en entier dans un std::vector ou un
std::string. C'est plus portable que la solution précédante
(mais il reste le problème de déterminer la taille), mais il
nettement plus de ressources, ce qui veut dire qu'il limite
la taille encore plus. En revanche, pour des fichiers
vraiment petits, selon la façon dont le système gère la
mémoire virtuelle, elle peut être encore plus performant.

Dans la pratique, je me sers assez peu de la dernière solution.
Elle a besoin déjà d'une requête non portable pour déterminer la
taille, et mmap est tellement plus facile à mettre en oeuvre.

Là, je vois plus trop quelle méthode utiliser pour non plus
seulement obtenir la taille d'un fichier mais aussi le
subdiviser en plusieurs.


Je ne vois pas trop de problème à utiliser un buffer de la
taille cible des fichiers. Je suppose que cette taille est
assez petite -- c'est bien le but de la division, n'est-ce pas ?
Encore que même là, on peut se poser la question. Pour
commencer, je crois que j'écrirais une simple boucle :

while ( count < size && infile.get( ch ) ) {
outfile.put( ch ) ;
++ count ;
}

A priori, filebuf fait une bufferisation, et a choisi une taille
de buffer optimal pour ton système. Je n'irais plus loin que si
je constatais des problèmes de performance, et alors, je me
pencherais probablement sur des solutions propre à la
plateforme. (Il faut dire que la portabilité n'a rarement été un
concerne dans mes applications dernièrement.)

--
James Kanze GABI Software http://www.gabi-soft.fr
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
Michel Michaud
Dans le message ,
Michel Michaud wrote:
long segmentSize = (size+Count-1) / Count;


C'est la solution des vieux de l'assembleur. Dans le temps, je
m'en servais beaucoup, surtout quand count était une puissance
de deux (et l'arithmétique ce faisait au coup de décalage).

Mais j'imagine qu'il y aura encore moins de programmeurs à le
comprendre au premier coup d'oeil que la solution avec la
conversion implicite bool->int.


Alors j'imagine qu'il serait bon d'ajouter des fonctions Round
et une sorte de ceil sur division entière, car pour moi ajouter
diviseur-1, ou l'équivalent ((s-1)/c + 1), suit le même principe
qu'ajouter 0.5 avant une conversion entière pour arrondir.

En fait, pas besoin de mettre ces fonctions en bibliothèque, il
suffit de les faire si on ne comprend pas les calculs du premier
coup d'oeil (et ça vaut pour les calculs avec ?: ou autre) :

long Round(double n)
{
return static_cast<long>(n+0.5);
}

long CeilDiv(long n, long d)
{
return (n-1)/d + 1;
}

N.B. À moins que je ne me trompe, la solution -1...+1 est
meilleure que l'autre car elle risque moins de déborder si
n est près du maximum...

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


Avatar
kanze
Michel Michaud wrote:
Dans le message
,

Michel Michaud wrote:
long segmentSize = (size+Count-1) / Count;


C'est la solution des vieux de l'assembleur. Dans le temps,
je m'en servais beaucoup, surtout quand count était une
puissance de deux (et l'arithmétique ce faisait au coup de
décalage).

Mais j'imagine qu'il y aura encore moins de programmeurs à
le comprendre au premier coup d'oeil que la solution avec la
conversion implicite bool->int.


Alors j'imagine qu'il serait bon d'ajouter des fonctions Round
et une sorte de ceil sur division entière, car pour moi
ajouter diviseur-1, ou l'équivalent ((s-1)/c + 1), suit le
même principe qu'ajouter 0.5 avant une conversion entière pour
arrondir.


En effet.

C'est marrant, mais je ne l'ai jamais considéré de ce point de
vue. Au fond, je ne suis qu'un vieux hacker, qui a appris par le
biais de l'assembleur et du hardware. Dans le temps, pour
arrondir à la puissance de deux supérieur, c'était courant
d'ajouter 2^n-1 et de masquer. Je en voyais ta technique comme
une variante.

En fait, pas besoin de mettre ces fonctions en bibliothèque,
il suffit de les faire si on ne comprend pas les calculs du
premier coup d'oeil (et ça vaut pour les calculs avec ?: ou
autre) :

long Round(double n)
{
return static_cast<long>(n+0.5);
}

long CeilDiv(long n, long d)
{
return (n-1)/d + 1;
}

N.B. À moins que je ne me trompe, la solution -1...+1 est
meilleure que l'autre car elle risque moins de déborder si
n est près du maximum...


Et a un comportement qui dépend de l'implémentation si n == 0.
(Je le dis comme ça, je ne sais pas si c'est un problème.)

--
James Kanze GABI Software http://www.gabi-soft.fr
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



1 2