将AnsiString转换为Unicode字符串

5

我正在将一个D2006程序转换为D2010。我的数据库中存储了一个单字节的字符字符串的值,我需要将其加载到一个具有LoadFromStream方法的控件中,所以我的计划是将该字符串写入流并将其与LoadFromStream一起使用。 但是它没有起作用。在研究这个问题时,我发现一个问题告诉我,我并不真正理解如何从AnsiString转换为Unicode字符串。以下是一个独立的代码片段,说明了我感到困惑的问题:;

procedure TForm1.Button1Click(Sender: TObject); {$O-}
var
  sBuffer: String;
  oStringStream: TStringStream;
  sAnsiString: AnsiString;
  sUnicodeString: String;
  iSize1,
  iSize2: Word;
begin
  sAnsiString := '12345';
  oStringStream := TStringStream.Create(sBuffer);
  sUnicodeString := sAnsiString;
  iSize1 := StringElementSize(sAnsiString);
  iSize2 := StringElementSize(sUnicodeString);
  oStringStream.WriteString(sUnicodeString);
end;

如果您在最后一行打断点,并检查oStringStream的Bytes属性,您会看到它长这样:
Bytes (49 {$31}, 50 {$32}, 51 {$33}, 52 {$34}, 53 {$35}

我原本期望它可能看起来像这样:
(49 {$31}, 00 {$00}, 50 {$32}, 00 {$00}, 51 {$33}, 00 {$00}, 
 52 {$34}, 00 {$00}, 53 {$35}, 00 {$00} ...

显然我的期望是错误的。但是,如何将AnsiString转换为Unicode?

我使用LoadFromStream时没有得到正确的结果,因为它每次从流中读取两个字节,但接收到的数据并不是这样排列的。我应该怎么做才能给LoadFromStream提供基于Unicode字符串的格式良好的数据流呢?

谢谢您的帮助。


3
我认为这个问题提供的信息不足以得出有意义的答案。涉及的变量类型是什么?这对于编译器生成代码中可能触发的任何自动转换来说可能非常重要。另外,oPayGrid的类型是什么?该对象上存在一个sStream属性,这表明它不是标准的VCL流。理想情况下,我希望看到问题中的代码示例被重新制作/扩展成一个独立的、可工作的示例,可以演示行为而无需进一步的解释/占卜。 - Deltics
抬头向奥特罗亚致敬! 抱歉,我试图避免在问题中添加无用的细节。我想我太成功了。oPaygrid是一个类(TObject)。oPaygrid.sStream是一个命名不当的AnsiString。sUnicodeString是Delphi字符串,默认情况下是Unicode字符串。iSize1和iSize2是整数。我的问题主要是概念性的。当AnsiString转换为Unicode字符串时,我应该期望在Unicode字符串中看到每个字符两个字节吗?我没有看到这一点,似乎这就是阻止我成功使用LoadFromStream加载控件的原因。 - jrodenhi
不应使用StringElementSize()。只有在从半迁移的C++Builder模块调用代码时才需要它。赋值sUnicodeString := sAnsiString将字符串的有效载荷更正为Char=WideChar,因此对AnsiString调用StringElementSize将始终返回SizeOf(AnsiChar),对UnicodeString则返回SizeOf(Char)。SizeOf(AnsiChar)/SizeOf(Char)也更快、更易于阅读和理解,写起来也更短。 - Andreas Hausladen
我只是加入StringElementSize进行健全性检查,以确保自己不会完全错误地期望看到每个字符用两个字节格式化的Unicode字符串。 - jrodenhi
感谢大家的帮助。Serg提供的代码使解决方案变得清晰明了。虽然我的Unicode迁移大多数情况下都很容易,但仍有一些需要额外的学习和工作。再次感谢。 - jrodenhi
4个回答

5
oStringStream.WriteString的参数类型是什么?如果它是AnsiString,那么就存在从Unicode到Ansi的隐式转换,这解释了你的示例。
更新:现在真正的问题是TStringStream如何在内部存储数据。在以下代码示例中(Delphi 2009):
procedure TForm1.Button1Click(Sender: TObject);
var
  S: string;
  SS: TStringStream;

begin
  S:= 'asdfg';
  SS:= TStringStream.Create(S);  // 1 byte per char
  SS.WriteString('321');
  Label1.Caption:= SS.DataString;
  SS.Free;
end;

TStringStream内部使用默认的系统ANSI编码(每个字符1个字节)。 构造函数和WriteString过程将字符串参数从Unicode转换为ANSI。

要覆盖此行为,您必须在构造函数中显式声明编码:

procedure TForm1.Button1Click(Sender: TObject);
var
  S: string;
  SS: TStringStream;

begin
  S:= 'asdfg';
  SS:= TStringStream.Create(S, TEncoding.Unicode);  // 2 bytes per char
  SS.WriteString('321');
  Label1.Caption:= SS.DataString;
  SS.Free;
end;

2
在 Delphi 的最新版本中,您可以使用 TEncoding:
TEncoding.UTF8.GetString(TEncoding.ANSI.GetBytes(MyString))

0

我觉得你想要使用:

LoadFromStream(stream, TEncoding.ASCII);

如果您的单字节文本不是ASCII,而是基于代码页,则可能会起作用:
LoadFromStream(stream, TEncoding.GetEncoding(1252));

其中的“1252”是你的单字节文本所基于的代码页。


LoadFromStream是TMS的AdvStringGrid中的一个方法。它只需要一个参数。 - jrodenhi
我不使用TMS,但是也许TMS的Unicode Component Pack中的TTntStringGrid可以为您完成此操作。请参见:http://www.tmssoftware.com/site/tmsuni.asp。否则,我建议您联系TMS并告诉他们您的问题,他们可能会将第二个参数添加到其LoadFromStream中,以使其与Delphi 2009+ Unicode兼容。 - lkessler
这似乎不是网格的问题。请查看我对原帖的编辑。将 AnsiString 强制转换为 Unicode 不会更改字符串的内部格式。 - jrodenhi
这是整个TEncoding系统添加到Delphi中的,旨在处理确切的问题,以便转换将是正确的。因此,您的解决方案可能是使用有效的编码将流加载到另一个流中,然后将其加载到AdvStringrid中。 - lkessler

0

流格式在很大程度上取决于TStringStream.Encoding。在您的示例中,使用的代码页应与sBuffer相同(请参见TStringStream.Create的实现)。

由于oStringStream.WriteString(sUnicodeStream);似乎保存为单个字节,我会假设sBuffer是Ansistring或RawByteString。

现在...为什么读取失败...您还没有向我们提供如何在该流中读取的示例。


所以,就像我一样,你期望如果sUnicodeStream被声明为UnicodeString,你会看到每个字符由两个字节组成的字符串。如果你尝试运行我新编辑过的示例代码,你会发现它似乎并不是这样工作的。 - jrodenhi
Serg是对的... TStringStream仅在ansistring版本中检查代码页。 - Ken Bourassa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接