|
Publicité ' | |||||||||||||||||||||||
|
|
#1 | ||
|
Membre du Club
![]() Inscription : février 2007 Messages : 227 ![]() |
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 :
|
||
|
|
00
|
|
|
#2 |
![]() ![]() Responsable de projet fonctionnel Inscription : mars 2002 Messages : 633 ![]() |
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 |
|
|
00
|
|
|
#3 | ||
|
Membre actif
![]() |
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 :
__________________
Désolé, on savait pas que c'était impossible, alors on l'a fait
|
||
|
|
00
|
|
|
#4 | |||||||
|
Expert Confirmé Sénior
![]() Développeur C++\Delphi Inscription : juillet 2006 Messages : 9 261 ![]() |
Si c'est une Exception Delphi, c'est plutôt cela le catch
Code :
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:
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 :
Code :
__________________
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é ! |
|||||||
|
|
00
|
|
|
#5 |
|
Membre du Club
![]() Inscription : février 2007 Messages : 227 ![]() |
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. |
|
|
00
|
|
|
#6 |
|
Membre actif
![]() |
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
|
|
|
00
|
|
|
#7 | ||
|
Membre du Club
![]() Inscription : février 2007 Messages : 227 ![]() |
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 :
|
||
|
|
00
|
|
|
#8 |
|
Membre actif
![]() |
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
|
|
|
00
|
|
|
#9 | ||
|
Expert Confirmé Sénior
![]() Développeur C++\Delphi Inscription : juillet 2006 Messages : 9 261 ![]() |
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 :
__________________
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é ! |
||
|
|
00
|
|
|
#10 |
|
Membre du Club
![]() Inscription : février 2007 Messages : 227 ![]() |
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 ! |
|
|
00
|
|
|
#11 |
|
Expert Confirmé Sénior
![]() Développeur C++\Delphi Inscription : juillet 2006 Messages : 9 261 ![]() |
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é ! |
|
|
00
|
|
|
#12 | ||
|
Membre du Club
![]() Inscription : février 2007 Messages : 227 ![]() |
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 :
|
||
|
|
00
|
Copyright © 2000-2013 - www.developpez.com