Précédent   Forum du club des développeurs et IT Pro > C et C++ > Outils pour C & C++ > C++Builder
C++Builder Environnement de développement RAD C++Builder. Avant de poster -> F.A.Q C++Builder, Sources C++Builder
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse
 
Outils de la discussion
Publicité
'
Vieux 27/11/2012, 17h42   #1
devroot
Membre du Club
 
Inscription : février 2007
Messages : 227
Détails du profil
Informations forums :
Inscription : février 2007
Messages : 227
Points : 47
Points : 47
Par défaut TFileStream et valeur retournée

Bonjour à tous !

J'essaie de lire un gros fichier par blocs.
N'ayant pas compris le FileMapping, j'essaie plutôt avec des classes trouvées dans C++Builder : TFileStream et TMemoryStream.

J'ai un doute quant au fonctionnement de la méthode CopyFrom qui devrait me permettre
de copier des blocs (60000 bytes dans l'exemple ci-bas). En effet, dès que le dernier bloc est lu et copié vers un TMemoryStream - parceque celui-ci ne mesure pas 60000 bytes - ça provoque un "Stream Read Error" que je ne peux même pas contourner avec un try / catch ?!?!?

Si vous avez des suggestions...

Voir le code source suivant :

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 
TBytes myBytes;
 
std::auto_ptr<TFileStream> LFileStream(new TFileStream(fileName.c_str(), fmOpenRead));
std::auto_ptr<TBytesStream> myBytesStream(new TBytesStream(myBytes));
 
long res=1;
	while(res>0) {
	try{
		res = myBytesStream->CopyFrom(LFileStream.get(), 60000);
		}
 
	 catch(char *excp){
 
	 	}
	}
devroot est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 27/11/2012, 19h24   #2
DjmSoftware
Rédacteur/Modérateur
 
Avatar de DjmSoftware
 
Homme
Responsable de projet fonctionnel
Inscription : mars 2002
Messages : 633
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : Suisse

Informations professionnelles :
Activité : Responsable de projet fonctionnel
Secteur : High Tech - Opérateur de télécommunications

Informations forums :
Inscription : mars 2002
Messages : 633
Points : 1 522
Points : 1 522
Salut
pourquoi utilise tu une taille fixe de 60000
il serait beaucoup plus simple de calculer à partir de la taille de ton fichier un nombre constant de blocs

ex d'algo :si ton fichier fait 142000 octets tu obtiens
2 blocs de 60000 et un bloc 22000

cdlt
__________________
A lire : Les règles du forum
DjmSoftware est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/11/2012, 09h28   #3
Argol_Medusa
Membre actif
 
Avatar de Argol_Medusa
 
Homme Yann
Ingénieur Radiofréquences
Inscription : août 2005
Messages : 160
Détails du profil
Informations personnelles :
Nom : Homme Yann
Localisation : France

Informations professionnelles :
Activité : Ingénieur Radiofréquences
Secteur : High Tech - Électronique et micro-électronique

Informations forums :
Inscription : août 2005
Messages : 160
Points : 152
Points : 152
Envoyer un message via MSN à Argol_Medusa
Concrètement, que veux-tu faire exactement en fait ?
Gros fichier c'est quelle taille ?

Voici une manière possible d'utiliser des MemoryStream pour lire un gros fichier :

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
TMemoryStream *a;
a = new TMemoryStream();
a->LoadFromFile("c:\\test.exe");
 
TMemoryStream *Destination;
Destination = new TMemoryStream();
 
unsigned long int taille=456;
Destination->CopyFrom(a,taille);
Destination->Seek(0,0);
 
// faire ce que l'on veut ici avec la memory stream de Destination
 
delete a;
delete Destination;
a=NULL;
Destination=NULL;
__________________
Désolé, on savait pas que c'était impossible, alors on l'a fait
Argol_Medusa est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/11/2012, 11h44   #4
ShaiLeTroll
Expert Confirmé Sénior
 
Avatar de ShaiLeTroll
 
Homme
Développeur C++\Delphi
Inscription : juillet 2006
Messages : 9 261
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 32
Localisation : France

Informations professionnelles :
Activité : Développeur C++\Delphi
Secteur : High Tech - Électronique et micro-électronique

Informations forums :
Inscription : juillet 2006
Messages : 9 261
Points : 13 373
Points : 13 373
Si c'est une Exception Delphi, c'est plutôt cela le catch

Code :
1
2
catch(Exception *e) 
catch(const Exception &e)
get() c'est lié à auto_ptr ?

Evite le c_str(), la VCL gère des AnsiString, String, UnicodeString naturellement, c_str() doit être utilisé uniquement si l'on a explicitement besoin d'un char* pour une API Windows par exemple

Utilise plutôt des String que des std::string en C++Builder
Si fileName est String, ceci est suffisant ... new TFileStream(fileName, fmOpenRead));.


