本文介绍了提高使用OLE和Delphi的Word文档中的Search Replace的性能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

经过一些实验,我结束了以下代码,在MSWord中执行搜索和替换。这个代码在页眉和页脚中也是完美的,包括头文件和/或页脚在第一页或奇/偶页面不同的情况。



问题是我需要为我替换的每个字符串调用 MSWordSearchAndReplaceInAllDocumentParts ,并且我得到一个不可接受的表现(4页doc字中约50个字符串的2分钟)。理想情况下当然应该是即时。



在处理页眉和页脚之前,我正在主文档中进行搜索和替换(使用wdSeekMainDocument)。在这种情况下,perofmrance是可以接受的(即使相当慢)。我只是想知道为什么这么慢:切换视图需要时间?通常,页眉或页脚包含的单词很少,所以我预计页眉和页脚中的所有搜索和替换都不会使整体性能更糟。但是这不是我观察到的。



这是代码,在底部我放置了分析器结果:

  //全局变量(只是为了方便发布到Stack Overflow)
var
aWordApp:OLEVariant; // global

//这是每个字符串执行一次的函数,我替换
函数MSWordSearchAndReplaceInAllDocumentParts;
begin
try
iseekValue:= aWordApp.ActiveWindow.ActivePane.View.SeekView;
iViewType:= aWordApp.ActiveWindow.ActivePane.View.Type;
如果iViewType<> wdPrintView然后
aWordApp.ActiveWindow.ActivePane.View.Type:= wdPrintView;
如果aWordApp.ActiveDocument.PageSetup.OddAndEvenPagesHeaderFooter然后
开始
尝试
aWordApp.ActiveWindow.ActivePane.View.SeekView:= wdSeekEvenPagesFooter;
SearchAndReplaceInADocumentPart;
除了
//不做任何事情..it无法设置上面的视图
end;
尝试
aWordApp.ActiveWindow.ActivePane.View.SeekView:= wdSeekEvenPagesHeader;
SearchAndReplaceInADocumentPart;
除了
//不做任何事情..it无法设置上面的视图
end;
结束
如果aWordApp.ActiveDocument.PageSetup.DifferentFirstPageHeaderFooter然后
开始
尝试
aWordApp.ActiveWindow.ActivePane.View.SeekView:= wdSeekFirstPageFooter;
SearchAndReplaceInADocumentPart;
除了
//不做任何事情..it无法设置上面的视图
end;
尝试
aWordApp.ActiveWindow.ActivePane.View.SeekView:= wdSeekFirstPageHeader;
SearchAndReplaceInADocumentPart;
除了
//不做任何事情..it无法设置上面的视图
end;
结束
//在主Docpart中替换
尝试
aWordApp.ActiveWindow.ActivePane.View.SeekView:= wdSeekMainDocument;
SearchAndReplaceInADocumentPart;
除了
//不做任何事情..it无法设置上面的视图
end;
//在头文件中替换
尝试
aWordApp.ActiveWindow.ActivePane.View.SeekView:= wdSeekCurrentPageHeader;
SearchAndReplaceInADocumentPart;
除了
//不做任何事情..it无法设置上面的视图
end;
//替换页脚
尝试
aWordApp.ActiveWindow.ActivePane.View.SeekView:= wdSeekCurrentPageFooter;
SearchAndReplaceInADocumentPart;
除了
//不做任何事情..it无法设置上面的视图
end;
//在头文件中替换
尝试
aWordApp.ActiveWindow.ActivePane.View.SeekView:= wdSeekPrimaryHeader;
SearchAndReplaceInADocumentPart;
除了
//不做任何事情..it无法设置上面的视图
end;
//替换页脚
尝试
aWordApp.ActiveWindow.ActivePane.View.SeekView:= wdSeekPrimaryFooter;
SearchAndReplaceInADocumentPart;
除了
//不做任何事情..it无法设置上面的视图
end;
finally
aWordApp.ActiveWindow.ActivePane.View.SeekView:= iseekValue;
如果iViewType<> wdPrintView然后
aWordApp.ActiveWindow.ActivePane.View.Type:= iViewType;
结束
结束

