【问题标题】:Converting an AnsiString to a Unicode String将 AnsiString 转换为 Unicode 字符串
【发布时间】:2010-04-01 00:12:32
【问题描述】:

我正在将 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 字符串的格式良好的数据流?

感谢您的帮助。

【问题讨论】:

  • 我认为问题中没有足够的信息来提供有意义的答案。涉及的变量是什么类型?就编译器生成的代码中触发的任何自动转换而言,这将具有潜在的意义。还特别是 oPayGrid 的类型是什么?此对象上存在 sStream 属性表明它不是标准的 VCL 流。理想情况下,我希望看到问题中的代码示例被重新设计/扩展为一个独立的工作示例,无需进一步解释/预测即可演示该行为。
  • 向奥特罗瓦致敬!对不起。我试图避免用无益的细节使问题变得混乱。我想我太成功了。 oPaygrid 是一个类(TObject)。 oPaygrid.sStream 是一个名称不佳的 AnsiString。 sUnicodeString 是一个 Delphi 字符串,默认情况下是一个 unicode 字符串。 iSize1 和 iSize2 是整数。我的问题主要是概念性的。当 AnsiString 被转换为 unicode 字符串时,我是否应该期望在 unicode 字符串中看到每个字符两个字节?我没有看到,这似乎是让我无法使用 LoadFromStream 成功加载控件的原因。
  • 你不应该使用 StringElementSize()。仅当您的代码从半迁移的 C++Builder 模块调用时才需要。赋值sUnicodeString := sAnsiString 将字符串的有效负载更正为 Char=WideChar,因此对 StringElementSize 的调用将始终为您的 AnsiString 返回 SizeOf(AnsiChar),为您的 UnicodeString 返回 SizeOf(Char)。 SizeOf(AnsiChar)/SizeOf(Char) 也更快,更容易阅读和理解,并且写起来更短。
  • 我将 StringElementSize 放入只是为了进行健全性检查,以确保我不会完全不期望看到一个 unicode 字符串格式化为每个字符两个字节。
  • 感谢大家的帮助。 Serg 提供的代码使解决方案变得清晰。虽然我向 unicode 的迁移基本上很容易,但仍有一些事情需要额外的学习和工作。再次感谢。

标签: delphi unicode encoding delphi-2010


【解决方案1】:

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))
    

    【讨论】:

      【解决方案3】:

      我想你想用:

      LoadFromStream(stream, TEncoding.ASCII);
      

      如果您的单字节文本不是 ASCII 而是基于代码页,那么这可能有效:

      LoadFromStream(stream, TEncoding.GetEncoding(1252));
      

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

      【讨论】:

      • LoadFromStream 是来自 TMS 的 AdvStringrid 方法。它只需要一个参数。
      • 我不使用 TMS,但也许 TMS 在他们的 Unicode 组件包中的 TTntStringGrid 可以为你做这件事。请参阅:tmssoftware.com/site/tmsuni.asp 否则,我建议您联系 TMS 并告诉他们您的问题,他们可能会将第二个参数添加到 LoadFromStream 以使其与 Delphi 2009+ Unicode 兼容。
      • 这似乎不是网格的问题。请查看我对原始帖子的修改。将 AnsiString 转换为 unicode 似乎不会改变字符串的内部格式。
      • 这是他们添加到 Delphi 中的整个 TEncoding 系统,旨在处理该确切问题,以便转换正确。因此,也许您的解决方案是使用有效的编码将您的流加载到另一个流中,然后将其加载到您的 AdvStringrid。
      【解决方案4】:

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

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

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

      【讨论】:

      • 所以,和我一样,您希望如果 sUnicodeStream 被声明为 UnicodeString,您会看到一个由每个字符两个字节组成的字符串。如果您尝试我新编辑的示例代码,您会发现它显然不是那样工作的。
      • Serg 是对的... TStringStream 只检查 ansistring 版本中的代码页。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-04
      • 1970-01-01
      相关资源
      最近更新 更多