Sinon, il faut savoir que c'est le comportement par défaut de LoadFromFile avec TStream, il charge le fichier par bloc comme je l'ai expliqué dans le sujet TMemoryStream : incompréhension

Citation:
Envoyé par ShaiLeTroll Voir le message
Pour LoadFromStream, tu peux effectivement copier un TFileStream vers un TMemoryStream, cela utilise CopyFrom en interne
Pour LoadFromFile, c'est ni plus ni moins qu'un wrapper de TFileStream\LoadFromStream
CopyFrom utilise un tampon de 60Kio pour la méthode abstraite Read() et boucle jusqu'à ce que la quantitée de donnée restant à copier atteignent zéro
Si tu veux copier plusieurs Gio cela fera via quelques milliers d'itérations de 60Kio


Si au final, tout est copié dans le TMemoryStream, je ne vois pas l'intérêt de le faire à la main !

L'intérêt de lire par bloc, c'est d'effectuer une manipulation des données à chaque itération, sans avoir en mémoire l'ensemble du fichier


@Argol, tu charges entièrement le fichier mémoire avec ton code pour au final ne lire que 456 octets, pour un réel chargement partiel

Code :
1
2
3
4
5
6
7
8
9
10
11
12
TFileStream *FS = new TFileStream ("c:\\test.exe", fmOpenRead);
try
{
  Byte Buffer[456];  
  FS->ReadBuffer(Buffer, sizeof(Buffer));    
 
  // faire ce que l'on veut ici avec la memory Buffer
}
__finally
{
  delete FS ;
}
Et si l'on absolument ton continué avec un Stream

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
TFileStream *FS = new TFileStream("c:\\test.exe", fmOpenRead);
try
{
  TMemoryStream *MS = new TMemoryStream();
  try
  {
     MS->CopyFrom(Buffer, 456); // va appeler Read et lire ce qui est nécessaire   
     MS->Seek(0,0);
     // faire ce que l'on veut ici avec la memory stream de Destination
 
 
  }
  __finally
  {
    delete MS ;
  }
}
__finally
{
  delete FS ;
}
__________________
Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
Attention Troll Méchant !
"Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
L'ignorance n'excuse pas la médiocrité !

L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

Halte à la ségrégation des Cinémas, VO sur Paris, VF en Banlieue, Abonnement résilié !
ShaiLeTroll est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/11/2012, 13h03   #5
devroot
Membre du Club
 
Inscription : février 2007
Messages : 227
Détails du profil
Informations forums :
Inscription : février 2007
Messages : 227
Points : 47
Points : 47
Merci Argol Medusa et ShaiLeTroll.

J'imprime vos suggestions pour les étudier.

En fait, mon souci c'est que là où TStringList->LoadFromFile fonctionne parfaitement pour de petits fichiers sans que j'aie à me soucier de leur encodage, dès lors que le fichier en question est plus gros (en l'occurrence 500G d'UTF-8) ça marche plus aussi bien.

Donc je lorgne du coté du file mapping puis des TStream qui me semblent plus simples à saisir.

En gros j'essaie d'écrire un TStringList->LoadFromFile... par blocs.
...et entre chaque lecture, je tokenize le bloc en mots.
devroot est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/11/2012, 13h15   #6
Argol_Medusa
Membre actif
 
Avatar de Argol_Medusa
 
Homme Yann
Ingénieur Radiofréquences
Inscription : août 2005
Messages : 160
Détails du profil
Informations personnelles :
Nom : Homme Yann
Localisation : France

Informations professionnelles :
Activité : Ingénieur Radiofréquences
Secteur : High Tech - Électronique et micro-électronique

Informations forums :
Inscription : août 2005
Messages : 160
Points : 152
Points : 152
Envoyer un message via MSN à Argol_Medusa
Cela rejoint notre discussion d'il y a quelques semaines (dont tu as mis le lien,Shaï).

Effectivement c'est une bonne remarque, l'emploie d'un TFileStream semble plus judicieux ici (sauf cas particuliers, suivant ce que tu vas en faire une fois en mémoire ).

