Avatar billede frede_manden Nybegynder
16. juli 2006 - 20:21 Der er 7 kommentarer

Læse fil - FileStream

Er der nogle som kan se hvordan man koden skal være hvis den kun skal "pakke" en fil ud hvis filen navnet er "test.dat".

Jeg har brugt denne kode til at komprimere flere filer ind i en fil:

procedure CompressFiles(Files: TStrings; const Filename: string);
var
  infile, outfile, tmpFile: TFileStream;
  compr: TCompressionStream;
  i, l: Integer;
  s: string;

begin
  if Files.Count > 0 then
  begin
    outFile := TFileStream.Create(Filename, fmCreate);
    try
      { the number of files }
      l := Files.Count;
      outfile.Write(l, SizeOf(l));
      for i := 0 to Files.Count - 1 do
      begin
        infile := TFileStream.Create(Files[i], fmOpenRead);
        try
          { the original filename }
          s := ExtractFilename(Files[i]);
          l := Length(s);
          outfile.Write(l, SizeOf(l));
          outfile.Write(s[1], l);
          { the original filesize }
          l := infile.Size;
          outfile.Write(l, SizeOf(l));
          { compress and store the file temporary}
          tmpFile := TFileStream.Create('tmp', fmCreate);
          compr := TCompressionStream.Create(clMax, tmpfile);
          try
            compr.CopyFrom(infile, l);
          finally
            compr.Free;
            tmpFile.Free;
          end;
          { append the compressed file to the destination file }
          tmpFile := TFileStream.Create('tmp', fmOpenRead);
          try
            outfile.CopyFrom(tmpFile, 0);
          finally
            tmpFile.Free;
          end;
        finally
          infile.Free;
        end;
      end;
    finally
      outfile.Free;
    end;
    DeleteFile('tmp');
  end;
end;

Og denne kode til at "pakke" filerne ud:

procedure DecompressFiles(const Filename, DestDirectory: string);
var
  dest, s: string;
  decompr: TDecompressionStream;
  infile, outfile: TFilestream;
  i, l, c: Integer;
begin
//version Delphi 5
  if length(DestDirectory) = 0 then dest := 'c:\';

  if DestDirectory[length(DestDirectory)] <> '\' then
    Dest := DestDirectory + '\'
  else Dest := DestDirectory;

  // utiliser IncludeTrailingPathDelimiter sous D6/D7
// dest := IncludeTrailingPathDelimiter(DestDirectory);

  infile := TFileStream.Create(Filename, fmOpenRead);
  try
    { number of files }
    infile.Read(c, SizeOf(c));
    for i := 1 to c do
    begin
      { read filename }
      infile.Read(l, SizeOf(l));
      SetLength(s, l);
      infile.Read(s[1], l);
      { read filesize }
      infile.Read(l, SizeOf(l));
      { decompress the files and store it }
      s := dest + s; //include the path
      outfile := TFileStream.Create(s, fmCreate);
      decompr := TDecompressionStream.Create(infile);
      try
        outfile.CopyFrom(decompr, l);
      finally
        outfile.Free;
        decompr.Free;
      end;
    end;
  finally
    infile.Free;
  end;
end;
Avatar billede hrc Mester
19. juli 2006 - 14:59 #1
Jeg endte med at omskrive rutinerne - har ikke haft mulighed for at teste, men synes det ser godt nok ud til at du skulle prøve at få det til at virke.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

const
  clMin = 0;
  clMax = 9;

type
  TCompressionStream = class(TStream)
  private
  public
    constructor Create(const aCompressor : integer; aStream : TStream);
  end;

  TDecompressionStream = class(TStream)
  private
  public
    constructor Create(aStream : TStream);
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
  public
    procedure DecompressFiles(const aZipFilename : string; aDestDirectory : string; const aDestFilenameMask : string = '*.*');
    procedure CompressFiles(aFilenames: TStrings; const aZipFilename: string);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  Masks;

{ TCompressionStream }

constructor TCompressionStream.Create(const aCompressor: integer; aStream: TStream);
begin
  inherited Create;
  // ...
end;

{ TDecompressionStream }

constructor TDecompressionStream.Create(aStream: TStream);
begin
  inherited Create;
  // ...
end;

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
begin
  DecompressFiles('test.zip','c:\temp','*.dat');
end;

procedure TForm1.CompressFiles(      aFilenames: TStrings;
                              const aZipFilename: string);
var
  i : Integer;
  Writer : TWriter;
  tmpBuffer : TMemoryStream;
  compr: TCompressionStream;
  infile, outfile: TFileStream;
begin
  outFile := TFileStream.Create(aZipFilename, fmCreate);
  Writer := TWriter.Create(outFile,1024);
  try
    for i := 0 to aFilenames.Count - 1 do
    begin
      if FileExists(aFilenames[i]) then
      begin
        infile := TFileStream.Create(aFilenames[i], fmOpenRead);
        try
          { the original filename }
          Writer.WriteString(ExtractFilename(aFilenames[i]));
          { the original filesize }
          Writer.WriteInteger(inFile.Size);
          { compress and store the file temporary}

          tmpBuffer := TMemoryStream.Create;
          compr := TCompressionStream.Create(clMax, tmpBuffer);
          try
            compr.CopyFrom(infile, inFile.Size);

            Writer.WriteInteger(tmpBuffer.Size); // Save compressed size
            Writer.FlushBuffer; // This one's important

            { append the compressed file to the destination file }
            outfile.CopyFrom(tmpBuffer, 0);
          finally
            compr.Free;
            tmpBuffer.Free;
          end;
        finally
          infile.Free;
        end;
      end;
    end;
  finally
    Writer.Free;
    outFile.Free;
  end;
end;