//这是在所选视图中执行搜索和替换的函数
//每个视图调用一次

function SearchAndReplaceInADocumentPart;
begin
aWordApp.Selection.Find.ClearFormatting;
aWordApp.Selection.Find.Text:= aSearchString;
aWordApp.Selection.Find.Replacement.Text:= aReplaceString;
aWordApp.Selection.Find.Forward:= True;
aWordApp.Selection.Find.MatchAllWordForms:= False;
aWordApp.Selection.Find.MatchCase:= True;
aWordApp.Selection.Find.MatchWildcards:= False;
aWordApp.Selection.Find.MatchSoundsLike:= False;
aWordApp.Selection.Find.MatchWholeWord:= False;
aWordApp.Selection.Find.MatchFuzzy:= False;
aWordApp.Selection.Find.Wrap:= wdFindContinue;
aWordApp.Selection.Find.Format:= False;
{执行搜索}
aWordApp.Selection.Find.Execute(Replace:= wdReplaceAll);
结束

这里我粘贴分析结果(我有aqtime pro):



您能帮助我找出问题吗?

解决方案

在我的机器上测试时,我没有看到这样可怕的性能,但仍然有办法提高性能。 / p>

最大的改进是将 aWordApp.ActiveWindow.Visible 设置为 False

第二个改进是设置 aWordApp.ScreenUpdating False



当您连续多次调用MSWordSearchAndReplaceInAllDocumentParts时,应用上述设置一次。另外,在多次调用MSWordSearchAndReplaceInAllDocumentParts之前,将 ActiveWindow.ActivePane.View.Type 设置为 wdPrintView



编辑:



我通过改变你找到/替换的方式得到了另一个改进: SeekView,遍历所有部分,并自己获取文档,页眉和页脚的范围,并对这些范围进行查找/替换。

  procedure TForm1.MSWordSearchAndReplaceInAllDocumentParts(const aDoc:OleVariant); 
var
i:整数;
lSection:OleVariant;
演员:OleVariant;
lFooters:OleVariant;
l部分:OleVariant;
begin
lSections:= aDoc.Sections;
for i:= 1 to lSections.Count do
begin
lSection:= lSections.Item(i);
演讲者:= lSection.Headers;
lFooters:= lSection.Footers;
如果lSection.PageSetup.OddAndEvenPagesHeaderFooter然后
begin
SearchAndReplaceInADocumentPart(1Headers.Item(wdHeaderFooterEvenPages).Range);
SearchAndReplaceInADocumentPart(lFooters.Item(wdHeaderFooterEvenPages).Range);
结束
如果lSection.PageSetup.DifferentFirstPageHeaderFooter然后
begin
SearchAndReplaceInADocumentPart(1Headers.Item(wdHeaderFooterFirstPage).Range);
SearchAndReplaceInADocumentPart(lFooters.Item(wdHeaderFooterFirstPage).Range);
结束
SearchAndReplaceInADocumentPart(1Headers.Item(wdHeaderFooterPrimary).Range);
SearchAndReplaceInADocumentPart(lFooters.Item(wdHeaderFooterPrimary).Range);

SearchAndReplaceInADocumentPart(lSection.Range);
结束
结束

程序TForm1.SearchAndReplaceInADocumentPart(const aRange:OleVariant);
begin
aRange.Find.ClearFormatting;
aRange.Find.Text:= aSearchString;
aRange.Find.Replacement.Text:= aReplaceString;
aRange.Find.Forward:= True;
aRange.Find.MatchAllWordForms:= False;
aRange.Find.MatchCase:= True;
aRange.Find.MatchWildcards:= False;
aRange.Find.MatchSoundsLike:= False;
aRange.Find.MatchWholeWord:= False;
aRange.Find.MatchFuzzy:= False;
aRange.Find.Wrap:= wdFindContinue;
aRange.Find.Format:= False;

{执行搜索}
aRange.Find.Execute(Replace:= wdReplaceAll);
结束

如果您在应用程序不可见时打开要修改的文档,您将看到更大的改进,或者如果您使用Visible:= False打开文档; (再次设置应用程序也会将文档设置为可见)。


