我开发了通过HTTP进行通信的服务器和移动客户端。服务器是用Delphi 7编写的(因为它必须与旧代码兼容),客户端是用XE6编写的移动应用程序。服务器将包含字符串的数据流发送到客户端。问题与编码有关。
在服务器上,我尝试在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;
正如用Delphi7编写的那样,Value是一个单字节字符串。
在客户端上,我读取UTF8中的字符串并将其编码为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;
但这不起作用,当我用
ShowMessage
显示字符串时,字母是错误的。那么如何在Delphi 7中存储字符串并在移动应用程序的XE6中恢复它呢?是否应在表示字符串的数据开头添加BOM? 最佳答案
要在移动应用程序中读取UTF8编码的字符串,请使用字节数组和TEncoding
类。像这样:
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;
该代码可以满足您在XE6中的需要,但是,当然,此代码不会在Delphi 7中进行编译,因为它使用了
TEncoding
。而且,您的TStreamWrap.WriteString
实现可以在Delphi 7中实现所需的功能,但是在XE6中却无法实现。现在看来,您正在为Delphi 7和Delphi XE6版本使用相同的代码库。这意味着您可能需要使用一些条件编译来处理这些版本之间不同的文本处理。
我个人将按照
TEncoding
的示例进行操作。您需要的是一个将本机Delphi string
转换为UTF-8编码的字节数组的函数,以及一个与之相反的函数。因此,让我们考虑字符串到字节的功能。我不记得Delphi 7是否具有
TBytes
类型。我怀疑不是。因此,让我们定义它:{$IFNDEF UNICODE} // definitely use a better conditional than this in real code
type
TBytes = array of Byte;
{$ENDIF}
然后我们可以定义函数:
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}
相反的函数应该对您来说微不足道。
一旦封装的两个Delphi版本之间在文本编码处理方面存在差异,则可以在程序的其余部分中编写有条件的免费代码。例如,您将这样编码
WriteString
: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;