procedure TForm1.DecompressFiles(const aZipFilename : string;
                                      aDestDirectory : string;
                                const aDestFilenameMask: string);
var
  f_name : string;
  f_size : integer;
  f_compsize : integer;
  Reader : TReader;
  inFile, outFile: TFilestream;
  decompr: TDecompressionStream;
begin
  aDestDirectory := IncludeTrailingPathDelimiter(aDestDirectory);

  inFile := TFileStream.Create(aZipFilename, fmOpenRead);
  Reader := TReader.Create(InFile,1024);
  try
    while inFile.Position < inFile.Size do
    begin
      f_name := Reader.ReadString;
      f_size := Reader.ReadInteger;
      f_compsize := Reader.ReadInteger; // Should be used in the decompr class
      Reader.FlushBuffer;

      if MatchesMask(f_name,aDestFilenameMask) then
      begin
        outFile := TFileStream.Create(aDestDirectory + f_name, fmCreate);
        // Assuming that the decompression knows where to stop.
        decompr := TDecompressionStream.Create(infile);
        try
          outfile.CopyFrom(decompr, f_size); // Write the uncompressed data
        finally
          decompr.Free;
          outFile.Free;
        end;
      end
      else
        inFile.Seek(f_compsize,soFromCurrent); // Go to next file
    end;
  finally
    Reader.Free;
    infile.Free;
  end;
end;

end.
Avatar billede frede_manden Nybegynder
19. juli 2006 - 18:51 #2
Det virker desværre ikke, den fejler når man komprimere :-(
Avatar billede hrc Mester
19. juli 2006 - 21:53 #3
Hej Frede. Det er en af den slags kommentarer som får mine nakkehår til at rejse sig! Hvis jeg skal prøve at løse problemet med min kode så kan man ikke bruge din beskrivelse til noget som helst.

Jeg sidder ikke og lukker mavesure kommentarer ud med vilje (jo, jeg gør), men jeg har gennem min karierre stødt på den slags beskrivelser før og jeg gider dem ikke.
Lidt ligesom Microsoft manualer; de er principielt set uangribelige, men i praksis aldeles ubrugelige og folk der skriver dem burde vide bedre.

Derfor: Hvad er det der ikke virker? Hvilken Delphi bruger du (jeg bruger D2006)? Oplysninger, oplysninger.

I øvrigt. Hvis du bare har taget min kode, oversat og så kørt den, så virker det naturligvis ikke. Jeg har oprettet to tomme skal-klasser så jeg kunne arbejde uden dine komprimerings- og dekomprimeringsklasser.
Avatar billede hrc Mester
19. juli 2006 - 22:18 #4
Prøv at erstatte mine skaller med disse. De kopierer 1:1 begge veje og med dem kan jeg flytte filer fra et sted til et andet og jeg kan vælge enkelte filer ud

  TCompressionStream = class(TMemoryStream)
  private
    fDestStream : TStream;
  public
    constructor Create(aDestStream : TStream);
    function CopyFrom(Source: TStream; Count: Int64): Int64; reintroduce;
  end;

  TDecompressionStream = class(TMemoryStream)
  private
  public
    constructor Create(aDestStream : TStream; const aCount : integer = 0);
  end;

implementation

{ TCompressionStream }

function TCompressionStream.CopyFrom(Source: TStream; Count: Int64): Int64;
begin
  result := inherited CopyFrom(Source, Count);
  fDestStream.CopyFrom(self,0);
end;

constructor TCompressionStream.Create(aDestStream: TStream);
begin
  inherited Create;
  fDestStream := aDestStream;
end;

{ TDecompressionStream }

constructor TDecompressionStream.Create(aDestStream: TStream; const aCount : integer);
begin
  inherited Create;
  self.CopyFrom(aDestStream,aCount); // Eller 0, men jeg synes det her er pænere
end;

Desuden er der en lille rettelse i DecompressFiles:

        decompr := TDecompressionStream.Create(inFile,f_compsize);
        try
          assert(decompr.Size = f_size);
          outfile.CopyFrom(decompr,0);
Avatar billede frede_manden Nybegynder
19. juli 2006 - 22:40 #5
Ja undskyld, jeg glemte vist lige at klare problemet :-) Hvad mener du med "dem kan jeg flytte filer fra et sted til et andet og jeg kan vælge enkelte filer ud"??

Jeg har indsat din kode, og jeg får en fejl meddelse i "CompressFiles" proceduren.

          compr := TCompressionStream.Create(clMax, tmpBuffer); <--- "[Error] Unit1.pas(104): Incompatible types: 'TStream' and 'Integer'"
Avatar billede frede_manden Nybegynder
19. juli 2006 - 22:57 #6
Hmm "compressFiles" virker fint, HVIS jeg i stedet for "compr := TCompressionStream.Create(clMax, tmpBuffer);" skriver "compr := TCompressionStream.Create(tmpBuffer);".
Er det rigtigt det jeg gør??
Er det muligt at slettet filer inde i den pakkede file? eller f.eks. opdatere en fil inde i den pakkede fil?
Avatar billede hrc Mester
22. juli 2006 - 22:44 #7
Ja, jeg fjernede komprimerings-parameteren i min "skal". Da jeg ikke har adgang til dine pakke-unit så blev jeg nødt til at lave en tom "skal" der bare ligner.

Med at flytte filer fra et sted til et andet mener jeg, at de kan pakke filer ned i en enkelt fil - og da mine "skaller" ikke komprimerer/dekomprimerer så gemmes de bare 1-1 i forlængelse af hinanden - med dekomprimeringen kan man pakke dem ud andetsteds; en noget omstændig kopieringsrutine om man vil.
Hvis de to "skaller" bliver erstattet med de som du har liggende, så vil de også blive pakket/udpakket.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester