views:

993

answers:

3

Hi. I have a structure like below that needs to be saved and loaded from disk.

 RSecStructure= packed record
   Name               : string[255];       {NEED UNICODE SUPPORT HERE}
   ScreenName         : string[255];
   OrigFileName       : string[255];                    
   Prim               : string[255];                                           
   ParentVersion      : integer;
   sTag1              : string[255];      
   sTag2              : string[255];
   sTag3              : string[255];
   sTag4              : string[255];
   DateAdd            : TDateTime;
   DateModify         : TDateTime;
  end;

Until now I have used something like this to save the structure:

function
var F: FILE;
    Hdr: RSecStructure;
begin
...
 BlockWrite (F, Hdr, SizeOf(Hdr));   
...
end

The above code worked under Delphi 7. Under D2009 I got a lot of warning messages when I make assignments between short and Unicode strings. Until now I managed to write Delphi code without having ANY compiler warnings or hints and I want to stay like that. So I need an elegant was to save strings (Unicode will be great but not critical) to disk without getting warnings.

+7  A: 

Those string fields are the same in Delphi 2009 as they were in all previous versions. ShortString is not a Unicode type.

So you should be able to continue using that record as-is.

You say it worked in Delphi 7. Does it not work in Delphi 2009? Describe the problem you're having.

Do you mean to say that you want a fixed-length Unicode equivalent to ShortString? There isn't one, so you can't have a record like that, have it hold string-like Unicode values, and save it directly to disk.

I don't think that's a major problem, though, since your disk format wouldn't be compatible with your current format anyway: your characters would be too big.

You can use an array of characters:

type
  TSecStructure = packed record
    Name               : array[0..255] of UnicodeChar;
    ScreenName         : array[0..255] of UnicodeChar;
    OrigFileName       : array[0..255] of UnicodeChar;
    Prim               : array[0..255] of UnicodeChar;
    ParentVersion      : integer;
    sTag1              : array[0..255] of UnicodeChar;
    sTag2              : array[0..255] of UnicodeChar;
    sTag3              : array[0..255] of UnicodeChar;
    sTag4              : array[0..255] of UnicodeChar;
    DateAdd            : TDateTime;
    DateModify         : TDateTime;
  end;

That's not going to be quite as convenient as real string types, but it will work for most purposes.

You can also use a normal UnicodeString type:

type
  TSecStructure = record
    Name               : UnicodeString;
    ScreenName         : UnicodeString;
    OrigFileName       : UnicodeString;
    Prim               : UnicodeString;
    ParentVersion      : integer;
    sTag1              : UnicodeString;
    sTag2              : UnicodeString;
    sTag3              : UnicodeString;
    sTag4              : UnicodeString;
    DateAdd            : TDateTime;
    DateModify         : TDateTime;
  end;

You can't save that directly to disk anymore, but you're also no longer limited to 255 characters. You'll have to store each string field separately. Make sure to store the string's length, too, or you won't know where one string ends and the next begins when it comes time to load your file later.

Rob Kennedy
> array[0..255] of UnicodeChar;I think this will do it. Thanks a lot.
Altar
That doubles the storage size for ASCII chars. Instead you could use ShortString and store UTF8 strings into it (UTF8Encode, UTF8ToString)
Andreas Hausladen
> That doubles the storage size for ASCII chars.This is fine. Thanks.
Altar
I will use a packed structure with "array[0..255] of UnicodeChar" strings to save/load data to disk. Then I transfer the data to a non packed structure with full unicode string in order to get rid of compiler warnings. Thanks Rob.
Altar
+5  A: 

Alternatively, you can declare storable short unicode string record and implement implicit 'to string' and 'from string' conversions.

program TestShortUnicodeString;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type
  TShortUnicodeString = record
  private
    Data: array [0..255] of char; //WARNING - NOT initialized to an empty string
  public
    class operator Implicit(const sus: TShortUnicodeString): string; inline;
    class operator Implicit(const ws: string): TShortUnicodeString; inline;
  end;

class operator TShortUnicodeString.Implicit(const sus: TShortUnicodeString): string;
begin
  Result := StrPas(sus.Data);
end;

class operator TShortUnicodeString.Implicit(const ws: string): TShortUnicodeString;
begin
  // too long strings are silently truncated
  StrPLCopy(Result.Data, ws, Length(Result.Data)-1);
end;

type
  TTestRec = record
    ws1: TShortUnicodeString;
    ws2: TShortUnicodeString;
  end;

var
  f    : file;
  test1: TTestRec;
  test2: TTestRec;

begin
  test1.ws1 := '6*9 =';
  test1.ws2 := ' 42';
  AssignFile(f, 'test.dat');
  Rewrite(f, 1);
  BlockWrite(f, test1, SizeOf(test1));
  CloseFile(f);
  AssignFile(f, 'test.dat');
  Reset(f, 1);
  Assert(FileSize(f) = SizeOf(test2));
  BlockRead(f, test2, SizeOf(test2));
  CloseFile(f);
  Writeln(string(test2.ws1), string(test2.ws2));
  Readln;
end.

[The code above is released to public domain and carries no license obligations.]

gabr
Is it possible to write something like `TShortUnicodeString<N: Integer> = record` analog to `string[N]` (I have no D2010 to test it.)
Ulrich Gerhardt
+2  A: 

You can keep existing structure (string[255]), existing file format and even correctly read non-unicode data previously saved by using the following:

before writing data:

...
record.Name:= UTF8EncodeToShortString(Name);

after reading data:

...
Name:= UTF8ToString(record.Name);
seletit