Link to home
Start Free TrialLog in
Avatar of T0masz
T0masz

asked on

problems with blockread and reading non ascii chars

Im using the function below to extract data from a file it has data+image at certain offsets within the file
the problem Im having is that after reading
a:=fread(sizeofimage); it only returns the first 3 characters (which are ascii) but nothing more... im guessing that this has to do with reading non ascii characters and passing em as strings, can anyone tell me how to read this correctly?

Tom

function fread(n : integer) : string;
var
  txt : pchar;
  nread : integer;
begin
GetMem(txt, n);
BlockRead(f, txt[0], n,nread);
result := copy(txt,1,n);
FreeMem(txt);
if(NOT nread = n) then // if couldnt read all the bytes then possibly EOF
begin                  // if eof here there is a problem with the file
  neof:=1;             // the record wasnt complete
  ShowMessage('ERROR EOF TRIPPED FILE NOT COMPLETE');
  Application.Terminate;
end;
end;
Avatar of TheRealLoki
TheRealLoki
Flag of New Zealand image

shouldn't it be
result := copy(txt,1,nread);  // not n. In case you ask for 10 bytes, but only get 5
Avatar of T0masz
T0masz

ASKER

if that happends the program is gonna be terminated anyway so it wont even have a chance of returning the result... my problem basicly is that I have
6000bytes of tiff image thats being read a := fread(6000); but then when I do count(a) i get less then 6000 I only get like 3 which are the first 3 ascii chars of the file.

Tom
can you check the value 'n' when you do the read, anbd make sure it is about 6000.
I only ask this because if you are doing sizeof(Image1) or sizeof(image1.picture) etc you will only get about 4 bytes (pointer)
not the actual size of the picture
Avatar of T0masz

ASKER

  ShowMessage(ar['TiffPage1_image_size']);
   a := fread( strtoint(ar['TiffPage1_image_size']));
   ShowMessage(inttostr(Length(a))+'|'+a);

first msg I get "6063"
second one I get "3|II*"

Tom
ASKER CERTIFIED SOLUTION
Avatar of Member_2_248744
Member_2_248744
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
btw, yes, you are right. because your result is a string, it will assume it is the end of the string if 1 of the bytes (chars) is a zero

here is how I would go about loading your file ( see 3rd procedure "LoadIntoBuffer" )
the first 2 procedures are a quick example I threw together to demonstarte loading and saving a string and then a bitmap
I am not sure of the file format for your TIFF, so best i could do

procedure TForm1.bSaveClick(Sender: TObject);
    var
        fs: TFileStream;
        i: integer;
        s: string;
    begin
        fs := TFileStream.Create('c:\temp\text_image.dat', fmCreate);
        try
            s := eFilename.Text; // the filename
            i := length(s);
            fs.Write(i, sizeof(i)); // write the length of the string
            fs.Write(s[1], i); // write the actual string
            Image1.Picture.Bitmap.SaveToStream(fs);  // now save the bitmap image
        finally
            fs.Free;
        end;
    end;

procedure TForm1.bLoadClick(Sender: TObject);
    var
        fs: TFileStream;
        i: integer;
        s: string;
    begin
        fs := TFileStream.Create('c:\temp\text_image.dat', fmOpenRead);
        try
            fs.Read(i, sizeof(i));
            SetLength(s, i);
            fs.Read(S[1], i);
            eFilename.Text := s;
            Image1.Picture.Bitmap.LoadFromStream(fs);
        finally
            fs.Free;
        end;
    end;

procedure TForm1.bLoadIntoBufferClick(Sender: TObject);
    var
        fs: TFileStream;
        i: integer;
        s: string;
        buffer: array[1..8192] of byte; // this holds
        bufferlength: integer; // how much is actually read
    begin
        fs := TFileStream.Create('c:\temp\text_image.dat', fmOpenRead);
        try
            fs.Read(i, sizeof(i));
            SetLength(s, i);
            fs.Read(S[1], i);
            eFilename.Text := s;
// now load the image into the buffer. assuming a length of 6000
            bufferlength := fs.Read(Buffer[1], 6000);
            ShowMessage(Format('we read %d bytes into the buffer. We expected 6000', [bufferlength]));
        finally
            fs.Free;
        end;
    end;
just wondering, why to treat a binary as string?
Did you use reset() function with 1 parameter or 2? If you're using untyped files, you should specify a size of record. The default size is 128 bytes for untyped files.
  Reset(f, 1);

About result := copy(txt,1,n);
Why do you need this? Can't you use just result := txt; ?