Par contre ça te bloque peut-être le fichier en ouverture ( à vérifier, c'est même pas sur suivant comment ça a été codé ), donc prudence sur ce point dans le cas d'accès multiples.

Sinon oui lire le gros fichier pour seulement 300 octets, il faut qu'il y ait un intérêt, d'où ma question "qu'est-ce que tu veux faire exactement avec ça?".

Si tu dois pouvoir lire/écrire n'importe quel bloc du fichier à n'importe quel moment pour application temps réel (vidéo/audio/base de donnée importante), alors oui ça a un intérêt.

Sinon, le TFileStream ira très bien.
__________________
Désolé, on savait pas que c'était impossible, alors on l'a fait
Argol_Medusa est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/11/2012, 13h30   #7
devroot
Membre du Club
 
Inscription : février 2007
Messages : 227
Détails du profil
Informations forums :
Inscription : février 2007
Messages : 227
Points : 47
Points : 47
J'essaie donc d'appliquer vos recettes mais je n'arrive pas à intercepter l'erreur du CopyFrom malgrè le try / catch.
Ci-dessous,pour un fichier de taille 129000, la boucle while tourne deux fois puis l'impossibilité de lire 60000 bytes une troisième fois provoque une erreur de type
"class EReadError with message Stream read error".

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
 
	unsigned long int taille=60000;
 
   bool cont=true;
   while(cont){
	try{
		Destination->CopyFrom(a, taille);
		Destination->Seek(0,0);
		}
 
	catch(const Exception &e){
		 cont=false;
	}
	}
devroot est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/11/2012, 13h32   #8
Argol_Medusa
Membre actif
 
Avatar de Argol_Medusa
 
Homme Yann
Ingénieur Radiofréquences
Inscription : août 2005
Messages : 160
Détails du profil
Informations personnelles :
Nom : Homme Yann
Localisation : France

Informations professionnelles :
Activité : Ingénieur Radiofréquences
Secteur : High Tech - Électronique et micro-électronique

Informations forums :
Inscription : août 2005
Messages : 160
Points : 152
Points : 152
Envoyer un message via MSN à Argol_Medusa
Oups, j'étais en train d'écrire quand tu as posté.
Je vois mieux l'application maintenant.

Tu as 500 Go d'UTF-8 ... hé bé ... moi qui croyait avoir de gros fichier avec mes 10Go !

A priori il est clair que dans ton cas il ne faut pas essayer de travailler en un seul fichier en mémoire, tu vas exploser tes TStringList ou TMemoryStream.

Utiliser des TFileStream pour charger morceau par morceau et copier dans des TStringList me semble cohérent.

Cependant, tu peux essayer séparer ton fichier de 500Go en plusieurs fichiers et/ou compresser en zip ( le texte se compresse TRES bien, tu pourrais facilement passer à 20 Go ), et de le décompresser en live en faisant une classe genre "TSpecialeFileStream", c'est ce que j'ai fais il y a quelques semaines ).
Ca permettra de gagner en place si le temps de traitement n'est pas critique ( utilisation d'une TZipForge par exemple )
__________________
Désolé, on savait pas que c'était impossible, alors on l'a fait
Argol_Medusa est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/11/2012, 15h01   #9
ShaiLeTroll
Expert Confirmé Sénior
 
Avatar de ShaiLeTroll
 
Homme
Développeur C++\Delphi
Inscription : juillet 2006
Messages : 9 261
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 32
Localisation : France

Informations professionnelles :
Activité : Développeur C++\Delphi
Secteur : High Tech - Électronique et micro-électronique

Informations forums :
Inscription : juillet 2006
Messages : 9 261
Points : 13 373
Points : 13 373
Moi qui pensait que l'on était limité à 40Go pour un fichier !

Pourquoi utiliser CopyFrom ?
Si tu veux traiter bloc par bloc inutile de copier dans un autre stream pour au final tout accumuler en mémoire (ce que fait LoadFromFile pour info)

Code :
1
2
3
4
5
6
7
8
9
10
11
std::auto_ptr<TFileStream> LFileStream(new TFileStream(fileName.c_str(), fmOpenRead));
 
  do 
  {
    Byte Buffer[65536];  
    RealRead = FS->Read(Buffer, sizeof(Buffer));    
    for(int i = 0; i < RealRead; i++)
    {
      Traitement de Buffer[i]
    }
  } while (RealRead > 0);
__________________
Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
Attention Troll Méchant !
"Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
L'ignorance n'excuse pas la médiocrité !

L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

Halte à la ségrégation des Cinémas, VO sur Paris, VF en Banlieue, Abonnement résilié !
ShaiLeTroll est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 29/11/2012, 19h13   #10
devroot
Membre du Club
 
Inscription : février 2007
Messages : 227
Détails du profil
Informations forums :
Inscription : février 2007
Messages : 227
Points : 47
Points : 47
Merci Shai LeTroll,

