【问题标题】:Writing to a TMemoryStream and losing unicode写入 TMemoryStream 并丢失 unicode
【发布时间】:2012-07-27 15:20:19
【问题描述】:

使用 Delphi XE2 将 TMemoryStream(包含 unicode 字符串)复制到另一个 TMemoryStream 时,我遇到了一个奇怪的行为:

我有两个 TMemoryStream 实例。第一个实例包含 unicode 文本 (SourceMS)。我将一些任意数据写入第二个 MemoryStream (DestMS),然后将第一个流的内容复制到第二个流,如下所示:

var
  SomeInt: Integer;
  SomeByte: Byte;
  SourceMS, DestMS: TMemoryStream;
begin
  ...
  DestMS.Write(SomeInt, SizeOf(SomeInt));
  DestMS.Write(SomeByte, SizeOf(SomeByte));
  SourceMS.SaveToFile('c:\SourceMS.txt');  // SourceMS.txt contains the unicode chars
  DestMS.CopyFrom(SourceMS, 0);          // copy the whole content of SourceMS to DestMS   
  DestMS.SaveToFile('c:\DestMS.txt');  // DestMS.txt DOEST NOT contain unicode chars              
end;

如何将第一个流的内容复制到第二个流而不丢失 unicode(具有隐式转换)? 当我说“丢失 unicode”时,我的意思是:unicode 字符串确实被复制到第二个流中,但是 unicode 丢失了。我只得到 ANSI 字符。

【问题讨论】:

  • 您的 MS1MS2 变量在您的示例中是否正确排序?
  • 当您说..I write some arbitrary data to the second MemoryStream and then copy the contents of the first stream to the second stream 时,您是否知道您正在替换第二个流的BOM
  • @James:这只是一段代码。调用 CopyFrom 方法时,MS2 已经填充了数据。
  • @RRUZ:是的,我想到了。但我看不出将 BOM 写入第二个流将如何解决该问题。我以前从未将 BOM 写入任何流,并且从/向流复制从未给我这种行为。
  • @James:我把代码改得更清楚了

标签: delphi delphi-xe2


【解决方案1】:

似乎DestMS 只是一些任意字节,SourceMS 是您的 Unicode 内容所在的位置。如果将source 附加到dest,则来自sourceBOM 将不会位于内存流的开头。当您在 Windows 中打开保存的文本文件时,它不会看到 BOM,因为它不在文件的开头,因此它不会知道文件后面的其他字符应该被视为 Unicode。

您似乎正试图在 Unicode 内容的前面插入一些内容。

如果这是真的,那么您可以将 Unicode 内容放在符合 Unicode 的控件中,将字符添加到开头,然后从控件中捕获内容。这会将BOM 保留在字节流的开头。

【讨论】:

    【解决方案2】:

    如果我们仅根据发布的 5 行代码来判断,这就是可能发生的情况。 TMemoryStream 不会以任何方式更改字节,我们必须假设原始字节已成功从一个 .txt 文件复制到另一个文件。两个文件应该包含完全相同的字节,但是在使用文本查看器应用程序查看文件时,这些相同的字节不会以相同的方式解释。

    我只能想象这样一种情况:

    • 其中一个文件有一个 BOM,很可能是 UTF8。
    • 另一个文件没有 BOM,所以它被解释为 ANSI。

    哪个文件有 BOM 甚至都无关紧要:经历这样的过程会改变字节的解释方式。根据 Wikipedia 的说法,绝大多数代码页都是 ASCII 的超集,这意味着可以使用 7bit 写入的所有字节都以与 UTF8 和 ANSI 完全相同的方式解释。 OP 抱怨的“Unicode”字符肯定在“扩展”ANSI(8 位)中,或者在使用 UTF8 时,它们是使用 2 个或更多字节组成的。这给出了故障模式:

    • 如果原始文件是包含扩展字符(非 ASCII)的 ANSI 文件,如果将其解释为 UTF8,结果可能看起来有点像垃圾:原始文件的两个(或更多)字符似乎被一些奇怪的字符取代。
    • 如果原始文件是 UTF8,则所有国际字符都将使用至少两个字节表示:根据 PC 的代码页,当解释为 ANSI 时,这两个字节将表示为两个不同的字符。

    【讨论】:

      【解决方案3】:

      CopyFrom 确实将整个源流复制到目标流中,但它从目标的当前位置开始。之前写的任意数据依然存在!

      你应该在调用CopyFrom之前设置MS1.Position := 0

      【讨论】:

      • 也许MS1应该在执行CopyFrom之前被清除,这样如果MS2小于MS1,额外的字节就不会留在流的末尾之后复制。
      • 您好 Uwe,感谢您的回答。实际上,如果在 CopyFrom 调用中传递 0 作为第二个参数(参数 Count),则 Source.Position 设置为零,并复制整个流(检查 System.Classes.pas 中的方法 TStream.CopyFrom 源)
      • @Alexandre,是的,这就是我写的。但是源流被附加到目标流 - 它不会替换当前位置之前的内容。
      • @Uwe,是的,但这就是我的意图。我希望将 unicode 字符串(来自第二个流)附加到第一个流,而不会丢失 unicode。
      • @Alexandre - 即使没有 BOM,通过在文件开头强加任意数据,您也会破坏字符串的 unicode 特性。按照 James 的建议使用十六进制编辑器查看文件,你会发现它的结构是如何被破坏的。
      猜你喜欢
      • 1970-01-01
      • 2021-01-25
      • 1970-01-01
      • 2019-04-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多