【问题标题】:Function to extract plain text from RTF file gives wrong result从 RTF 文件中提取纯文本的函数给出错误的结果
【发布时间】:2022-01-30 11:57:47
【问题描述】:

在 Delphi 11 Alexandria 的 Windows 10 中的 32 位 VCL 应用程序中,我需要在 RTF 文件中搜索文本。所以我使用这个函数(找到here)从RTF文件中提取纯文本:

function RtfToText(const RTF_FilePath: string; ReplaceLineFeedWithSpace: Boolean): string;
var
  RTFConverter: TRichEdit;
  MyStringStream: TStringStream;
begin
  RTFConverter := TRichEdit.CreateParented(HWND_MESSAGE);
  try
    MyStringStream := TStringStream.Create(RTF_FilePath);
    try
      RTFConverter.Lines.LoadFromStream(MyStringStream);
      RTFConverter.PlainText := True;
      RTFConverter.Lines.StrictDelimiter := True;
      if ReplaceLineFeedWithSpace then
        RTFConverter.Lines.Delimiter := ' '
      else
        RTFConverter.Lines.Delimiter := #13;
      Result := RTFConverter.Lines.DelimitedText;
    finally
      MyStringStream.Free;
    end;
  finally
    RTFConverter.Free;
  end;
end;

但是,该函数返回的是 RTF 文件的文件路径,而不是 RTF 文件的纯文本内容!

这个函数有什么问题,我怎样才能有效地从 RTF 文件中提取纯文本而不必使用父级 TRichEdit 控件?

【问题讨论】:

  • 该控件能够搜索文本。见EM_FINDTEX[EX]。
  • @SertacAkyuz TRichEdit 有一个 FindText() 方法。

标签: delphi rtf delphi-11-alexandria trichedit


【解决方案1】:

TStringStream 构造函数不会像您期望的那样加载文件。 TStringStream 不是 TFileStream。顾名思义,TStringStreamstring 的流包装器。因此,它的构造函数接受一个字符串并将其原样复制到流中。因此,您正在使用文件路径字符串本身的值加载 RichEdit,而不是该字符串所引用的文件的内容。

您实际上根本不需要TStringStream,因为TRichEdit 可以直接加载文件,例如:

function RtfToText(const RTF_FilePath: string; ReplaceLineFeedWithSpace: Boolean): string;
var
  RTFConverter: TRichEdit;
begin
  RTFConverter := TRichEdit.CreateParented(HWND_MESSAGE);
  try
    RTFConverter.PlainText := False; 
    RTFConverter.Lines.LoadFromFile(RTF_FilePath);
    RTFConverter.PlainText := True;
    RTFConverter.Lines.StrictDelimiter := True;
    if ReplaceLineFeedWithSpace then
      RTFConverter.Lines.Delimiter := ' '
    else
      RTFConverter.Lines.Delimiter := #13;
    Result := RTFConverter.Lines.DelimitedText;
  finally
    RTFConverter.Free;
  end;
end;

话虽如此,本机 RTL 或 VCL 中除了 TRichEdit 之外没有任何内容可以为您将 RTF 解析为纯文本。如果您不想使用TRichEdit,则必须自己解析 RTF,或者找到要使用的第 3 方解析器。

【讨论】:

  • 将 RTF 文件加载到 TStringStream 是否比将其加载到 TRichEdit 更有效?
  • 我做了一个测试,发现使用TStringStream的版本效率稍高(更快)。
  • @user1580348 我不会说它更高效。充其量,它可能更快,因为它将整个文件的副本加载到内存中,然后 RichEdit 的解析器不会因文件 I/O 而变慢。但是,缺点是将整个文件加载到内存中,而LoadFromFile() 以较小的块读取文件。无论如何,由于你没有使用TStringStream.DataString属性,TStringStream基本上只是TMemoryStream,所以你也可以使用TMemoryStream本身。
  • @user1580348 如果您真的想要更高的效率,请找到(或编写)一个包装内存映射文件的TStream,那么您根本不会浪费分配任何内存来读取文件的开销。
【解决方案2】:

Q 中的函数将 RTF 文件路径字符串直接分配给 TStringStream,而不加载 RTF 文件(正如@Remy Lebeau 正确观察到的那样:“TStringStream 构造函数没有加载文件”)。

这是通过将 RTF 文件加载到 TStringStream 来工作的:

function RtfToText(const RTF_FilePath: string; ReplaceLineFeedWithSpace: Boolean): string;
var
  RTFConverter: TRichEdit;
  MyStringStream: TStringStream;
begin
  RTFConverter := TRichEdit.CreateParented(HWND_MESSAGE);
  try
    MyStringStream := TStringStream.Create('');
    try
      MyStringStream.LoadFromFile(RTF_FilePath);
      RTFConverter.Lines.LoadFromStream(MyStringStream);
      RTFConverter.PlainText := True;
      RTFConverter.Lines.StrictDelimiter := True;
      if ReplaceLineFeedWithSpace then
        RTFConverter.Lines.Delimiter := ' '
      else
        RTFConverter.Lines.Delimiter := #13;
      Result := RTFConverter.Lines.DelimitedText;
    finally
      MyStringStream.Free;
    end;
  finally
    RTFConverter.Free;
  end;
end;

【讨论】:

  • @MartynA 是的,它确实回答了第一个问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多