2012-03-01 5 views
11

いくつかの実験の後、次のコードを使用してMSWordで検索と置換を実行しました。このコードは、ヘッダーやフッターが最初のページや奇数/偶数ページで異なる場合を含め、ヘッダーとフッターでも完全に機能します。OLEとDelphiを使用してWord文書の検索置換のパフォーマンスを改善

問題は、私が置き換えるすべての文字列に対してMSWordSearchAndReplaceInAllDocumentPartsを呼び出す必要があり、許容できないパフォーマンス(4ページのドキュメントワードで約50文字列の2分間)が発生するということです。理想的には、それはもちろん「瞬間的」であるべきです。

ヘッダーとフッターを処理する前に、(wdSeekMainDocumentを使用して)メイン文書で検索と置換を行うだけでした。その場合、(たとえ非常に遅いとしても)輻輳は許容可能であった。私はちょうどなぜそんなに遅いのだろうか:切り替えるビューは時間がかかりますか?通常、ヘッダーやフッターには単語がほとんど含まれていないので、ヘッダーとフッターのすべての検索と置換が全体的なパフォーマンスをそれほど悪化させないと予想しました。しかし、これは私が観察したことではありません。ここで

// 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; 

iは(私はAQtimeはプロを持っている)プロファイリングの結果を貼り付けます: enter image description here

は、あなたがピンポイントで私を助けてください

この

は私がプロファイラの結果を置く下部に、コードであります問題?

+0

本当にパフォーマンスが必要な場合OLEを介してWordを使用する/ ActiveXは基本的にはそれをカットしません... Word文書を処理するためのライブラリ(Word依存関係なし)を使用していますか? – Yahia

+3

ベンチマークのために適切なサンプル文書を提供できる方が良いでしょう。 – menjaraz

+0

プロファイリングの結果を詳しく説明できますか?時間は秒単位かミリ秒単位か、ヒットあたりの時間か、すべてのヒットの累積ですか? –

答えて

8

私のマシンでテストしたときにこのようなひどいパフォーマンスは見られませんでしたが、それでもパフォーマンスを向上させる方法があります。

MSWordSearchAndReplaceInAllDocumentPartsを呼び出す前に、aWordApp.ActiveWindow.VisibleFalseに設定することが最も大きな改善点です。

第2の改善点は、aWordApp.ScreenUpdating~Falseです。

MSWordSearchAndReplaceInAllDocumentPartsを連続して複数回呼び出す場合は、上記の設定を1回適用します。また、MSWordSearchAndReplaceInAllDocumentPartsを複数回呼び出す前にActiveWindow.ActivePane.View.TypewdPrintViewに設定してください。

編集:

私はあなたがデ見つける方法を変更することにより、別の改善を得た/交換してください:すべてのセクションを反復処理し、代わりにSeekViewを変更すると、文書の範囲を取得し、ヘッダをして自分をフッターとそれらの範囲で検索/置換を行います。

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; 

あなたが可視でドキュメントを開く場合は、アプリケーションが見えないながら変更したい、または文書を開いた場合は、さらに大きな改善が表示されます。= Falseの; (アプリケーションを再び表示するように設定すると、ドキュメントが表示されるようになります)。

+0

提案してくれてありがとう、私はそれらを試してみる、彼らは意味をなさない。私が理解できない唯一のものはwdPrintVIewです。それはこれを行う利点ですか? – LaBracca

+0

もう1つのコメント:私の場合のパフォーマンスは、(プロファイラの結果によると)約150個の文字列を置き換えるため、ひどいです。 – LaBracca

+0

さらに、すでにaWordApp.AcitveWIndow.VisibleをFalseに設定しています。 – LaBracca

関連する問題