9
votes

Fonction Delphi Comparaison du contenu de deux ttream?

J'ai besoin de comparer si deux ttstream descendant ont le même contenu . Le seul résultat intéressant pour moi est le booléen oui / non.

Je vais coder une "forte> boucle simple check-octet après octet le contenu des flux.

Mais je suis curieux savoir s'il existe une fonction déjà existante. Je n'ai trouvé aucun intérieur Delphixe ou JCL / JVCl Libs.

Bien sûr, les deux ruisseaux ont la même taille!


0 commentaires

4 Réponses :


5
votes

Il n'y a pas de fonctionnalité de ce type. Une seule chose que je peux recommander - lire pas les octets à octet, mais utiliser des blocs de 16-64kbytes, ce serait beaucoup plus rapide.


2 commentaires

ouais, de grands blocs avec des appels à compareremem pourront cela faire


Ok, merci pour cette première réponse et les astuces sur les gros blocs. J'accepterai la réponse de Daemon_X car il y a le code complet avec l'utilisation du comparatif ().



12
votes

Exactement, comme Nickolay O. a déclaré que vous devriez lire votre flux en blocs et utiliser un comparatif. Voici un exemple (y compris le test de taille) ...

function IsIdenticalStreams(Source, Destination: TStream): boolean;
const Block_Size = 4096;

var Buffer_1: array[0..Block_Size-1] of byte;
    Buffer_2: array[0..Block_Size-1] of byte;
    Buffer_Length: integer;

begin
  Result := False;

  if Source.Size <> Destination.Size then
    Exit;

  while Source.Position < Source.Size do
    begin
      Buffer_Length := Source.Read(Buffer_1, Block_Size);
      Destination.Read(Buffer_2, Block_Size);

      if not CompareMem(@Buffer_1, @Buffer_2, Buffer_Length) then
        Exit;
    end;

  Result := True;
end;


11 commentaires

Peut-être que cela aide lorsque vous définissez les deux positions de flux à 0 avant de commencer votre boucle.


Ou charger les flux entiers dans 2 Mémoramays et nourrir ceux à comparermem.


@Uwe Raabe - +1, vous êtes totalement droit, @Remko - +1, vous avez probablement raison aussi, j'espère que ce comparatif quitte immédiatement quand il trouve la différence d'octet


Je n'aime pas TMemoryStream car c'est une catastrophe pour les fichiers volumineux en raison de sa nécessité d'avoir un bloc de mémoire contigu. Cela ira peut-être à DCC64 mais jusque-là, cela mérite d'éviter. Ce code ne fonctionnera pas car il représente des flux dont la taille est inférieure à block_size parce que vous comparez du bruit de pile aléatoire.


@Daemon_x CompeMemem est en effet court-circuit. Mais alors quoi, avec un bloc de 4k, vous ne remarqueriez pas la différence.


@Daemon_X La solution simple pour cela est de noter la valeur de retour de l'appel à ttream.Read comme bytesread et passez min (bytesdread, block_size) au comparatif.


@David Heffernan - Oui, tu étais plus rapide :)


@David Heffernan: TMemoryStream fonctionne bien tant que vous prévenez la mémoire appelant la mémoire SETSIZE Si vous savez quelle taille il sera à l'avance. Quoi qu'il en soit, pour comparer deux flux, je ne les chargerais pas complètement en mémoire, mieux de commencer à charger, comparer et arrêtez-vous au premier décalage, en particulier pour les gros flux sur disque (ou à d'autres sources «lentes», c'est-à-dire un champ de base de données).


@David Heffernan - J'ai écrit ce morceau de code à la volée, donc si vous avez du temps, modifiez-le et postez-le à votre réponse :)


@Daemon_x c'est mieux! Et je ne sais pas ce que je continuais avec min (buffer_length, block_size) car cela est toujours égal à buffer_length. D'Oh!


@Idsandon non, cela ne fonctionne pas bien! Vous pouvez soit avoir un flux> 2 Go dans lequel il échoue certainement. Ou alternativement, il peut ne pas y avoir un bloc assez important d'espace d'adressage contigu disponible disponible. Cela se produit bien avant d'arriver à 2 Go sous la plupart des modèles d'utilisation normale.



8
votes

