ククログ

株式会社クリアコード > ククログ > Microsoft Office製品のファイル内の文字列を検索、置換する

Microsoft Office製品のファイル内の文字列を検索、置換する

OfficeStringReplacerというMicrosoft Office 製品1のファイル内の文字列を検索・置換するツールを開発した橋田です。

Office文書に含まれる文字列の検索、置換処理を素直に実装するとものすごく時間がかかりますが、OfficeStringReplacerでは現実的な時間に収まるように工夫をしています。 この記事ではその工夫の詳細について解説します。

検索、置換での工夫

OfficeStringReplacerでは以下のファイル形式をサポートしています。

  • .xlsx.docx.pptx: Office 2007以降の標準のExcel、Word、PowerPointのファイル形式
  • .xlsm.docm.pptm: Office 2007以降マクロ機能付きのExcel、Word、PowerPointのファイル形式
  • .xls.doc.ppt: Office 2007より前の、旧形式のExcel、Word、PowerPointのファイル形式
  • .accdb.mdb: Microsoft Accessのファイル形式

OfficeStringReplacerはC#で実装しています。Office文書の内容をC#で読み書きする正規の方法としては、Microsoft.Office.Interop.Excel.ApplicationなどのOffice 相互運用オブジェクトにアクセスするC#ライブラリ(以下、Office相互運用ライブラリ)を使う方法があります。ですが、Office相互運用ライブラリでの操作は処理が遅く、ファイル内すべてを検索、置換するような処理では、現実的な時間では実行が完了しません。 そのため、現実的な時間で実行を完了させるためには、何らかの工夫をする必要があります。

それぞれのファイル形式について、どのような工夫を行ったのか解説します。

.xlsx.docx.pptx

これらのファイルは、実体としてはOffice Open XML形式のXMLファイルをまとめたZIPファイルです。そのため、ZIPファイルとして展開・圧縮することができます。例えば、エクセルファイルのシートやセルの情報なども、すべて展開後のXMLファイルの中に含まれています。

OfficeStringReplacerでは、これらファイルをZIPファイルとして展開し、展開後のXMLファイルの中の文字列を検索、置換しています。 Office相互運用ライブラリを使って検索、置換するより、XMLファイルを検索、置換するほうが高速だからです。 ただし、単にテキストファイルとして検索、置換すると<drawing r:id="rId2"/>といったXMLタグのタグ名(drawing)など、検索、置換するべきではない場所にヒットしてしまう可能性があります。 そのため、XMLパーサーを使い、要素の値、属性の値のみが検索、置換対象となるようにしています。

具体的な実装は以下のリンクを参照してください。

https://gitlab.com/officereplacer/office-string-replacer/-/blob/main/OfficeStringReplacerCore/Domain/Processer/XmlFileProcesser.cs#L23

この工夫により、処理時間は1ファイルに数秒程度になっています。

.xlsm.docm.pptm

これらのファイルも、基本的にはOffice Open XML形式のXMLファイルをまとめたZIPファイルです。 ただし、これらのファイルは展開後のファイルにVBAマクロのプロジェクトファイルを含んでいる点が異なります。

展開後のXMLファイルについては.xlsx.docx.pptxと同様に、ZIPファイルとして展開し、XMLファイルを検索、置換するという工夫で高速化しています。 しかし、VBAマクロのプロジェクトファイルは、独自のバイナリ形式であるため、XMLファイルの場合のように単なる文字列操作で対処することができません。 Office相互運用ライブラリを使って検索、置換しています。

例として、Excel(.xlsm)の場合、.xlsmをExcel向けのOffice相互運用ライブラリであるMicrosoft.Office.Interop.Excel.Applicationで開き、Workbook.VBProjectプロパティからそのワークブックのVBプロジェクト一覧を取得します。その後、VBプロジェクト一覧からマクロ一覧を取得し、Microsoft.Vbe.Interopを使用して各マクロの文字列を検索、置換しています。

具体的な実装は以下のリンクを参照して下さい。

.docm(Word)、.pptm(PowerPoint)の場合もほぼ同様です。

以上のVBAマクロの処理には多少の時間がかかるため、処理時間は1ファイルにつき数十秒程度になっています。 処理時間はVBAマクロの数に依存します。

.xls.doc.ppt

旧形式のMicrosoft Officeのファイル形式は、独自のバイナリ形式です。 また、VBAマクロを含んでいる場合も含んでいない場合も同じファイル形式です。

