如何在Delphi 7中将字符串存储在流中并在XE6中的移动应用程序上恢复?

时间:2014-05-12 10:04:13

标签: delphi mobile delphi-7 delphi-xe6

我开发了一个通过 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 吗?

2 个答案:

答案 0 :(得分:4)

要在移动应用程序中读取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;

答案 1 :(得分:-1)

而不是

Utf8 : String;

使用

Utf8 : Utf8String;

在客户端上。然后转换为自动。

编辑:由于客户端位于移动平台上,而Embarcadero已经决定取消移动编译器中的8位字符串,因此上述情况不会适用于此特定情况。但在其他具有8位UTF-8编码字符串的情况下,Utf8String可用于在UTF-8和Unicode字符串之间无缝转换,而无需使用显式UTF-8转换函数。就像使用

一样
UnicodeStringVariable := Utf8StringVariable;

Utf8StringVariable := UnicodeStringVariable;

并且编译器将插入适当的转换。