?
Solved

Read x amount of bytes from file

Posted on 2003-03-24
17
Medium Priority
?
252 Views
Last Modified: 2010-04-04
Hello

I am trying to write function that takes a number of files and archive it into one file.
The first step was easy (that is: getting them into one file). Now I sit with the problem of trying to extract those files from the one file I created.

Here is what I have so far: (getting all the files into one file)
===========================

procedure TForm1.Button1Click(Sender: TObject);
var
  i: integer;
  strHeader: string;
  targetFile: file;
  currentFile: file;
  myTextFile: TextFile;
  Buf: array[1..2048] of char;
  NumRead, NumWritten: integer;
begin
//------------------------
  if (OpenDialog1.Execute = true) and (OpenDialog1.Files.Count <> 0) then
  begin
    AssignFile(targetFile, 'd:\target.tmp');
    Rewrite(targetFile, 1);

    for i := 0 to OpenDialog1.Files.Count - 1 do
    begin
      AssignFile(currentFile, OpenDialog1.Files.Strings[i]);
      Reset(currentFile, 1);
      strHeader := strHeader + ExtractFileName(OpenDialog1.Files.Strings[i]) + ':' + IntToStr(filesize(currentFile)) + ';';
      Repeat
        BlockRead(currentFile, Buf, SizeOf(Buf), NumRead);
        BlockWrite(targetFile, Buf, NumRead, NumWritten);
      Until (NumRead = 0) or (NumWritten <> NumRead);
      CloseFile(currentFile);
    end;
    CloseFile(targetFile);

    strHeader := copy(strHeader, 1, Length(strHeader) - 3) + '*!';

//------------------------
    AssignFile(myTextFile, 'd:\target.txt');
    Rewrite(myTextFile);
    Writeln(myTextFile, strHeader);
    CloseFile(myTextFile);

//------------------------
    AssignFile(targetFile, 'd:\target.git');
    Rewrite(targetFile, 1);
//------------------------
    AssignFile(currentFile, 'd:\target.txt');
    Reset(currentFile, 1);
    Repeat
      BlockRead(currentFile, Buf, SizeOf(Buf), NumRead);
      BlockWrite(targetFile, Buf, NumRead, NumWritten);
    Until (NumRead = 0) or (NumWritten <> NumRead);
    CloseFile(currentFile);
//------------------------
    AssignFile(currentFile, 'd:\target.tmp');
    Reset(currentFile, 1);
    Repeat
      BlockRead(currentFile, Buf, SizeOf(Buf), NumRead);
      BlockWrite(targetFile, Buf, NumRead, NumWritten);
    Until (NumRead = 0) or (NumWritten <> NumRead);
    CloseFile(currentFile);
//------------------------
    CloseFile(targetFile);
//------------------------

    DeleteFile('d:\target.txt');
    DeleteFile('d:\target.tmp');

    ShowMessage('Done!');
  end;
end;

============================================

I need to eventualy put this into an ActiveX control, and do not want to install any other third party components etc. Using dll's to do the job is out of the question as well.

I have a header section and know the filesize and filename of each file inside my archive- but how do I read those exact amount of bytes from the archive file?

Thanx in advance

-RR-

0
Comment
Question by:rogueripper
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 5
  • 4
  • 3
  • +2
17 Comments
 
LVL 27

Expert Comment

by:kretzschmar
ID: 8195008
var t,s : TFileStream;
begin
  s := TFileStream.Create('SourceFileName',fmOpenRead);  //source
  t := TFileStream.Create('SourceFileName',fmOpenRead);  //Target, could also be a memorystzream
  s.postion := whatever; //positioning to a offset
  t.CopyFrom(s,SizeOfBytesIWantToCopyHere);
  s.free;
  t.free;
end;

just from head and from scratch

meikl ;-)
0
 
LVL 27

Accepted Solution

by:
kretzschmar earned 2000 total points
ID: 8195017
oops, sorry

