?
Solved

problems with blockread and reading non ascii chars

Posted on 2005-04-13
18
Medium Priority
?
560 Views
Last Modified: 2010-05-18
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;
0
Comment
Question by:T0masz
  • 5
  • 3
  • 2
  • +5
17 Comments
 
LVL 17

Expert Comment

by:TheRealLoki
ID: 13778195
shouldn't it be
result := copy(txt,1,nread);  // not n. In case you ask for 10 bytes, but only get 5
0
 

Author Comment

by:T0masz
ID: 13778260
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
0
 
LVL 17

Expert Comment

by:TheRealLoki
ID: 13778274
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
0
Industry Leaders: 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!

 

Author Comment

by:T0masz
ID: 13778357
  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
0
 
LVL 34

Accepted Solution

by:
Slick812 earned 2000 total points
ID: 13778363
?? I do not think it is wize to use the dephi string functions like Copy( ) for non ascii data like images, the string functions and the PChar transfers (you use both in the copy(txt,1,n);) will not do null #0. .  you might try something like -

function fread(n : integer) : string;
var
  nread : integer;
begin
SetLength(Result, n);
BlockRead(f, Result[1], n,nread);

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;

untested, just from head

you can transfer non ascii data to strings, but you can not use most of the string manipulation operators or functions on them
0
 
LVL 17

Expert Comment

by:TheRealLoki
ID: 13778365
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;
0
 
LVL 27

Expert Comment

by:kretzschmar
ID: 13779115
just wondering, why to treat a binary as string?
0
 
LVL 11

Expert Comment

by:ZhaawZ
ID: 13779188
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)
0
 
LVL 11

Expert Comment

by:ZhaawZ
ID: 13779208
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
0
 
LVL 17

Expert Comment

by:geobul
ID: 13779690
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
0
 
LVL 14

Expert Comment

by:Pierre Cornelius
ID: 13809266
   
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
0
 
LVL 14

Expert Comment

by:Pierre Cornelius
ID: 13809309
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);
...
0
 

Author Comment

by:T0masz
ID: 13922352
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
0
 

Author Comment

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

Tom
0
 

Author Comment

by:T0masz
ID: 13932130
nvm
0
 
LVL 34

Expert Comment

by:Slick812
ID: 13940050
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
0
 
LVL 14

Expert Comment

by:cwwkie
ID: 16412413
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
0

Featured Post

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

Question has a verified solution.

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

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
Please read the paragraph below before following the instructions in the video — there are important caveats in the paragraph that I did not mention in the video. If your PaperPort 12 or PaperPort 14 is failing to start, or crashing, or hanging, …
This lesson discusses how to use a Mainform + Subforms in Microsoft Access to find and enter data for payments on orders. The sample data comes from a custom shop that builds and sells movable storage structures that are delivered to your property. …
Suggested Courses
Course of the Month17 days, 5 hours left to enroll

862 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