Les iSidenticulestreams forts> La fonction postée par Daemon_x est excellente - mais a besoin d'un ajustement pour fonctionner correctement. (Uwe Raabe a déjà attrapé la question.) Il est critique que vous réinitialisez les positions du flux avant de démarrer la boucle em> - ou cette procédure renvoie probablement une erreur incorrecte si les deux flux ont déjà été accessibles en dehors de cette fonction.

C'est la solution finale qui fonctionne à chaque fois. Je viens de renommé la fonction pour convenir à mes conventions de nommage. Merci Daemon_x pour la solution élégante. P>

function StreamsAreIdentical(Stream1, Stream2: TStream): boolean;
const
  Block_Size = 4096;

var
  Buffer_1: array[0..Block_Size-1] of byte;
  Buffer_2: array[0..Block_Size-1] of byte;
  Buffer_Length: integer;

begin

  Result := False;

  if Stream1.Size <> Stream2.Size then exit;

  // These two added lines are critical for proper operation 
  Stream1.Position := 0;
  Stream2.Position := 0;

  while Stream1.Position < Stream1.Size do
  begin

    Buffer_Length := Stream1.Read(Buffer_1, Block_Size);
    Stream2.Read(Buffer_2, Block_Size);
    if not CompareMem(@Buffer_1, @Buffer_2, Buffer_Length) then exit;

  end;

  Result := True;

end;


0 commentaires

4
votes

réponses de user532231 strong> et Mike STRT> travaille dans 99% de cas, mais il existe des contrôles supplémentaires supplémentaires à faire forts>!

Descendants de Tstream peut être presque n'importe quoi, donc il n'est pas garanti que flux.Lead reviendra la même quantité de données forte>, même si des flux sont de la même longueur (le flux descendant peut également télécharger des données, donc peut-être revenir en lecture = 0 octets, en attendant le prochain chunk). Les flux peuvent également être également sur une erreur de support et de flux de flux complètes. P>

Pour 100% de code de travail, toutes ces vérifications doivent être effectuées. J'ai modifié la fonction de Mike. P>

Si cette fonction est utilisée par exemple pour réécrire le flux 2 s'il n'est pas identique à Stream1, toutes les erreurs doivent être vérifiées. Lorsque le résultat de la fonction est vrai, tout cela est correct, mais s'il est faux, il serait très intelligent de vérifier si des flux sont en réalité différentes ou simplement une erreur survenue. EM> P>

Edité: a ajouté des contrôles supplémentaires, FichiersAdentifier Fonction basé sur l'exemple StreamSareDidydyDidydyDidyDidydyDidydyDidyDidySical et d'utilisation. EM> PR>

// Usage example

var lError: Integer;
...
 if FilesAreIdentical(lError, 'file1.ext', 'file2.ext')
    then Memo1.Lines.Append('Files are identical.')
    else case lError of
           0: Memo1.Lines.Append('Files are NOT identical!');
           1: Memo1.Lines.Append('Files opened, stream read exception raised!');
           2: Memo1.Lines.Append('File does not exist!');
           3: Memo1.Lines.Append('File open exception raised!');
         end; // case
...

// StreamAreIdentical

function StreamsAreIdentical(var aError: Integer;
                             const aStream1, aStream2: TStream;
                             const aBlockSize: Integer = 4096): Boolean;

var
  lBuffer1: array of byte;
  lBuffer2: array of byte;
  lBuffer1Readed,
  lBuffer2Readed,
  lBlockSize: integer;

begin
  Result:=False;
  aError:=0;
  try
    if aStream1.Size <> aStream2.Size
       then Exit;

    aStream1.Position:=0;
    aStream2.Position:=0;

    if aBlockSize>0
       then lBlockSize:=aBlockSize
       else lBlockSize:=4096;

    SetLength(lBuffer1, lBlockSize);
    SetLength(lBuffer2, lBlockSize);

    lBuffer1Readed:=1; // just for entering while

    while (lBuffer1Readed > 0) and (aStream1.Position < aStream1.Size) do
    begin
      lBuffer1Readed := aStream1.Read(lBuffer1[0], lBlockSize);
      lBuffer2Readed := aStream2.Read(lBuffer2[0], lBlockSize);

      if (lBuffer1Readed <> lBuffer2Readed) or ((lBuffer1Readed <> lBlockSize) and (aStream1.Position < aStream1.Size))
         then Exit;

      if not CompareMem(@lBuffer1[0], @lBuffer2[0], lBuffer1Readed)
         then Exit;
    end; // while

    Result:=True;
  except
    aError:=1; // stream read exception
  end;