this
t := TFileStream.Create('SourceFileName',fmOpenRead);  

should be
t := TFileStream.Create('TargetFileName',fmCreate);  


just a copy and paste failure :-))
0
 
LVL 1

Expert Comment

by:User137
ID: 8195160
What ever the read-write style is (TFileStream or AssignFile...), the idea is to write info about files in packet also.
Sequence example:
-Open PackFile for write
-Write file1 size
-Write file1
-Write file2 size
-Write file2
-and so on

or alternatively write all sizes of all files at the beginning of the pack-file.
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
LVL 1

Expert Comment

by:User137
ID: 8195207
Oops, you did have file info...

So, if you can read the header and create an array of file sizes, you surely know how to use the array to read exact amount of bytes.
0
 
LVL 9

Expert Comment

by:mocarts
ID: 8195264
yes, you must write file information in some kind of table of contents to retrieve later file offsets, names, sizes etc:

type
  TFileRec = record
    offset: integer;
    size: integer;
    name: string;
  end;
  TFileRecs = array of TFileRec;

procedure TForm1.btnConcatClick(Sender: TObject);
const
  // files for testing
  fn: array [0..1] of string = ('d:\fsfrom1.txt', 'd:\fsfrom2.txt');
var
  fr: TFileRecs;
  fsto, fsfrom: TFileStream;
  i, s, ss: integer;
begin
  SetLength(fr, length(fn));
  fsto := TFileStream.Create('d:\fsto.txt', fmCreate or fmShareDenyWrite);
  try
  for i := 0 to high(fn) do begin
    fsfrom := TFileStream.Create(fn[i], fmOpenRead);
    try
      fr[i].offset := fsto.position;
      fr[i].size := fsto.CopyFrom(fsfrom, 0);
      fr[i].name := ExtractFileName(fn[i]);
    finally
      fsfrom.free;
    end;
  end;
  // writing *table of contents*
  s := fsto.position;
  i := length(fr);
  fsto.write(i, sizeOf(i)); // count of files
  for i := 0 to high(fr) do begin
    fsto.Write(fr[i], sizeOf(integer) * 2);
    ss := length(fr[i].name);
    fsto.write(ss, sizeof(ss));
    fsto.write(fr[i].name[1], length(fr[i].name));
  end;
  fsto.write(s, sizeOf(integer)); // table offset
  finally
    fsto.free;
  end;
end;

procedure TForm1.btnRestoreClick(Sender: TObject);
var
  fr: TFileRecs;
  fsto, fsfrom: TFileStream;
  i, s, ss: integer;
begin
  fsto := TFileStream.Create('d:\fsto.txt', fmOpenRead);
  fsto.Position := fsto.Size - sizeOf(integer);
  fsto.read(s, sizeof(integer));
  fsto.Position := s;
  fsto.read(s, sizeOf(integer));
  SetLength(fr, s);
  for i := 0 to s -1 do begin
    fsto.read(fr[i], sizeOf(integer)*2);
    fsto.read(ss, sizeOf(integer));
    SetLength(fr[i].name, ss);
    fsto.read(fr[i].name[1], ss);
  end;

  for i := 0 to high(fr) do begin
    fsfrom := TFileStream.Create('d:\'+fr[i].name, fmCreate);
    try
      fsto.position := fr[i].offset;
      fsfrom.CopyFrom(fsto, fr[i].size);
    finally
      fsfrom.free;
    end;
  end;
end;

wbr, mo.
0
 
LVL 9

Expert Comment

by:mocarts
ID: 8195295
I have bug in code :) must free created stream in btnRestoreClick;

procedure TForm1.btnRestoreClick(Sender: TObject);
var
..
begin
 fsto := TFileStream.Create('d:\fsto.txt', fmOpenRead);
try
..
finally
 fsto.free;
end;
end;

wbr, mo.
0
 

Author Comment

