見出し画像

Pythonで複数ファイルから見出し行を抽出して目次を作成する

VS Codeだと、マークダウンで原稿を書いていれば、Outlineに見出し行が抽出されて表示されます。

画像1

これは結構便利なのですが、章ごとにファイルを分割していると、その章の見出ししか出てきません。アウトラインを切り替えるためには、ファイルを切り替える必要があります。

だったら、原稿ファイルを走査して見出し行を抽出して、それをテキストファイルか何かに書き出せばいいんじゃね?

やってみましょう。

まずイメージしているのは、

python makeTox.py *.md

という形。プログラムファイルの後ろに、見出しを作りたいファイル名を与えたら、そこから抽出してくれる、というもの。でもって、原稿をすべて.mdで保存していれば、それらすべてを走査してくれることになります。

で、標準入出力を扱うから、「import sys」は必須でしょう(これは以前勉強した)。

あと、受け取った引数の配列の一つ目は、プログラム名になっているから、二つ目移行を利用することに。つまり、

import sys
a = sys.argv[1:]

です。

あとは、この配列からfileをopenしていけばよさそうですね。一行ごとに処理するので、readlinesが使えるはず。

with open(path) as f:
   l = f.readlines()

このpathにループで、さっきのaの中身を放り込んでいけばいけそうです。リストの中身を取り出す繰り返しは以下。

for item in list1:

さっそく書いてみましょう。

import sys
a = sys.argv[1:]
mainbody = [] #ここに見出し行を入れていくよ
for tfilepath in a:
   with open(tfilepath) as f:
       l = f.readlines()
       for line in l:

あとは、ここで取り出した行が「## hoge」みたいな形になっていたら、それを取り出してmainbodyに放り込めばOKです、というところで正規表現を使う必要性に気がつきました。

というわけで、「import re」も追加しておきます。で、それはいいとして、見出し行を抜き出すのって、どうするんだと深く考える前に、CotEditorのマークダウンのシンタックスカラーの指定方法をパクりましょう。

画像2

深く考えずに、これをコピーすればOKそうです(使えるものはなんでも使おうの精神で)。

あとは、判定処理のイメージですが、もしその行が正規表現のパターンにマッチする場合は、マッチした部分をmainbodyに格納して、それ以外はスルーというのでよいでしょうか。

for line in l:
           h1 = re.match(r'^#[\t ]?([^#][^\n]*?)#*$', line)
           if h1:
               mainbody.append(h1.group())

あとはこれを使いたい見出しの深さ分繰り返せばいいですね。私は見出し3までしかまず使わないので、h3まで対応していれば(いまのところ)OKです。

 for line in l:
           h1 = re.match(r'^#[\t ]?([^#][^\n]*?)#*$', line)
           if h1:
               mainbody.append(h1.group())
           h2 = re.match(r'^#{2}[\t ]?([^#][^\n]*?)#*$', line)
           if h2:
               mainbody.append(h2.group())
           h3 = re.match(r'^#{3}[\t ]?([^#][^\n]*?)#*$', line)
           if h3:
               mainbody.append(h3.group())

見出しごとに処理を分けるのは面倒な気もしますが、たとえばインデントに差を与えるとかもできそうなので、このままにしておきましょう。

あとは、このmainbodyをテキストファイルに書き出せばOKです。toc.txtとしておきます。

with open(tocfilepaty, mode='w') as f:
   f.writelines('\n'.join(mainbody))

というわけで、以下で完成です。

import sys
import re
tocfilepaty = 'toc.txt'
a = sys.argv[1:]
mainbody = [] #見出し行を入れる
for tfilepath in a:
   with open(tfilepath) as f:
       l = f.readlines()
       for line in l:
           h1 = re.match(r'^#[\t ]?([^#][^\n]*?)#*$', line)
           if h1:
               mainbody.append(h1.group())
           h2 = re.match(r'^#{2}[\t ]?([^#][^\n]*?)#*$', line)
           if h2:
               mainbody.append(h2.group())
           h3 = re.match(r'^#{3}[\t ]?([^#][^\n]*?)#*$', line)
           if h3:
               mainbody.append(h3.group())
with open(tocfilepaty, mode='w') as f:
   f.writelines('\n'.join(mainbody))

これを「python makeToc.py *.md」すると、以下のように見出しが抽出されたものがtoc.txtに書き込まれます。

画像3

見出しの高さが論理的でないのは、この見出しの抽出をイメージしてファイルを作っていないからです。プログラムのエラーというわけではありませんのであしからず。

とりあえず、OKですね。

この記事が気に入ったらサポートをしてみませんか?