如何使用正确的格式创建 TMemoryStream 和下一个 LoadFromStream 到 TRichEdit

问题描述 投票:0回答:1

我有一大串 RTF 格式的数据。格式正确,将其放入 ANSI 文本文件中,将其重命名为 *.rtf,写字板将正确显示它。

该字符串本质上是 std:wstring,而不是 TUnicodeString

如果我执行以下操作,文本将正确显示并具有正确的颜色格式等:

TStringStream *Stream = new TStringStream(String(MyString.c_str(), MyString.size())) ;
Stream->Position = 0 ;
RichEdit1->PlainText = false ;
RichEdit1->Lines->LoadFromStream(Stream) ;
delete Stream ;

一切都好,它可以工作,但我正在考虑避免创建字符串时发生的 memcpy,这将节省一些特别大的字符串的资源。

我的目标是创建一个

TCustomMemoryStream
后代,它以 MyString 作为输入,并在构造过程中通过调用
SetPointer((void*)MyString.c_str(), MyString.Length() * 2 /*Size in bytes*/)
使用其内部内存。

如果小心处理,这可以节省 memcpy(MyString 必须比 Stream 等更长寿),并且这是一个简单快速的实现。

可悲的是..它无法正常工作,我似乎不明白为什么?我有一个可行的解决方案,我可以继续..但这让我烦恼..所以请启发我。

测试的实现略有不同,但归根结底是相同的:

TMemoryStream *Stream = new TMemoryStream() ;
Stream->Write((void*)MyString.c_str(), MyString.length() * 2 /*Bytes*/) ;
Stream->Position = 0 ;
RichEdit1->PlainText = false ;
RichEdit1->Lines->LoadFromStream(Stream) ;
delete Stream ;

RichEdit 无法显示格式化文本。相反,它显示纯文本(字符间隔开)。我理解这是编码不正确的情况,这是有道理的。

所以我告诉 LoadFromStream() 使用什么编码:

TMemoryStream *Stream = new TMemoryStream() ;
Stream->Write((void*)MyString.c_str(), MyString.length() * 2 /*Bytes*/) ;
Stream->Position = 0 ;
RichEdit1->PlainText = false ;
RichEdit1->Lines->LoadFromStream(Stream, TEncoding::Unicode) ;
delete Stream ;

文本现在可以正确显示 但是仍然是纯文本,rtf 未被解析。 我不明白为什么,看起来文本完整地到达了,复制粘贴到文本文件中,与早期的 rtf 文件相比,内容是相同的。

我想编码可能需要 BOM 才能正常工作(因为这是

TEncoding::Unicode
中的默认值),所以我添加了一个用于测试:

TMemoryStream *Stream = new TMemoryStream() ;
    WORD BOM = 0xFEFF ;
    Stream->Write((void*)&BOM, 2) ;
Stream->Write((void*)MyString.c_str(), MyString.length() * 2 /*Bytes*/) ;
Stream->Position = 0 ;
RichEdit1->PlainText = false ;
RichEdit1->Lines->LoadFromStream(Stream, TEncoding::Unicode) ;
delete Stream ;

但这没有什么区别。所以我尝试相反(通过不需要BOM的

TEncoding
):

TMemoryStream *Stream = new TMemoryStream() ;
Stream->Write((void*)MyString.c_str(), MyString.length() * 2 /*Bytes*/) ;
Stream->Position = 0 ;
RichEdit1->PlainText = false ;
   TUnicodeEncoding *Encoding = new TUnicodeEncoding(false /*UseBOM*/) ;
RichEdit1->Lines->LoadFromStream(Stream, Encoding) ;
   delete Encoding ;
delete Stream ;

遗憾的是,仍然只是纯文本

我在测试应用程序中尝试了一些其他的东西,加载到TMemo,保存到流,加载到RichEdit等(有各种结果),我还尝试在TStringStream构造期间设置编码,但结果很奇怪,但我不知道不想把这个问题弄乱了。

我想了解为什么 TRichEdit 无法解析 rtf,即使它似乎正确获取所有数据,因为它以纯文本方式显示

我目前正在使用 C++ Builder 12

c++builder
1个回答
0
投票

我的目标是创建一个

TCustomMemoryStream
后代,以
MyString
作为输入并使用其内部记忆

请注意,RTL 已经有一个专门用于此目的的类 -

TPointerStream

我想了解为什么 TRichEdit 无法解析 rtf,即使它似乎正确获取所有数据,因为它以纯文本方式显示

TStringStream

 存储到内存中时,
TEncoding::Default
默认使用
String
。 IOW,它实际上将
String
转换为指定(或在本例中为默认)编码,然后存储转换后的字节。

TRichEdit::Lines::LoadFromStream()
方法加载
TStream
时,如果没有显式指定
TEncoding::Default
并且流数据中不存在 BOM,它也会假定
TEncoding

这就是您的

TStringStream
测试成功的原因。您的
String
已转换为
LoadFromStream()
期望的编码。

但是,在 Windows 上

TEncoding::Default
TEncoding::ANSI
相同。如果您将 UTF-16 存储在
TMemoryStream
中,则与
TEncoding::ANSI
所期望的不匹配,因此您必须明确要使用的实际编码。

现在,当您明确指定编码时,事情仍然不起作用,因为当

PlainText
为 false 时,
TRichEdit
在向自身发出
SF_RTF
窗口消息时使用 SF_UNICODE
without
EM_STREAMIN
。仅当
SF_UNICODE
为 true 时才使用
PlainText
(用
SF_TEXT
代替
SF_RTF
)。 RTF 是 7 位 ASCII 格式,并且
SF_RTF
无法处理 UTF-16(这也是您的
TStringStream
测试有效的原因)。

SF_RTF
失败时,
TRichEdit
将使用
SF_TEXT
SF_UNICODE
再次尝试,这就是为什么您最终会得到纯文本版本的 RTF。

所以,简而言之,使用

PlainText=false
时不要使用UTF-16数据。如果您确实想使用 UTF-16 RTF,我认为您需要实现一个自定义
TConversion
后代并将其分配给
TRichEdit::DefaultConverter
属性。

© www.soinside.com 2019 - 2024. All rights reserved.