by:rogueripper
ID: 8195382
Nice and easy 500pts to you! Thanx allot GREAT answer! :o)

-RR-
0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 8195407
Have you looked into the Windows API functions (OLE32) StgCreateStorage and StgOpenStorage which return a compound storage object (IStorage), which may exactly be what you are looking for. Think of it as a directory which is stored in a single file.

All MS Office applications seem to store their data in these compond streams (Word, etc.), and they are pretty handy in many situations.
0
 
LVL 27

Expert Comment

by:kretzschmar
ID: 8195536
hum, never guessed that grade, thanks :-))

but morcarts,
(the name sounds like many automobiles in miniformats :-))
(just kidding)
i guess, gave you a more precise answer,
with respect a working sample

and i like also to hear more about
avonwyss suggestion

anyway thanks

meikl ;-)
0
 
LVL 9

Expert Comment

by:mocarts
ID: 8195566
tnx, meikl about your comment :)
mocarts is Mozart (V.A.) in latvian spelling :DD

heh, this was an interesting etude for my programming skills :))

wbr, mo.
0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 8195594
kretzschmar, information can be found here:
http://msdn.microsoft.com/library/en-us/stg/stg/istorage_compound_file_implementation.asp

Included in Windows since Windows 95... free of (additional) charge ;-)
0
 
LVL 27

Expert Comment

by:kretzschmar
ID: 8195867
>this was an interesting etude for my programming skills :))

:-)) nice etude, mocarts

thanks for the link avonwyss

meikl ;-)

0
 

Author Comment

by:rogueripper
ID: 8200746
Hello all

Here is what I eventualy did:
============================

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    OpenDialog1: TOpenDialog;
    Button2: TButton;
    OpenDialog2: TOpenDialog;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  vList: TStringList;

implementation

{$R *.dfm}

//*****************************************************************************************************
procedure Split(SourceString, Delimiter: string);
var
  Token: string;
  iPos: integer;
begin
       if Assigned(vList) = false then
             vList := TStringList.Create
  else
        vList.Clear;

  iPos := Pos(Delimiter, SourceString);
  while iPos > 0 do
  begin
    Token := Copy(SourceString, 1, iPos - 1);
    vList.Add(Token);
    Delete(SourceString, 1, iPos);
    iPos := Pos(Delimiter, SourceString);
  end;
  if Trim(SourceString) <> '' then
    vList.Add(SourceString);
end;

//*****************************************************************************************************
procedure TForm1.Button1Click(Sender: TObject);
var
      i: integer;
  strHeader: string;
  targetFile: file;
  currentFile: file;
  myTextFile: TextFile;
  Buf: array[1..2048] of char;
      NumRead, NumWritten: integer;
begin
      if (OpenDialog1.Execute = true) and (OpenDialog1.Files.Count <> 0) then
  begin
            AssignFile(targetFile, 'd:\target.tmp');
        Rewrite(targetFile, 1);
            for i := 0 to OpenDialog1.Files.Count - 1 do
    begin
                  AssignFile(currentFile, OpenDialog1.Files.Strings[i]);
      Reset(currentFile, 1);
                  strHeader := strHeader + ExtractFileName(OpenDialog1.Files.Strings[i]) + ':' + IntToStr(filesize(currentFile)) + ';';
      repeat
        BlockRead(currentFile, Buf, SizeOf(Buf), NumRead);
        BlockWrite(targetFile, Buf, NumRead, NumWritten);
      until (NumRead = 0) or (NumWritten <> NumRead);
      CloseFile(currentFile);
    end;
    CloseFile(targetFile);

        strHeader := copy(strHeader, 1, Length(strHeader) - 1) + '*!';

        AssignFile(myTextFile, 'd:\target.txt');
            Rewrite(myTextFile);
        Writeln(myTextFile, strHeader);
        CloseFile(myTextFile);

             AssignFile(targetFile, 'd:\target.git');
        Rewrite(targetFile, 1);

                  AssignFile(currentFile, 'd:\target.txt');
              Reset(currentFile, 1);
              repeat
                BlockRead(currentFile, Buf, SizeOf(Buf), NumRead);
                BlockWrite(targetFile, Buf, NumRead, NumWritten);
              until (NumRead = 0) or (NumWritten <> NumRead);
              CloseFile(currentFile);

                  AssignFile(currentFile, 'd:\target.tmp');
              Reset(currentFile, 1);
              repeat
                BlockRead(currentFile, Buf, SizeOf(Buf), NumRead);
                BlockWrite(targetFile, Buf, NumRead, NumWritten);
              until (NumRead = 0) or (NumWritten <> NumRead);
              CloseFile(currentFile);

            CloseFile(targetFile);

            DeleteFile('d:\target.txt');
        DeleteFile('d:\target.tmp');

        ShowMessage('Done!');
      end;
