如何从 UnicodeString 加载 TMemoryStream

How to load a TMemoryStream from a UnicodeString

我试图用计算出的 RTF 文件的字符串填充 DevExpress VCL TdxRichEditControl,而没有花时间保存文件。所以,这是一个演示项目:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    UnicodeString txt = "{\rtf1\deff0{\fonttbl{\f0 Calibri;}{\f1 Arial Unicode MS;}{\f2 Britannic Bold;}}{\colortbl ;\red0\green0\blue255 ;\red255\green0\blue0 ;}{\*\defchp \fs22}{\*\defpap \sl275\slmult1\sa200}{\stylesheet {\ql\sl275\slmult1\sa200\fs22 Normal;}{\*\cs1\fs22 Default Paragraph Font;}{\*\cs2\sbasedon1\fs22 Line Number;}{\*\cs3\ul\fs22\cf1 Hyperlink;}{\*\ts4\tsrowd\fs22\ql\sl275\slmult1\sa200\tscellpaddfl3\tscellpaddl108\tscellpaddfb3\tscellpaddfr3\tscellpaddr108\tscellpaddft3\tsvertalt\cltxlrtb Normal Table;}{\*\ts5\tsrowd\sbasedon4\fs22\ql\sl275\slmult1\sa200\trbrdrt\brdrs\brdrw10\trbrdrl\brdrs\brdrw10\trbrdrb\brdrs\brdrw10\trbrdrr\brdrs\brdrw10\trbrdrh\brdrs\brdrw10\trbrdrv\brdrs\brdrw10\tscellpaddfl3\tscellpaddl108\tscellpaddfr3\tscellpaddr108\tsvertalt\cltxlrtb Table Simple 1;}}{\*\listoverridetable}\nouicompat\splytwnine\htmautsp\sectd\pard\plain\ql\sl275\slmult1\sa200{\f1\fs28\cf1 This is in blue }{\b\i\f2\fs40\cf2 Red bold}\b\i\fs22\cf2\par}";
    TMemoryStream *AStream = new TMemoryStream();

    AStream->WriteBuffer(txt.c_str(), txt.Length());
    AStream->Position = 0;

    AStream->SaveToFile("C:/Trash/Test.rtf");
    AStream->Position = 0;

    dxRichEditControl1->Document->InsertDocumentContent(dxRichEditControl1->Document->Range->Start, AStream, TdxRichEditDocumentFormat::Rtf);
    AStream->Free();
}

我在这个演示中将它保存到一个文件中以查看出了什么问题,发现该文件在每个其他字符中都包含一个 space(所以,当然,我试图用这个填充的组件数据无法显示任何内容)。当我查看文件时,这是我看到的内容类型:

{ \ r t f 1 \ d e f f 0 { \ f o n t t b l { \ f 0   C a l i b r i ; } { \ f 1   A r i a l   U n i c o d e   M S ; } { \ f 2   B r i t a n n i c   B o l d ; } } { \ c o l

我想坚持将 txt 变量存储为 UnicodeString,因为实际项目有一个 class 来计算该字符串,但是有什么解决这个问题的想法吗?我意识到我可能会做一些耗时的事情,比如遍历并消除所有其他字符,但我想要一些有效的解决方案。

RTF 是一种 7 位 ASCII 格式,因此您根本不应将其视为 UTF-16。正确的解决方案是简单地将 UnicodeString 更改为 AnsiString:

AnsiString txt = ...;

"extra spaces" 是由于在 UTF-16 中将 7 位 ASCII 字符扩展为 16 位值时添加了 0x00 个字节。

此外,您甚至没有将 UnicodeString 正确写入 TMemoryStream。由于 WriteBuffer() 处理原始字节,而不是字符串字符,因此您需要将 txt.Length() 乘以 sizeof(System::WideChar) 以获得正确的字节数:

UnicodeString txt = ...;
...
AStream->WriteBuffer(txt.c_str(), txt.Length() * sizeof(WideChar));

如果改为AnsiString,则不需要乘法,因为sizeof(AnsiChar)是1。

但是,如果您坚持使用 UnicodeString,那么至少要使用 TStringStream,这样您就可以为其指定字节编码:

UnicodeString txt = ...;
TStringStream *AStream = new TStringStream(txt, TEncoding::ASCII);
...

此外,由于流是使用 new 创建的,您应该将 AStream->Free(); 更改为 delete AStream;。切勿在 C++ 中直接调用 TObject::Free() ,这是一个 Delphi 习惯用法。释放使用 new 分配的内容的 C++ 方法是 delete。它将按预期调用 TObject 析构函数,就像 TObject::Free() 一样。