ReadFile // WriteFile // Taille du buffer [Résolu]

Inekman 292 Messages postés dimanche 2 février 2003Date d'inscription 30 juin 2006 Dernière intervention - 4 avril 2005 à 00:56 - Dernière réponse : Inekman 292 Messages postés dimanche 2 février 2003Date d'inscription 30 juin 2006 Dernière intervention
- 6 avril 2005 à 00:19
Salut les amis, j'ai un problème de lecture avec l'api readfile.



J'ai un buffer de 64ko qui contient ce que lit l'api sauf que quand
j'arrive à la fin du fichier que je suis en train de lire et que la
taille restante à lire est inférieure à la taille du buffer, alors
l'api ne lit rien du tout et la boucle boucle
sans fin puisque la taille lue reste toujours inférieure à la taille
totale du fichier...je sais pas si vous avez compris mais moi je sèche
:-(



buffer: array[0..65535] of char;





ZeroMemory(@buffer, sizeof(buffer));





[...]





Source := CreateFile(PChar(src), GENERIC_READ, 0, nil, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, 0);


Dest:= CreateFile(PChar(dest), GENERIC_WRITE, 0, nil, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);





[...]





alire := sizeof(buffer);


aecrire := alire;


totalecrit := 0;





while totalecrit < total do


begin


// si "alire" est inférieur à la taille du buffer,


// alors la fonction ne lit rien du tout :(





ReadFile(Source, buffer, alire, lu, nil);


WriteFile(Dest, buffer, aecrire, ecrit, nil);



totalecrit := totalecrit + ecrit;


if totalecrit + alire > total then


begin


alire := total - totalecrit ;


aecrire := alire;


end;


end;
Afficher la suite 

14 réponses

Répondre au sujet
WhiteHippo 1270 Messages postés samedi 14 août 2004Date d'inscription 5 avril 2012 Dernière intervention - 5 avril 2005 à 00:51
+3
Utile
Moi j'suis comme Kenavo, y'a quelquechose qui me plait pas non plus !!!!
C'est que tu n'utilises pas la taille du fichier !!!

Bon j'ai fait vite (j'suis peut être passé à côté de quelquechose) un bout de code (mais qui fonctionne) avec les API windows (sauf pour l'allocation du buffer) puisque cela te tenait tellement à coeur
N.B. Je sais, il n'y a pas beaucoup de commentaires mais je pense que les variables utilisées parlent d'elles mêmes.

const
TAILLE_BUFFER = 65536 ; // Ou plus pour un nombre de passes de copie inférieur


type
PBuffer = ^TBuffer ;
TBuffer = array[0..TAILLE_BUFFER-1] of char ;


procedure CopieFichier( Source, Dest : string ) ;
var
SourceHandle : THANDLE ;
DestHandle : THANDLE ;
Erreur : DWORD ;
TailleFichierSourceLow : Cardinal ;
TailleFichierSourceHigh : Cardinal ;
TailleFichierSource : int64 ;
NombreOctetsRestants : int64 ;
SourceOctetsLus : Cardinal ;
DestOctetsEcrits : Cardinal ;
SourceOctetsALire : Cardinal ;
DestOctetsAEcrire : Cardinal ;
Resultat : Boolean ;


PtrBuffer : PBuffer ;
begin
new ( PtrBuffer ) ;
if ( PtrBuffer = NIL ) then
begin
Messagedlg('Impossible d''allouer de la mémoire pour le buffer.',mtError,[mbOK],0);
Exit ;
end ;
try
// !! Attention Avec FILE_FLAG_NO_BUFFERING à la place de FILE_ATTRIBUTE_NORMAL
// ça ne fonctionne pas parce que :
// "An application must meet certain requirements when working with files
// opened with FILE_FLAG_NO_BUFFERING (c.f. Win32 Programmer's References)"
SourceHandle := CreateFile(PChar(Source), GENERIC_READ, 0, NIL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (SourceHandle = INVALID_HANDLE_VALUE) then
begin
Erreur := GetLastError() ;
Messagedlg(Format('Erreur %d à l''ouverture du fichier source %s.', [Erreur,Source]),mtError,[mbOK],0);
Exit ;
end ;
DestHandle := CreateFile(PChar(dest), GENERIC_WRITE, 0, NIL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
if (DestHandle = INVALID_HANDLE_VALUE) then
begin
// Traitement erreur : Message, exception, etc...
Erreur := GetLastError() ;
CloseHandle( SourceHandle );
Messagedlg(Format('Erreur %d à l''ouverture du fichier destination %s.', [Erreur,Dest]),mtError,[mbOK],0);
Exit ;
end ;


TailleFichierSourceLow := 0 ;
TailleFichierSourceHigh := 0 ;
TailleFichierSourceLow := GetFileSize( SourceHandle, @TailleFichierSourceHigh ) ;
if ( TailleFichierSourceLow = $FFFFFFFF ) then
begin
Erreur := GetLastError() ;
Messagedlg(Format('Erreur %d : Impossible d''obtenir la taille du fichier %s.', [Erreur,Dest]),mtError,[mbOK],0);
CloseHandle(SourceHandle);
CloseHandle(DestHandle);
Exit ;
end ;
TailleFichierSource := ( TailleFichierSourceHigh shl 32 ) + TailleFichierSourceLow ;


NombreOctetsRestants := TailleFichierSource ;
while ( NombreOctetsRestants > 0 ) do
begin
if ( NombreOctetsRestants < TAILLE_BUFFER ) then
begin
SourceOctetsALire := NombreOctetsRestants ;
DestOctetsAEcrire := NombreOctetsRestants ;
end else
begin
SourceOctetsALire := TAILLE_BUFFER ;
DestOctetsAEcrire := TAILLE_BUFFER ;
end ;


SourceOctetsLus := 0 ;
FillChar( PtrBuffer^, TAILLE_BUFFER, #0 ) ;
Resultat := ReadFile ( SourceHandle, PtrBuffer^, SourceOctetsALire, SourceOctetsLus , nil ) ;
if ( Resultat and ( SourceOctetsLus = 0 ) ) then
begin
// Fin du fichier
end else begin
if ( not Resultat ) or ( SourceOctetsLus <> SourceOctetsALire ) then
begin
Messagedlg( format( 'Erreur : Nombre d''octets lus %d différents du nombre attendu %d.'
,[SourceOctetsLus,SourceOctetsALire]),mtError,[mbOK],0);
CloseHandle(SourceHandle);
CloseHandle(DestHandle);
Exit ;
end ;
end ;


DestOctetsEcrits := 0 ;
Resultat := WriteFile ( DestHandle, PtrBuffer^, DestOctetsAEcrire, DestOctetsEcrits , nil ) ;
if ( not Resultat ) or ( DestOctetsEcrits <> DestOctetsAEcrire ) then
begin
Messagedlg( format( 'Erreur : Nombre d''octets écrits %d différents du nombre attendu %d.'
,[DestOctetsEcrits,DestOctetsAEcrire]),mtError,[mbOK],0);
CloseHandle(SourceHandle);
CloseHandle(DestHandle);
Exit ;
end ;
NombreOctetsRestants := NombreOctetsRestants - SourceOctetsALire ;
end ;
CloseHandle(SourceHandle);
CloseHandle(DestHandle);
finally
dispose( PtrBuffer );
PtrBuffer := NIL ;
end ;
end ;

Cordialement.
Cette réponse vous a-t-elle aidé ?  
Commenter la réponse de WhiteHippo
Inekman 292 Messages postés dimanche 2 février 2003Date d'inscription 30 juin 2006 Dernière intervention - 5 avril 2005 à 00:58
+3
Utile
Salut WhiteHippo,



Merci beaucoup pour ta solution, ça fait plaisir. Mais tu a répondu à mon problème en 2 lignes :-D :



// !! Attention Avec FILE_FLAG_NO_BUFFERING à la place de FILE_ATTRIBUTE_NORMAL
// ça ne fonctionne pas parce que :
// "An application must meet certain requirements when working with files
// opened with FILE_FLAG_NO_BUFFERING (c.f. Win32 Programmer's References)"



Ouai donc c'en fait 4 :-)



j'ai changé le createfile en mettant FILE_ATTRIBUTE_NORMAL et mon problème s'est résolu tout seul ;-)



et pour "
C'est que tu n'utilises pas la taille du fichier !!!", c'est ma variable "total" dans la condition de la boucle. :-P



Autre question en despi et on boucle l'affaire, est-il mieux de faire un buffer de char ou un buffer de byte ?



quoi qu'il en soit, un ENORME merci à vous trois
Cette réponse vous a-t-elle aidé ?  
Commenter la réponse de Inekman
cs_Kenavo 759 Messages postés vendredi 21 mars 2003Date d'inscription 1 octobre 2009 Dernière intervention - 4 avril 2005 à 08:29
0
Utile
Salut,



Y a quelque chose qui me plait pas dans ton algo (qui doit servir à
faire une copie de fichier, non ?). Tu n'utilises pas au mieux la
variable "lu" de la fonction ReadFile.



J'aurais écrit:



alire := sizeof(buffer);

repeat

if ReadFile(Source, buffer, alire, lu, nil) then // On essaye de lire 64K

WriteFile(Dest, buffer, Lu, ecrit, nil); // On copie ce qu'on a lu

until Lu <> ALire; // On n'a pas lu 64K : on est a la fin



Je ne sais pas si ça va régler tes problèmes ...



Ken@vo


<hr size="2" width="100%">Code, Code, Codec !
Commenter la réponse de cs_Kenavo
Inekman 292 Messages postés dimanche 2 février 2003Date d'inscription 30 juin 2006 Dernière intervention - 4 avril 2005 à 12:15
0
Utile
oui il sert à copier des données d'un fichier mais y'a juste un extrait du code ici, la partie qui pose problème ;-)



Toujours est-il que ta solution est intéressante mais on va se
retrouver avec le même problème dans la mesure où ReadFile ne lira plus
rien dès que la taille des données restantes à lire sera inférieure à
la taille du buffer :-\ donc Lu sera toujours <> de ALire et ça
bouclera sans fin



C'est ça que je comprend pas...peu importe la taille du buffer,
ReadFile devrait au moins lire la quantité de données qu'on lui demande
de lire non ? Je sais pas du tout...
Commenter la réponse de Inekman
Emandhal 199 Messages postés dimanche 2 mars 2003Date d'inscription 10 octobre 2006 Dernière intervention - 4 avril 2005 à 12:35
0
Utile
J'utiliserai FileRead plutot que ReadFile. Et FileWrite plutot que WriteFile.
Dans l'aide Delphi il est marqué :
function FileRead(Handle:Integer; varBuffer; Count:Integer): Integer;

C'est donc une fonction, ce qui est renvoyé, c'est le nombre de caractères lus, à partir de là il suffit de comparer ce que renvoie la fonction avec la taille du buffer. Si c'est différent (normalement inférieur) c'est que l'on est à la fin du fichier et après il suffit de copier que le nombre de caractères lus dans WriteFile.

FinFichier := False;
alire := SizeOf(buffer);

Repeat
SizeRead : = FileRead(Source, buffer^, alire
); // SizeRead: Integer;
If SizeRead<alire
Then FinFichier := True;
SizeWrite := FileWrite(Dest, buffer^, SizeRead); // SizeWrite: Integer; // SizeWrite permet de faire un controle de ce qui est écrit, comme ca, si SizeRead<>SizeWrite c'est qu'il y a eut un problème à l'écriture... Il suffit de rajouter le test adéquate.
Until not FinFichier;

Tout problème a sa solution... Mais en général, c'est jamais la bonne...
Commenter la réponse de Emandhal
Inekman 292 Messages postés dimanche 2 février 2003Date d'inscription 30 juin 2006 Dernière intervention - 4 avril 2005 à 13:00
0
Utile
Merci pour la proposition, je test ça ce soir même si mon ambition était de faire tout ça uniquement avec les api de windows :-(
Commenter la réponse de Inekman
WhiteHippo 1270 Messages postés samedi 14 août 2004Date d'inscription 5 avril 2012 Dernière intervention - 5 avril 2005 à 01:30
0
Utile
Ok pour la variable "total", j'suis plus très reveillé à cette heure là moi

Buffer de byte ou buffer de char ?? Dépend de l'utilisation que tu veux en faire derrière. Si c'est un fichier texte j'aurais tendance à dire buffer de char pour pouvoir exploiter les données en caractère, et si c'est un fichier binaire alors je dirais buffer de byte. Cependant, char ou byte c idem en Delphi, alors tu fais comme tu le sens...

Par contre je te conseille vivement d'augmenter la taille de ton buffer de lecture/ecriture!!!

Cordialement.
Commenter la réponse de WhiteHippo
japee 1792 Messages postés vendredi 27 décembre 2002Date d'inscription 12 novembre 2016 Dernière intervention - 5 avril 2005 à 01:47
0
Utile
Salut, les couche-tard !



Curieux, chez moi ça marche kif-kif que je mette FILE_ATTRIBUTE_NORMAL ou FILE_FLAG_NO_BUFFERING...



J'utilise quasiment le code à Kenavo, ça donne ça :



procedure CopieFichier(Src, Dst: String);

var Buffer: array[0..65535] of Char;

Lu, Ecrit: Cardinal;

hSource, hDestination: THandle; // ou Cardinal

begin

hSource := CreateFile(PChar(Src),


GENERIC_READ,


0,


nil,


OPEN_EXISTING,


FILE_FLAG_NO_BUFFERING,


//FILE_ATTRIBUTE_NORMAL,


0);

hDestination := CreateFile(PChar(Dst),


GENERIC_WRITE,


//GENERIC_READ or GENERIC_WRITE,


0,


nil,


CREATE_NEW,


FILE_ATTRIBUTE_NORMAL,


0);

repeat

if ReadFile(hSource, Buffer, SizeOf(Buffer), Lu, nil) then

WriteFile(hDestination, Buffer, Lu, Ecrit, nil);

until Lu <> SizeOf(Buffer);

CloseHandle(hSource);

CloseHandle(hDestination);

end;



Et ça marche nickel !



Bonne prog'



japee
Commenter la réponse de japee
Inekman 292 Messages postés dimanche 2 février 2003Date d'inscription 30 juin 2006 Dernière intervention - 5 avril 2005 à 12:49
0
Utile
"Par contre je te conseille vivement d'augmenter la taille de ton buffer de lecture/ecriture!!!"



Combien par exemple ? parce que j'ai aucune notion de taille de buffer optimale :-)
Commenter la réponse de Inekman
cs_Kenavo 759 Messages postés vendredi 21 mars 2003Date d'inscription 1 octobre 2009 Dernière intervention - 5 avril 2005 à 14:08
0
Utile
J'vais p'tèt dire une incongruité, mais je donne mon avis.

Il me semble bien qu'il ne doit pas être trop gros pour ne pas générer
des swaps entre RAM et fichier d'échange, ce qui nuirait à la
performance.

Je dirais aussi qu'il doit être préférable qu'il soit d'une
taille multiple de l'unité l'allocation (bon ça, ça doit pas être trop
dur, le tout c'est de choisir un nombre "rond" en hexa).

Avec tout ça je dirais un multiple de $100000 (= 1Mo).



Si les fichiers sont gros (>10Mo) , j'irais même jusqu'à $1000000 (16Mo) sur une machine qui en a.

soit :

Buffer = Array[0..$FFFFFF] of byte (ou of char)



Ken@vo


<hr size="2" width="100%">Code, Code, Codec !
Commenter la réponse de cs_Kenavo
Emandhal 199 Messages postés dimanche 2 mars 2003Date d'inscription 10 octobre 2006 Dernière intervention - 5 avril 2005 à 17:53
0
Utile
Je vais faire comme Kenavo...
Je dirai qu'un grand buffer (>1Mo) est bien parce que ca limite les accès au disque dur.
Mais il faut tout de meme savoir que Win95/98/Me ont une gestion de la mémoire légèrement... chaotique et un gros buffer comme ca, c'est bien plus préjudiciable que bénéfique.
Un buffer de taille variable me parrait une meilleure idée
Mais ça reste mon idée...

Tout problème a sa solution... Mais en général, c'est jamais la bonne...
Commenter la réponse de Emandhal
Inekman 292 Messages postés dimanche 2 février 2003Date d'inscription 30 juin 2006 Dernière intervention - 5 avril 2005 à 17:58
0
Utile
le buffer de 1 Mo ne passe pas :'( car :



---------------------------

Debugger Fault Notification

---------------------------

Project "Project1.exe" faulted with message: 'access violation at
0x0045c31f: write of address 0x01920fa0'. Process Stopped. Use Step or
Run to continue.

---------------------------

OK

---------------------------



Voilà, j'avais mis 512 ko un coup et ça marchait bien aussi mais je
sais pas c'est quoi la limite du tableau...en tout cas 1 Mo c'est pas
la peine, alors 16 Mo
Commenter la réponse de Inekman
WhiteHippo 1270 Messages postés samedi 14 août 2004Date d'inscription 5 avril 2012 Dernière intervention - 5 avril 2005 à 19:03
0
Utile
Inekman, en reprenant mon code (qui n'est peut etre pas parfait, soit !! )et en mettant la constante à 16Mo, ça fonctionne chez moi (Copie de fichier de plus de 10Mo) !!

const
TAILLE_BUFFER = $1000000 ;

Donc, faut que tu regardes ton code de plus près, doit y avoir un hic !!

Cordialement.
Commenter la réponse de WhiteHippo
Inekman 292 Messages postés dimanche 2 février 2003Date d'inscription 30 juin 2006 Dernière intervention - 6 avril 2005 à 00:19
0
Utile
Ayé j'ai fait comme t'as dis WhiteHippo. En fait, quand on met la
taille directement dans le tableau ça passe pas...mais je l'ai mis en
constante comme toi et ça marche good ;-) J'ai mis le buffer à 1Mo, 4
Mo etc..et ça passe nikel.



Je voudrai savoir ce que ça apporte de passer par des pointeurs, car je comprend pas encore cette philosophie
Commenter la réponse de Inekman

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.