About if(NOT nread = n)... I think it should be
if not (nread = n)
Btw, if you're using PChar, take in mind that it can't contain null char. It will automatically truncate at null char.

var
  pch : pchar;
  str : string;
begin
pch := 'blah'#0'blah';
str := copy(pch, 0, 10);
ShowMessage(IntToStr(length(str)));

str will be only the text that is before #0.
This is why using PChar to read a binary file is not good
Hi,

Try this:

var
  F: file;
  s1: string;
  neof: integer;

function fread(n : integer) : string;
var
  txt: array of byte;
  nread : integer;
begin
  SetLength(txt, n);
  ZeroMemory(pointer(txt), Length(txt));
  BlockRead(f, txt[0], n, nread);
  result := string(txt);

  if NOT (nread = n) then // if couldnt read all the bytes then possibly EOF
  begin                  // if eof here there is a problem with the file
    neof:=1;             // the record wasnt complete
    ShowMessage('ERROR EOF TRIPPED FILE NOT COMPLETE');
    Application.Terminate;
  end;
end;

// usage:
begin
  AssignFile(F, 'c:\temp\test.bin');
  Reset(F, 1);
  s1 := fread(6000);
  ShowMessage(s1[81]);
  CloseFile(F);
end;

Slick812 gave a simplier solution that should work well also. Just have in mind in both cases that if you try to visualize the string (using ShowMessage(s1), for instance) nothing will be visible after the first null character. s1 variable still holds the correct bytes, though.

Regards, Geo
   
function fread(var SomeFile: File; n : integer) :TBuf; //declared as TBuf = array of char; in the interface section
var nread : integer;
begin
  try
    SetLength(result, n);
    Reset(SomeFile, 1);
    BlockRead(SomeFile, result, n, nread);
    CloseFile(SomeFile);
    //if you want to kick out an exception when the amount
    // read is not equal to the requested amount (i.e. 6000
    // in your example) then raise it here like this:
    //  if (nRead <> n) then
    //    raise exception.Create('Could not read requested number of bytes.'#13'-Only '+IntToStr(nRead)+' bytes were read');
    Setlength(result, nRead);
  except on e: exception do
    raise exception.Create('Error reading from file:'#13+e.Message);
  end;
end;

use like this:

var buf: TBuf;
     f: File;
begin
  AssignFile(f, 'somefile.name');
  buf:= fread(f, 6000);
...


regards
Pierre
from my previous example, if you wanted to see the output you could do something like:

var buf: TBuf;
     s: string; i: integer;
     f: File;
begin
  AssignFile(f, 'somefile.name');
  buf:= fread(f, 6000);
  Setlength(s, length(buf));
  for i:= 0 to length(buf)-1 do  
    if buf[i] = #0 then s[i]:= ' ' else s[i]:= buf[i]; //replace zero chars with a space
  ShowMessage(s);
...
Avatar of T0masz

ASKER

sorry, I have been away from this problem for a while ;)
geobul: I tried your example and when I output the file
---
   a := fread( strtoint(ar['TiffPage1_image_size']));
   ShowMessage(inttostr(Length(a))+'|'+a);

   AssignFile(tiffFile, 'c:\Test1.tif');
   ReWrite(tiffFile, 1);
   BlockWrite(tiffFile, a, length(a));
   CloseFile(tiffFile);
-----
I always get different data inside the file... altho the size is finally right :)

PierreC  let me test this
Thanks
Tom
Avatar of T0masz

ASKER

Pierre: I put
TBuf : Array of char in my type under interface but that doesnt seem to work please advise

Tom
Avatar of T0masz

ASKER

nvm
I see that you are having problems with this, first, I do not beleive that you give enough information for me to get what you are trying to do, , , .
Second, you seem to be having problems with the Non-Ascii data you use in Ascii memory blocks, like String and PChar, , , I am wondering why you are using Ascii memory blocks, if you are having so much trouble with them? Why not use just a Pointer type (a NON-Ascii type) and assign memory to it with GetMem(pTiff, TiffSize)?
Third, I think that the delphi TFileStream is so much better for this type of thing, so I use TFileStream.

anyway here is my version for reading a Multi-File container file and then extracting the several image files from the Mutifile to individual image files. . .
I do NOT know anything about your multi-TIFF file so I had to build my own muti-image file with Bitmaps, here is the code in a button click event that I used to write the muti-Bitmap file, it uses a file Header to record all of the Image (bitmap) position and sizes -


type
  TBmpHeader = record
    Pos, Size: Cardinal;
    end;