end;

//*****************************************************************************************************
procedure TForm1.Button2Click(Sender: TObject);
var
      targetFileStream: TFileStream;
  sourceFileStream: TFileStream;
  myTextFile: TextFile;
  strHeader: string;
  readChar: char;
  tmpChar: char;
  i, j: integer;
  blnStop: boolean;
  myPos: integer;
  arrMyFile: array[0..50] of string;
  nextPos: integer;
begin
      blnStop := false;
  i := 0;

      if (OpenDialog2.Execute = true) and (OpenDialog2.FileName <> '') then
  begin
        AssignFile(myTextFile, OpenDialog2.FileName);
    Reset(myTextFile);
    repeat
          Read(myTextFile, readChar);
      if (readChar = '!') and (tmpChar = '*') then
      begin
                        blnStop := true;
        myPos := i + 3;
      end;
      inc(i);
      tmpChar := readChar;
      strHeader := strHeader + readChar;
    until blnStop = true;
    CloseFile(myTextFile);
    strHeader := copy(strHeader, 1, Length(strHeader) - 2);

    Split(strHeader, ';');

    for j := 0 to vList.Count - 1 do
          arrMyFile[j] := vList.Strings[j];

    nextPos := myPos;
   
            sourceFileStream := TFileStream.Create(OpenDialog2.FileName, fmOpenRead);
            for i := 0 to j - 1 do
    begin
          Split(arrMyFile[i], ':');
               targetFileStream := TFileStream.Create(vList.Strings[0], fmCreate);
               sourceFileStream.Position := nextPos;
               targetFileStream.CopyFrom(sourceFileStream, StrToInt(vList.Strings[1]));
                  targetFileStream.free;
      nextPos := nextPos + StrToInt(vList.Strings[1]);
        end;
            sourceFileStream.free;
    ShowMessage('Done!');
  end;
end;

end.


==============================================


This code works nicely, anybody got a better way of doing it?


-RR-

0
 

Author Comment

by:rogueripper
ID: 8200769
The reason I picked Kretzschmar's answer is that he answered within 10 min of my post. I needed something urgently- and Kretzschmar put me on the right track.

Thanx again!

:o)

0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 8200871
That's OK... I only wanted to give a hint about a not-so-known feature of Windows...
0
 
LVL 9

Expert Comment

by:mocarts
ID: 8204090
yep, we all know that kretzschmar is fast as wind :)
0
 
LVL 27

Expert Comment

by:kretzschmar
ID: 8204143
only fast as wind?
i would guess i'm faster than the light :-))

(just kidding, i'm not really fast, it's more random,
to be on right time on right question)


meikl ;-)
0

Featured Post

Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
If you’ve ever visited a web page and noticed a cool font that you really liked the look of, but couldn’t figure out which font it was so that you could use it for your own work, then this video is for you! In this Micro Tutorial, you'll learn yo…
This is my first video review of Microsoft Bookings, I will be doing a part two with a bit more information, but wanted to get this out to you folks.
Suggested Courses

770 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question