I develop a server and a mobile client that communicate over HTTP. Server is written in Delphi 7 (because it has to be compatible with old code), client is mobile application written in XE6. Server sends to client stream of data that contains strings. A problem is connected to encoding.
On the server I try to pass strings in UTF8:
//Writes string to stream
procedure TStreamWrap.WriteString(Value: string);
var
BytesCount: Longint;
UTF8: string;
begin
UTF8 := AnsiToUtf8(Value);
BytesCount := Length(UTF8);
WriteLongint(BytesCount); //It writes Longint to FStream: TStream
if BytesCount > 0 then
FStream.WriteBuffer(UTF8[1], BytesCount);
end;
As it's written in Delphi7, Value is a single byte string.
On the client I read string in UTF8 and encode it to Unicode
//Reads string from current position of stream
function TStreamWrap.ReadString: string;
var
BytesCount: Longint;
UTF8: String;
begin
BytesCount := ReadLongint;
if BytesCount = 0 then
Result := ''
else
begin
SetLength(UTF8, BytesCount);
FStream.Read(Pointer(UTF8)^, BytesCount);
Result := UTF8ToUnicodeString(UTF8);
end;
end;
But it doesn't work, when I display the string with ShowMessage
the letters are wrong. So how to store string in Delphi 7 and restore it in XE6 on the mobile app? Should I add BOM at the beginning of data representing the string?
To read your UTF8 encoded string in your mobile application you use a byte array and the TEncoding
class. Like this:
function TStreamWrap.ReadString: string;
var
ByteCount: Longint;
Bytes: TBytes;
begin
ByteCount := ReadLongint;
if ByteCount = 0 then
begin
Result := '';
exit;
end;
SetLength(Bytes, ByteCount);
FStream.Read(Pointer(Bytes)^, ByteCount);
Result := TEncoding.UTF8.GetString(Bytes);
end;
This code does what you need in XE6, but of course, this code will not compile in Delphi 7 because it uses TEncoding
. What's more, your TStreamWrap.WriteString
implementation does what you want in Delphi 7, but is broken in XE6.
Now it looks like you are using the same code base for both Delphi 7 and Delphi XE6 versions. Which means that you may need to use some conditional compilation to handle the treatment of text which differs between these versions.
Personally I would do this by following the example of TEncoding
. What you need is a function that converts a native Delphi string
to a UTF-8 encoded byte array, and a corresponding function in the reverse direction.
So, let's consider the string to bytes function. I cannot remember whether or not Delphi 7 has a TBytes
type. I suspect not. So let us define it:
{$IFNDEF UNICODE} // definitely use a better conditional than this in real code
type
TBytes = array of Byte;
{$ENDIF}
Then we can define our function:
function StringToUTF8Bytes(const s: string): TBytes;
{$IFDEF UNICODE}
begin
Result := TEncoding.UTF8.GetBytes(s);
end;
{$ELSE}
var
UTF8: UTF8String;
begin
UTF8 := AnsiToUtf8(s);
SetLength(Result, Length(UTF8));
Move(Pointer(UTF8)^, Pointer(Result)^, Length(Result));
end;
{$ENDIF}
The function in the opposite direction should be trivial for you to produce.
Once you have the differences in handling of text encoding between the two Delphi versions encapsulated, you can then write conditional free code in the rest of your program. For example, you would code WriteString
like this:
procedure TStreamWrap.WriteString(const Value: string);
var
UTF8: TBytes;
ByteCount: Longint;
begin
UTF8 := StringToUTF8Bytes(Value);
ByteCount := Length(UTF8);
WriteLongint(ByteCount);
if ByteCount > 0 then
FStream.WriteBuffer(Pointer(UTF8)^, ByteCount);
end;