これらを素直に操作する場合、Office相互運用ライブラリを使用し、ファイル全体を確認する必要があります。 しかし、前述の通り、それでは現実的な時間では実行が完了しません。そのため、以下の工夫をしています。

Office相互運用ライブラリを使用してこれらのファイルを開いた後、VBAマクロを含んでいるかどうか確認します。 VBAマクロを含んでいなかった場合、Office相互運用ライブラリ経由で.xlsx.docx.pptxとして保存します。 VBAマクロを含んでいた場合、Office相互運用ライブラリ経由で.xlsm.docm.pptmとして保存します。 こうすることで、バイナリ形式からOffice Open XML形式に変換します。

その後は、前述した通りの方法で内部の文字列を検索、置換します。 さらに、置換処理の場合は、置換処理終了後、Office相互運用ライブラリを使用して再度.xls.doc.pptとして保存し直します。

このファイル種別の変更の具体的な実装は以下のリンクを参照してください。

ファイル形式の変換のオーバーヘッドがある分、前述のケースよりも1ファイルにつき数秒~数十秒程度余分に時間がかかります。

.accdb.mdb

Microsoft Accessのファイル形式は、独自のバイナリ形式です。

これらのファイルについては、基本的にはOffice相互運用ライブラリを使用して検索、置換するしかありません。 ですが、可能な限り処理時間が短くなるよう、Accessがデータベース管理システムであることを利用して、データベースの内容の検索、置換時にはSQLクエリを実行する、という工夫をしています。 Office相互運用ライブラリでデータベースのデータを一つ一つ操作していくとかなりの時間がかかりますが、SQLクエリによってデータベースの処理としてまとめて処理することで高速化しています。

具体的な実装は以下のリンクを参照してください。

https://gitlab.com/officereplacer/office-string-replacer/-/blob/main/OfficeStringReplacerCore/Domain/Processer/AccessProcesser.cs

特にSQLクエリを実行している箇所は以下です。

https://gitlab.com/officereplacer/office-string-replacer/-/blob/main/OfficeStringReplacerCore/Domain/Searcher/ValueSearcher.cs#L94

SQLクエリを実行するという工夫により、処理時間は1ファイルにつき1分~数分程度になっています。

その他の実装上の工夫

COMオブジェクトの解放

Office相互運用ライブラリを使って外部からOffice文書を操作する場合、ライブラリで参照したCOMオブジェクトを明示的に解放する必要があります。 COMオブジェクトの解放漏れがあると、Officeのプロセスが終了しなくなるといった問題が発生します。

詳細については、以下の記事を参照して下さい。

OfficeStringReplacerでは、解放漏れをしないようにIDisposableインターフェイスを実装するリソース管理クラスを作成しています。リソース管理クラスを実装することで、開発者がCOMオブジェクトの解放を忘れるといったミスを防ぐことができます。

リソース管理クラスの具体的な実装は以下のリンクを参照してください。

https://gitlab.com/officereplacer/office-string-replacer/-/blob/main/OfficeStringReplacerCore/Model/DisposableCom.cs?ref_type=heads

以下のようにusingステートメントでこのクラスを使用します。

using (var workbooks = new DisposableCom<Workbooks>(excelApp.Value.Workbooks))
{
    ...
}

IDisposableインターフェイスを実装したクラスは、usingステートメントで呼び出すと、ブロックを抜けた際にDisposeメソッドが呼び出されます。 このDisposeメソッドにて、コンストラクタで渡したオブジェクトを解放しています。 これにより、使用したオブジェクトが必要なくなった段階で必ず解放されるようになり、解放漏れを防ぐことができます。

実際の実装での使い方は以下のリンクを参照してください。

https://gitlab.com/officereplacer/office-string-replacer/-/blob/main/OfficeStringReplacerCore/Domain/Processer/ExcelVbaScriptProcesser.cs

まとめ

今回はOfficeStringReplacerで行った工夫について紹介しました。

OfficeStringReplacerについて詳しく知りたい方や、評価版を試してみたいという方は、 お問い合わせフォーム からお気軽にご連絡ください。

また、クリアコードでは、このようにMicrosoft Office向けのソフトウェアやアドインの開発も行っています。 クリアコードにMicrosoft Office向けのソフトウェアやアドイン開発を依頼したい方も、 お問い合わせフォーム からお気軽にご連絡ください。

  1. Microsoft Office、Access、 Excel、 PowerPoint、 Word は、マイクロソフト コーポレーションの商標です。