Question

Yesterday I finally moved from Delphi 7 to Delphi-XE5. And I am re-writing whole application to Unicode and have some troubles.

Lets says, I have:

TCountry = record
  Name: WideString;
  Extension: WideString;
  Region: WideString;
end;

and have dynamic array of this class, which I want to store in a file on close and then load when application is started. Before (in Delphi 7) I stored it in INI file. But now I want to use Unicode (UTF-16LE) and I don't want to store it in the INI file; maybe I can save it in some binary format.

I tried couple methods, and just want to ask, what is the best way (method) to store a dynamic array with Unicode strings into a file?

Was it helpful?

Solution

Try this code:

TYPE
  TCountry    = RECORD
                  Name        : STRING;
                  Extension   : STRING;
                  Region      : STRING
                END;
  TCountryArr = ARRAY OF TCountry;

VAR
  Countries   : TCountryArr;

{$DEFINE UTF8 }

PROCEDURE SaveCountries(CONST FileName : STRING ; CONST ARR : TCountryArr);
  VAR
    S       : TStream;
    I       : INTEGER;

  PROCEDURE WriteString(S : TStream ; CONST STR : STRING);
    VAR
      LEN           : INTEGER;
      {$IFDEF UTF8 }
        UTF8        : UTF8String;
      {$ENDIF }

    BEGIN
      {$IFDEF UTF8 }
        UTF8:=STR; LEN:=LENGTH(UTF8);
        S.Write(LEN,SizeOf(INTEGER));
        IF LEN>0 THEN S.Write(UTF8[LOW(UTF8)],LEN*SizeOf(AnsiChar))
      {$ELSE }
        LEN:=LENGTH(STR);
        S.Write(LEN,SizeOf(INTEGER));
        IF LEN>0 THEN S.Write(STR[LOW(STR)],LEN*SizeOf(CHAR))
      {$ENDIF }
    END;

  BEGIN
    S:=TFileStream.Create(FileName,fmCreate);
    TRY
      TRY
        I:=LENGTH(ARR);
        S.Write(I,SizeOf(INTEGER));
        FOR I:=LOW(ARR) TO HIGH(ARR) DO WITH ARR[I] DO BEGIN
          WriteString(S,Name);
          WriteString(S,Extension);
          WriteString(S,Region)
        END
      FINALLY
        S.Free
      END
    EXCEPT
      DeleteFile(FileName);
      RAISE
    END
  END;

PROCEDURE LoadCountries(CONST FileName : STRING ; VAR ARR : TCountryArr);
  VAR
    S       : TStream;
    I       : INTEGER;

  PROCEDURE ReadString(S : TStream ; VAR STR : STRING);
    VAR
      LEN           : INTEGER;
      {$IFDEF UTF8 }
        UTF8        : UTF8String;
      {$ENDIF }

    BEGIN
      S.Read(LEN,SizeOf(INTEGER));
      {$IFDEF UTF8 }
        SetLength(UTF8,LEN);
        IF LEN>0 THEN S.Read(UTF8[LOW(UTF8)],LEN*SizeOf(AnsiChar));
        STR:=UTF8
      {$ELSE }
        SetLength(STR,LEN);
        IF LEN>0 THEN S.Read(STR[LOW(STR)],LEN*SizeOf(CHAR))
      {$ENDIF }
    END;

  BEGIN
    S:=TFileStream.Create(FileName,fmOpenRead);
    TRY
      TRY
        S.Read(I,SizeOf(INTEGER));
        SetLength(ARR,I);
        FOR I:=LOW(ARR) TO HIGH(ARR) DO WITH ARR[I] DO BEGIN
          ReadString(S,Name);
          ReadString(S,Extension);
          ReadString(S,Region)
        END
      FINALLY
        S.Free
      END
    EXCEPT
      SetLength(ARR,0);
      RAISE
    END
  END;

It'll save (and reload) an array of TCountry to a file. It'll work in both Unicode and non-Unicode (but the files are not interchangable between the two, ie. a UniCode program can save and reload a UniCode file, but a non-Unicode program cannot reload a file saved by a Unicode-program.

EDIT: If you leave the UTF8 symbol defined, it'll use UTF8 encoding in the binary file. As it stands, it'll only be avilable in the Unicode-enabled Delphi versions (2009 or 2010 and upwards), due to the use of UTF8String

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top