Ton exemple fonctionne parfaitement bien et me convient puisque la valeur retour realread
me permet de garder un oeil sur le nombre de caractères lus effectivement à chaque passage dans la boucle.

Aussi, il n'y a plus d'erreur de lecture dans le stream !

Reste le problème de la conversion de bytes à string (problème que TStringList->LoadFromFile gère automatiquement).

Si je dois décoder l'UTF-8 à la main, je sais que je dois "sauter" les 3 caractères de BOM
au début du fichier puis décoder chaque caractère pour ensuite tokenizer la chaîne.

Vous me conseillez de transformer les bytes en UnicodeString, wstring, etc. ?
Merci encore !
devroot est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 30/11/2012, 10h02   #11
ShaiLeTroll
Expert Confirmé Sénior
 
Avatar de ShaiLeTroll
 
Homme
Développeur C++\Delphi
Inscription : juillet 2006
Messages : 9 261
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 32
Localisation : France

Informations professionnelles :
Activité : Développeur C++\Delphi
Secteur : High Tech - Électronique et micro-électronique

Informations forums :
Inscription : juillet 2006
Messages : 9 261
Points : 13 373
Points : 13 373
Que contient de ton fichier, est-ce QUE du texte ?
Si UTF8, en buffer, faudra penser à gérer un caractère dont le 1er octet à la fin d'un buffer et le 2nd ou 3eme un début du buffer suivant !

Ton fichier fera vraiment 500 Go ?
500Go = 5 000 000 000 o
500 Gio = 5 368 709 120 o

Les fonctions UTF8Encode et UT8Decode sont prévu pour cela !
Tu as d'autres en XE2, je n'ai pas le nom en tête, je les ai cité dans un autre sujet
__________________
Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
Attention Troll Méchant !
"Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
L'ignorance n'excuse pas la médiocrité !

L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

Halte à la ségrégation des Cinémas, VO sur Paris, VF en Banlieue, Abonnement résilié !
ShaiLeTroll est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 30/11/2012, 14h31   #12
devroot
Membre du Club
 
Inscription : février 2007
Messages : 227
Détails du profil
Informations forums :
Inscription : février 2007
Messages : 227
Points : 47
Points : 47
Merci ShaiLeTroll !

Oui, oui, le fichier en entrée fait bien 500G (actuellement il mesure 210G de texte pur mais croît régulièrement).

Aussi, il faut effectivement traiter les caractères à 2/3 bytes qui seraient tronqués par la taille du buffer de lecture.
J'avais déjà écrit une fonction de lecture de gros fichiers (ANSI/ASCII) qui gère bien le
problème du tronquage de mots (voir fonction ci-dessous).

Je vais donc essayer de faire qqc de semblable pour les caractères UTF8 à moins que je trouve une fonction XE2. Je crois avoir vu des choses adéquates ici.

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
 
 
int TForm1::readBigFile()
{
	char buffer[BUF_SIZE];
	long offset, blocksread=0, charsread;
	std::streampos fpos;
 
	ifstream myFile (fileName.c_str(), ios::in | ios::binary);
 
	while (!myFile.eof()) {
 
		   myFile.read (buffer, BUF_SIZE);
		   charsread = myFile.gcount();
		   fpos = myFile.tellg();
 
		   // Buffer was filled with characters
		   if(charsread == BUF_SIZE){
 
				offset=1;
 
				// make sure buffer doesn't truncate a token (move back in buffer until first delimiter)
				if(!isDelimiter(buffer[myFile.gcount()-offset])){
					while(myFile.gcount()>=offset && !isDelimiter(buffer[myFile.gcount()-offset]))
						offset++;
					}
 
				// make sure buffer ends with whole word (move back in buffer to first non-delimiter character)
				if(isDelimiter(buffer[myFile.gcount()-offset])){
					while(myFile.gcount()>=offset && isDelimiter(buffer[myFile.gcount()-offset]))
						offset++;
					offset--;   	// Preserve last non-delimiter character
					}
 
				// exclude (erase) all characters not to be read
				for(long i=BUF_SIZE-offset; i<BUF_SIZE;i++)
					buffer[i]='\0';
 
				fpos = fpos -offset;
				myFile.seekg(fpos);
				}
 
			// Buffer was not filled to the maximum (end of file)
			else{
				for(long i=myFile.gcount(); i<BUF_SIZE; i++)
					buffer[i]='\0';
				}
 
	}
   myFile.close();
}
devroot est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse
Outils de la discussion

Navigation rapide


Fuseau horaire GMT +2. Il est actuellement 22h45.


 
 
 
 
Partenaires

Hébergement Web