After some experiments I ended up with the following code to perform Search and Replace in MSWord. This code works perfectly also in header and footer, including the cases in which header and/or footer are different for the first page or odd/even pages.

The problem is that I need to call MSWordSearchAndReplaceInAllDocumentParts for every string I replace, and I get an unacceptable performance (2 minutes for about 50 strings in a 4 pages doc word). Ideally it should be "instantaneous" of course.

Before handling headers and footers I was just doing search and replace in the main document (using wdSeekMainDocument). In that case the perofmrance was acceptable (even if quite slow). I just wonder why is it so slow: does switching view takes time? Typically headers or footers contain few words, so I expected that all the Search And Replace in headers and footers was not making the overall performance so worse. But this is not what I observed.

This is the code, at the bottom i put profiler results:

// global variable (just for convenience of posting to Stack Overflow)   
var
 aWordApp: OLEVariant; // global

// This is the function that is executed once per every  string I replace
function MSWordSearchAndReplaceInAllDocumentParts;
begin
    try
      iseekValue := aWordApp.ActiveWindow.ActivePane.View.SeekView;
      iViewType := aWordApp.ActiveWindow.ActivePane.View.Type;
      if iViewType <> wdPrintView then
        aWordApp.ActiveWindow.ActivePane.View.Type := wdPrintView;
      if aWordApp.ActiveDocument.PageSetup.OddAndEvenPagesHeaderFooter then
      begin
        Try
          aWordApp.ActiveWindow.ActivePane.View.SeekView := wdSeekEvenPagesFooter;
          SearchAndReplaceInADocumentPart;
        Except
            // do nothing ..it was not able to set above view
        end;
        Try
          aWordApp.ActiveWindow.ActivePane.View.SeekView := wdSeekEvenPagesHeader;
          SearchAndReplaceInADocumentPart;
        Except
          // do nothing ..it was not able to set above view
        end;
      end;
      if aWordApp.ActiveDocument.PageSetup.DifferentFirstPageHeaderFooter then
      begin
        Try
          aWordApp.ActiveWindow.ActivePane.View.SeekView := wdSeekFirstPageFooter;
          SearchAndReplaceInADocumentPart;
        Except
          // do nothing ..it was not able to set above view
        end;
        Try
          aWordApp.ActiveWindow.ActivePane.View.SeekView := wdSeekFirstPageHeader;
          SearchAndReplaceInADocumentPart;
        Except
          // do nothing ..it was not able to set above view
        end;
      end;
      //Replace in Main Docpart
      Try
        aWordApp.ActiveWindow.ActivePane.View.SeekView := wdSeekMainDocument;
        SearchAndReplaceInADocumentPart;
      Except
          // do nothing ..it was not able to set above view
      end;
      //Replace in Header
      Try
        aWordApp.ActiveWindow.ActivePane.View.SeekView := wdSeekCurrentPageHeader;
        SearchAndReplaceInADocumentPart;
      Except
          // do nothing ..it was not able to set above view
      end;
      //Replace in Footer
      Try
        aWordApp.ActiveWindow.ActivePane.View.SeekView := wdSeekCurrentPageFooter;
        SearchAndReplaceInADocumentPart;
      Except
          // do nothing ..it was not able to set above view
      end;
      //Replace in Header
      Try
        aWordApp.ActiveWindow.ActivePane.View.SeekView := wdSeekPrimaryHeader;
        SearchAndReplaceInADocumentPart;
      Except
        // do nothing ..it was not able to set above view
      end;
      //Replace in Footer
      Try
        aWordApp.ActiveWindow.ActivePane.View.SeekView := wdSeekPrimaryFooter;
        SearchAndReplaceInADocumentPart;
      Except
        // do nothing ..it was not able to set above view
      end;
    finally
      aWordApp.ActiveWindow.ActivePane.View.SeekView := iseekValue;
      if iViewType <> wdPrintView then
        aWordApp.ActiveWindow.ActivePane.View.Type := iViewType;
    end;
end;

// This is the function that performs Search And Replace in the selected View
 // it is called once per view

