22行のPythonコードでWordのgrepコマンドを作る
設計書を書く上で最も使用されているフォーマットは、Microsoft Officeの「Word」が提供している「docx」拡張子であることは言うまでもないでしょう。
「Word」ファイルの欠点としては、textファイルのようにgrepして検索できない問題点が挙げられます。
そこで、「Python 3」の「python-docx」を使用したdocx用grep機能を作成してみました。
ソースコードの全体
今回作成したソースコードの全体がこちらとなります。
# -*- coding: utf-8 -*-
from docx import Document
import glob
import sys
argvs = sys.argv
argc = len(argvs)
if (argc != 3):
print('引数が足りません. 検索文字 検索先')
quit() # プログラムの終了
files = glob.glob(argvs[2] + '/**/*.docx', recursive=True)
for fil in files:
document = Document(fil)
count =0
for para in document.paragraphs:
count+=1
if para.text.find( argvs[1]) > -1:
print(fil + ":" + '行数' + str(count) + ':' + para.text)
このようにdocxファイルのgrep機能は、22行で作成することができます。
(少ないコードで機能を実装できるPythonはいいですね)
今回使用するモジュールは、以下の通りとなります。
- python-docx
- glob
- Document
- sys
始める前に必要なライブラリをpipでインストールしましょう!
まずは、メインで使用する「python-docx」が入っていなければ、以下のコマンドでインストールすることができます。
pip install python-docx
コードの解説
インポートと文字コード
一行目に文字コードを宣言した上で、必要なライブラリをインポートします。
# -*- coding: utf-8 -*-
from docx import Document
import glob
import sys
今回は、python-docxを使用するのですが、「docx」ファイルを読み込むためにDocumentを宣言しております。
目指すべき処理としては、「glob」によりファイルの一覧を取得し、Documentで「docx」の中身を取り出す流れとなります。
引数の受け取りとチェック処理
引数を受け取るために「sys」から値を取得します。
argvs = sys.argv
argc = len(argvs)
「argvs」を宣言し、値を取るための変数を作成します。
「argc」は、引数の数が足りているかチェックを行うための変数となります。
以下が「argc」を使用した引数のチェック処理となります。
if (argc != 3):
print('引数が足りません. 検索文字 検索先')
quit() # プログラムの終了
今回のプログラムでは、引数は以下の2つが必要となります。
- 検索キーワード
- 検索するフォルダ
そのため、if文にて「argc」の長さが3ではない時にエラーとなるようなチェックを行っております。
チェック処理によりエラーとなった場合は、エラーメッセージの表示後、「quit()」でプログラムを終了します。
フォルダ階層を再帰的に検索し、配列として持つ
指定したフォルダからファイルを配列として持つために「glob」を使用します。
files = glob.glob(argvs[2] + '/**/*.docx', recursive=True)
再帰的にフォルダの中からファイルを取得する処理は、「recursive=True」を指定することで実装可能です。
検索する処理は正規表現を利用して得られるため、Pythonの引数である「argvs[2]」から正規表現でdocxファイルの一覧を取得します。
この一行により、「glob.glob」で指定したフォルダ階層にあるdocxファイルを配列として持つことができます。
glob配列をループして文字列を検索
globにて得られたdocxファイルの配列を1ファイルずつ1行ごとに文字検索しながらループします。
for fil in files:
document = Document(fil)
count =0
for para in document.paragraphs:
count+=1
if para.text.find( argvs[1]) > -1:
print(fil + ":" + '行数' + str(count) + ':' + para.text)
pipでインストールしたpython-docxを使用するために、「Document」へ「ファイル一覧」が格納された配列を渡します。
「document.paragraphs」でワードの文字列を行ごとに配列として格納することができます。
1番目のループ処理内になる2行目のカウントは、ヒットしたキーワードがWordファイルの何行目にあたるのか確認するために宣言しております。
2番目のループでは、配列化された「Word」の文章の長さで回し、「find」で配列の中に検索値(引数)があるかを判定しております。
判定結果により検索値が含まれていた場合は、行数と該当配列の中身(該当行)を出力します。
基本的に「python [ファイル名].py [検索キーワード] [対象フォルダ]」で利用することができますが、よく使うのであれば「.bashrc」のaliasに登録すると良いでしょう。
alias wordgrep="python [ファイル階層]/[ファイル名].py $@ "
課題
22行という少ないコードで実装することができたが、「python-docx」に依存しているため、古い拡張子である「doc」などは検索することができません。
また、grepをベースとして考えているため、ページ数ではなく行数でOutPutされるため、該当行を探すことが困難です。
あるあるですが、フォルダ構造のチェック処理などの文字列チェックがあれば、なお良いと思います。