Jump to content
peter2005

record with null-terminated string

Recommended Posts

How can I read content of (typed) file of different data types when one of them is null-terminated string?


Something like this:

 

 THeader = Record
    flags : integer;
    dummy  : integer;
    name: string;
    padding: integer;
  end;

 

This would work fine if it were not for string field.

 

By definition a record has to be of fixed size but generally strings are not.
Another problem with string is the first byte is omitted since it is considered to be length.


Most likely Record cannot be used for my purpose...is there anything else?
 

Edited by peter2005

Share this post


Link to post

You can create a method (of that record) for reading data from a stream, for example. In that method, you use Stream.Read(flags, sizeof(flags))...

To read the string, you probably have to read byte by byte and check, whether it is zero. Do not forget that size of the char is 2 bytes (in Unicode Delphi).

Edited by Vandrovnik

Share this post


Link to post
S:array[0..23] of ansichar

THeader = Record
    flags : integer;
    dummy  : integer;
    name: S;
    //padding: integer;
end;

That may be enough to get you started just determine the array size and the correct encoding.  If you wanting to do the easy way use component streaming or simply loadfromfile and value pairs. 

 

This example was "streamed" by copying from design and pasted here.   You can "stream" it by copying it and pasting in your design.      

object Memo1: TMemo
  Left = 56
  Top = 40
  Width = 185
  Height = 89
  Lines.Strings = (
    'Flag=true'
    'Dummy=" "'
    'name=Pete2005'
    '')
  TabOrder = 0
end

 

Share this post


Link to post

First read string length (obviously it must be written to the file), then set length of a string and read length number of chars

Edited by Fr0sT.Brutal

Share this post


Link to post

You want a typed file, right? Then you cannot have any dynamic type in the record which makes the file. A string is a dynamic type. Instead use an array of characters with specific length:

THeader = packed record
    flags : integer;
    dummy  : integer;
    name: array [0..25] of Char;
    padding: integer;
end;

You could add a property or a method to the record to transform that array of characters to a string for ease of use.

Don't forget that characters are 16 bit unicode since Delphi 2009. If you need 8 bit characters, use AnsiChar instead.

 

Also not that I used the keyword "packed" so that the compiler do not insert padding data for alignment for better performances.

 

Share this post


Link to post

If the initial description is accurate - you don't really have a record - but a stream? I.e. the length of the string is variable, not fixed and #0 padded?

 

[int flags][int dummy][shortish string#0][int padding][int flags][int dummy][this is a lot longer string#0][int padding]

 

Decoding the stream would have to be done element by element, and it would be necessary to know the format of the string - is it ASCII, ANSI (if so, what codepage) or UTF-8.

 

To put it into a more manageable format, you could use your initial record - but you would have to read it element by element to stuff that record or object structure.

This can be done in a number of ways - such as a TFileStream or by going old school BlockRead.

A well designed stream would have had the string length as an int before the string - but now you instead have to scan for that #0 termination.

Share this post


Link to post

After reading all the responses I realized there is no easy way....

 

My description is correct , it's a null-terminated ASCII string(one character one byte, no special ones), its length is not stored anywhere. Using static array only fixes problem of missing a first byte....

 

What I wanted to avoid the most is reading the string byte by byte until #0 is found. 

 

Quote

To put it into a more manageable format, you could use your initial record - but you would have to read it element by element to stuff that record or object structure.

I'll try this way.

 

Quote

A well designed stream would have had the string length as an int before the string.

Perhaps but the file I want to read was created in Visual Studio ..so I presume in C++ are "normal" ways to read #0 strings.

Share this post


Link to post
1 hour ago, peter2005 said:

My description is correct , it's a null-terminated ASCII string(one character one byte, no special ones), its length is not stored anywhere. Using static array only fixes problem of missing a first byte....

Are you trying to read an existing file format or are you trying to design your own to store your own data? We can only help you correctly if you tell us what you need to do! Telling us a problem you get with a solution you imagined for a problem we don't know is not the best track to the real solution.

 

Share this post


Link to post
20 hours ago, peter2005 said:

My description is correct , it's a null-terminated ASCII string(one character one byte, no special ones), its length is not stored anywhere. Using static array only fixes problem of missing a first byte....

 

What I wanted to avoid the most is reading the string byte by byte until #0 is found. 

If file could be loaded to memory fully, just use
 

strA := StrPas(pCurr);
Inc(pCurr, Length(strA) + 1);
...

Alternatively, I believe there are plenty of 3rd party buffer/stream readers available (it's weird RTL still doesn't have one!)

Edited by Fr0sT.Brutal

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×