procedure TForm1.sbut_MakeBmpFileClick(Sender: TObject);
var
FStream: TFileStream;
MemS: TMemoryStream;
Bmp1: TBitmap;
i: Integer;
BmpHeader1: TBmpHeader;
begin
FStream := TFileStream.Create('H:\File of Bmps.fob', fmCreate);
try
  Bmp1 := TBitmap.Create;
  Bmp1.canvas.brush.Color := $FF33DA;
  Bmp1.Width := 100;
  Bmp1.Height := 100;
  Bmp1.Canvas.TextOut(3,3,'ONE');
  i := 3; // 3 is the amount of Bitmaps in this file
  FStream.WriteBuffer(i, SizeOf(i));
  BmpHeader1.Pos := 4+ (3*SizeOf(BmpHeader1));
  MemS := TMemoryStream.Create;
  Bmp1.SaveToStream(MemS);
  BmpHeader1.Size := MemS.Size;
  for i := 0 to 2 do
    FStream.WriteBuffer(BmpHeader1, SizeOf(BmpHeader1));
  MemS.Position := 0;
  MemS.SaveToStream(FStream);

  Bmp1.Canvas.TextOut(3,3,'TWO');
  MemS.Clear;
  Bmp1.SaveToStream(MemS);
  BmpHeader1.Pos := FStream.Position;
  BmpHeader1.Size := MemS.Size;
  FStream.Position := 4+SizeOf(BmpHeader1);
  FStream.WriteBuffer(BmpHeader1, SizeOf(BmpHeader1));
  FStream.Seek(0, soFromEnd);
  MemS.SaveToStream(FStream);

  Bmp1.Canvas.TextOut(3,3,'THREE');
  MemS.Clear;
  Bmp1.SaveToStream(MemS);
  BmpHeader1.Pos := FStream.Position;
  BmpHeader1.Size := MemS.Size;
  FStream.Position := 4+(SizeOf(BmpHeader1)*2);
  FStream.WriteBuffer(BmpHeader1, SizeOf(BmpHeader1));
  FStream.Seek(0, soFromEnd);
  MemS.SaveToStream(FStream);
  finally
  FreeAndNil(MemS);
  FreeAndNil(Bmp1);
  FreeAndNil(FStream);
  end;
end;

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

next is the code I guess you will be more interested in the code in a button click to read the muti-bitmap file and then extract all of the bitmap files to separate bitmap files on disk.
I do create a new FileStream for each separate bitmap file and then use the Stream.CopyFrom method to copy the bitmap file to the new file, I do not use any transfer buffers (no strings, no PChar, no Pointers), the Stream does all of that.



procedure TForm1.sbut_ReadBmpFileClick(Sender: TObject);
var
FStream, OutStream: TFileStream;
arySize, i: Integer;
aryBmpHeader1: Array of TBmpHeader;
begin
FStream := TFileStream.Create('H:\File of Bmps.fob', fmOpenRead);
try
  FStream.ReadBuffer(arySize, SizeOf(arySize));
  if arySize > 256 then
    begin
    ShowMessage('ERROR - Wrong array size');
    Exit;
    end;
  setLength(aryBmpHeader1, arySize);
  for i := 0 to arySize-1 do
    begin
    FStream.ReadBuffer(aryBmpHeader1[i], SizeOf(aryBmpHeader1[i]));
    Memo1.Lines.Add(IntToStr(i)+' '+IntToStr(aryBmpHeader1[i].Pos)+' '+IntToStr(aryBmpHeader1[i].Size))
    end;
  for i := 0 to arySize-1 do
    begin
    OutStream := TFileStream.Create('H:\Bmp'+IntToStr(i)+'.bmp', fmCreate);
      try
      FStream.Position := aryBmpHeader1[i].Pos;
      OutStream.CopyFrom(FStream, aryBmpHeader1[i].Size);
      finally
      FreeAndNil(OutStream);
      end;
    end;
  finally
  FreeAndNil(FStream);
  end;
end;


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

but for you the important part of the code is -

FileStream1.Position := PositionOfTiff; // set the position to where the Image is located in the file
OutStream.CopyFrom(FStream, SizeOfTiff); // copy the image to another file

ask questions if you need more information
No comment has been added to this question in more than 21 days, so it is now classified as abandoned.

I will leave the following recommendation for this question in the Cleanup topic area:
   Accept: Slick812 {http:#13778363}

Any objections should be posted here in the next 4 days. After that time, the question will be closed.

cwwkie
EE Cleanup Volunteer