end;


// FilesAreIdentical using function StreamsAreIdentical

function FilesAreIdentical(var aError: Integer;
                           const aFileName1, aFileName2: String;
                           const aBlockSize: Integer = 4096): Boolean;

var lFileStream1,
    lFilestream2: TFileStream;

begin
 Result:=False;
 try
   if not (FileExists(aFileName1) and FileExists(aFileName2))
      then begin
        aError:=2; // file not found
        Exit;
      end;

   lFileStream1:=nil;
   lFileStream2:=nil;
   try
     lFileStream1:=TfileStream.Create(aFileName1, fmOpenRead or fmShareDenyNone);
     lFileStream2:=TFileStream.Create(aFileName2, fmOpenRead or fmShareDenyNone);
     result:=StreamsAreIdentical(aError, lFileStream1, lFileStream2, aBlockSize);
   finally
     if lFileStream2<>nil
        then lFileStream2.Free;

     if lFileStream1<>nil
        then lFileStream1.Free;
   end; // finally
 except
   aError:=3; // file open exception
 end; // except
end;


9 commentaires

+1 pour la remarque intéressante. THX pour répondre à une question assez ancienne, je suis toujours émerveillé de voir comment [logiciels] les gens sont tellement consacrés à partager et à améliorer et à échanger continuellement ... je n'ai jamais vu cela ailleurs :)


Trident: Il suffit de faire du monde un meilleur endroit;)


Je laisserais certainement l'exception à être jetée. Si quelque chose ne va pas lors de la lecture et que vous «mangerez ce problème» et de retourner juste un booléen, personne ne sera jamais en mesure de déterminer quel était le problème. Et, il n'est pas nécessaire de comparer ablocksize de mémoire, mais juste tampon1dreaded ou tampon2dreaded (ils seront égaux).


TLAMA: La seule exception possible ici est "Exception de lecture de flux", c'est donc sûr de retourner AERROR: = true sur faux résultat. Personnellement, je préfère ne pas déshabiller l'exécution du programme, mais c'est une question de goût et d'opinion: p aussi je préfère ne pas encoder statiquement la taille de la mémoire tampon (Construct Const en fonction du paramètre Const) et j'ai déplacé des tampons de la pile vers le tas. Encore une fois, il s'agit d'un goût des programmeurs et de la quantité de flexibilité qu'il veut. À propos de CompeMemem Vous avez absolument raison, j'ai changé en tampon1ired depuis les deux sont les mêmes!


Vous avez raison que c'est une question de goût ... Quoi qu'il en soit, utilisez @ Char suivi du nom de l'utilisateur (il déclenchera l'autosuggestion pendant que vous commencerez à taper) pour répondre à une personne à notifier A propos d'un nouveau commentaire (comme par exemple @Tlama me informerait). Je n'ai pas utilisé cela puisque vous êtes le propriétaire du poste et vous êtes donc toujours notifié ainsi que le propriétaire de la question. Merci et bienvenue dans le débordement de la pile!


@David serait-ce que cela fonctionnerait sur un ruisseau inférieur à 4096 en taille? (tampon1dreaded <> ablockSize) Que se passe-t-il?


@Merlinw. Oui, cela fonctionnerait même sur un flux de taille 0.;) Il ne compare que la quantité de la quantité de lecture de manière acualement lue, et si elle n'est pas la même taille, elle sort avant que si (tampon1dreaded <> tampon2dread) , et cette partie ou ((tampon1dreeded <> ablocksize) et (astresam1.position permet d'être lue inférieure à la taille d'Ablock Seulement lorsque fin du flux. C'est en fait ce que vous avez demandé;)


@Merlinw. En fait, tout en travaillant aujourd'hui sur un autre projet, j'ai découvert que le comparatif n'est pas aussi sûr que la longueur ou le déplacement, ce qui fonctionne normalement sur une matrice dynamique de longueur zéro (NIL). La fonction s'écraserait si une personne, pour une raison d'étrange, choisirait une taille de bloc de 0 (et pourrait même entraîner une boucle sans fin, car 0 octets seraient lus tout en s'attendant à atteindre la fin du flux). J'ai donc ajouté plus de chèques et exemple d'utilisation avec une fonction fichiersAdentiperie, à l'aide de StreamSareDidyDidyDidyDidyStical;)


@Trident - Je pense que l'âge de la question est hors de propos. Nous sommes ici pour enseigner / apprendre :)