function SearchAndReplaceInADocumentPart;
begin
    aWordApp.Selection.Find.ClearFormatting;
    aWordApp.Selection.Find.Text := aSearchString;
    aWordApp.Selection.Find.Replacement.Text := aReplaceString;
    aWordApp.Selection.Find.Forward := True;
    aWordApp.Selection.Find.MatchAllWordForms := False;
    aWordApp.Selection.Find.MatchCase := True;
    aWordApp.Selection.Find.MatchWildcards := False;
    aWordApp.Selection.Find.MatchSoundsLike := False;
    aWordApp.Selection.Find.MatchWholeWord := False;
    aWordApp.Selection.Find.MatchFuzzy := False;
    aWordApp.Selection.Find.Wrap := wdFindContinue;
    aWordApp.Selection.Find.Format := False;
    { Perform the search}
    aWordApp.Selection.Find.Execute(Replace := wdReplaceAll);
end;

Here i paste profiling results (i have aqtime pro):

Can you please help me in pinpointing the problem?

解决方案

I didn't see such terrible performance when testing on my machine, but still, there are ways to improve performance.

Biggest improvement is setting the aWordApp.ActiveWindow.Visible to False before calling MSWordSearchAndReplaceInAllDocumentParts.

Second improvement is setting aWordApp.ScreenUpdating to False.

When you are calling MSWordSearchAndReplaceInAllDocumentParts multiple times in a row, apply above settings once. Also, set ActiveWindow.ActivePane.View.Type to wdPrintView before calling MSWordSearchAndReplaceInAllDocumentParts multiple times.

Edit:

I got another improvement by changing the way you de find/replace: Instead of changing the SeekView, iterate through all the sections and get the range of the document, headers and footers yourself and do a Find/Replace over those ranges.

procedure TForm1.MSWordSearchAndReplaceInAllDocumentParts(const aDoc: OleVariant);
var
  i: Integer;
  lSection: OleVariant;
  lHeaders: OleVariant;
  lFooters: OleVariant;
  lSections: OleVariant;
begin
  lSections := aDoc.Sections;
  for i := 1 to lSections.Count do
  begin
    lSection := lSections.Item(i);
    lHeaders := lSection.Headers;
    lFooters := lSection.Footers;
    if lSection.PageSetup.OddAndEvenPagesHeaderFooter then
    begin
      SearchAndReplaceInADocumentPart(lHeaders.Item(wdHeaderFooterEvenPages).Range);
      SearchAndReplaceInADocumentPart(lFooters.Item(wdHeaderFooterEvenPages).Range);
    end;
    if lSection.PageSetup.DifferentFirstPageHeaderFooter then
    begin
      SearchAndReplaceInADocumentPart(lHeaders.Item(wdHeaderFooterFirstPage).Range);
      SearchAndReplaceInADocumentPart(lFooters.Item(wdHeaderFooterFirstPage).Range);
    end;
    SearchAndReplaceInADocumentPart(lHeaders.Item(wdHeaderFooterPrimary).Range);
    SearchAndReplaceInADocumentPart(lFooters.Item(wdHeaderFooterPrimary).Range);

    SearchAndReplaceInADocumentPart(lSection.Range);
  end;
end;

procedure TForm1.SearchAndReplaceInADocumentPart(const aRange: OleVariant);
begin
  aRange.Find.ClearFormatting;
  aRange.Find.Text := aSearchString;
  aRange.Find.Replacement.Text := aReplaceString;
  aRange.Find.Forward := True;
  aRange.Find.MatchAllWordForms := False;
  aRange.Find.MatchCase := True;
  aRange.Find.MatchWildcards := False;
  aRange.Find.MatchSoundsLike := False;
  aRange.Find.MatchWholeWord := False;
  aRange.Find.MatchFuzzy := False;
  aRange.Find.Wrap := wdFindContinue;
  aRange.Find.Format := False;

  { Perform the search}
  aRange.Find.Execute(Replace := wdReplaceAll);
end;

You will see even a bigger improvement if you open the document you want to modify while the application is invisible, or if you open the document with Visible := False; (setting the application visible again will also set the document visible).

这篇关于提高使用OLE和Delphi的Word文档中的Search Replace的性能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-18 23:41