Pythonで実行する【アウトライン・プロセッサー】コメント削除機能あり

トマトジュー酢

実況プログラミング

 プログラムは読める、でも書けない、自信がない、という方に捧げる、実況プログラミングを行います。


 お題は、アウトライン・プロセッサーの作成です。


 アウトライン・プロセッサーとは、見出しと連携したテキストを合成する処理です。


 小説や論文作成で使われています。


 なんと言っても、編集が自由自在です。


 場面1

 場面2

 場面3


 の見出しの並びに


 場面1

  場面1-1

 場面2

 場面3


 とか


 場面1

 場面3


 とか、本文の編集が数行の操作で実現できます。


 作ろうと思い立ったのは、当方ポメラの古い機種で小説を書いていて、場面編集がいちいちファイルを開いてコピペが面倒、というわけで始めます。


 さて、目標が定まったので、仕様を決めます。


 テキストファイルの先頭行は見出しとします。

 本文から見出しを引用する方法は、


 [見出し]

  [ 見出し ]


 など、半角のカッコを見出し指示とします。

 階層構造にも対応します。

 それ以外は、本文でそのまま出力します。

 ただし、行頭に半角#がある行は、コメントとみなして出力しません。


# コメントだよ


 実行ディレクトリに存在する拡張子がtxtのファイルは、見出しを含むとみなします。

 つまり、拡張子がtxtのファイルを全て読み込みます。


 これで、仕様が完成しました。


 プログラムを組む前に、仕様を詰めるのはとても大事です。


 さて、プログラミングを始めましょう。


 まずは、日本語のファイルを読み込まなければなりません。

 python utf8 で調べるとcodecsというモジュールを使えばいいことがわかりました。

 さっそく、webのサンプルプログラムをコピペして作成します。


------------ outliner.py

# coding:utf-8

# run on python2.7x


import codecs

import sys


class Context:

  def __init__(self, filename):

    f = codecs.open(filename, 'r', 'utf-8')

    self.lines_ = f.readlines()

    f.close()


    print len(self.lines_)


    for l in self.lines_:

      print l


if 1 < len(sys.argv):

  root = Context(sys.argv[1])


------------ root.txt

てすと

# コメントだよ

[ 操縦士訓練学校を受験する ]

 [ 初任務 ]

 [ 初任務a ]

------------


 root.txtを引数に実行すると、期待通りに本文が読み込まれました。


 次は、実行ディレクトリに存在する拡張子がtxtのファイルを列挙して読み込みます。

 ファイルの読み込みは、Contextクラスで対応したので、ファイルを列挙をする方法を調べます。

 python ファイル 列挙 で調べるとglobというモジュールを使えばいいことがわかりました。


------------ outliner.py

# coding:utf-8

# run on python2.7x


import codecs

import sys

import glob


class Context:

  def __init__(self, filename):

    f = codecs.open(filename, 'r', 'utf-8')

    self.lines_ = f.readlines()

    f.close()


  def dump(self):

    for l in self.lines_:

      print l


if 1 < len(sys.argv):

  root = Context(sys.argv[1])


  filenames = glob.glob('*.txt')


  for filename in filenames:

    print filename

    a = Context(filename)

    a.dump()

------------


 期待通りに、拡張子がtxtのファイルを読み込めました。

 次は、各ファイルの先頭行、つまり見出しを出力してみます。


------------ outliner.py

# coding:utf-8

# run on python2.7x


import codecs

import sys

import glob


class Context:

  def __init__(self, filename):

    f = codecs.open(filename, 'r', 'utf-8')

    self.lines_ = f.readlines()

    f.close()


  def dump(self):

    for l in self.lines_:

      print l


  def line(self, n = 0):

    if len(self.lines_) <= n:

      return None

    return self.lines_[n].rstrip()


if 1 < len(sys.argv):

  root = Context(sys.argv[1])


  filenames = glob.glob('*.txt')

  for filename in filenames:

    a = Context(filename)

    k = a.line()

    if k is None:

      pass

    elif 0 < len(k):

      print k

------------


 期待通りに、先頭行の見出しを出力できました。


 次は、見出しでファイルを管理します。

 具体的には、


 contexts[見出し] = Context()


 で見出しからファイルを辿れるようにします。


------------ outliner.py

# coding:utf-8

# run on python2.7x


import codecs

import sys

import glob


class Context:

  def __init__(self, filename):

    f = codecs.open(filename, 'r', 'utf-8')

    self.lines_ = f.readlines()

    f.close()


  def dump(self):

    for l in self.lines_:

      print l


  def line(self, n = 0):

    if len(self.lines_) <= n:

      return None

    return self.lines_[n].rstrip()


if 1 < len(sys.argv):

  contexts = {}


  root = Context(sys.argv[1])


  filenames = glob.glob('*.txt')

  for filename in filenames:

    a = Context(filename)

    k = a.line()

    if k is None:

      pass

    elif 0 < len(k):

      contexts[k] = a


  for k, v in contexts.items():

    print k

------------


 期待通りに、見出しからファイルを引用することができました。


 次は、本プログラムの最も重要な処理を作成します。

 本文中の見出し引用指示をみつけます。

 以下のプログラムでは、headline()がその処理にあたります。


------------ outliner.py

# coding:utf-8

# run on python2.7x


import codecs

import sys

import glob


class Context:

  def __init__(self, filename):

    f = codecs.open(filename, 'r', 'utf-8')

    self.lines_ = f.readlines()

    f.close()


  def dump(self):

    for l in self.lines_:

      print l


  def line(self, n = 0):

    if len(self.lines_) <= n:

      return None

    return self.lines_[n].rstrip()


  def headline(self, n):

    l = self.line(n)

    if l is None:

      return None

    if len(l) <= 0:

      return None

    h = l.lstrip()

    if h[0] == '[' and h[-1] == ']':

      return h[1:-1]

    return None


if 1 < len(sys.argv):

  contexts = {}


  root = Context(sys.argv[1])

  stack = [[root.line(), 1]]


  filenames = glob.glob('*.txt')

  for filename in filenames:

    a = Context(filename)

    k = a.line()

    if k is None:

      pass

    elif 0 < len(k):

      contexts[k] = a


  while len(stack):

    cur = stack[-1]

    k = cur[0]

    n = cur[1]

    stack[-1][1] += 1

    if k in contexts:

      h = contexts[k].headline(n)

      if h is None:

        l = contexts[k].line(n)

        if l is None:

          stack.pop()

        else:

          print l

      else:

        print '[',h,']'

        stack.append([h, 1])

    else:

      print 'warning : not found ', k

------------


 期待通りに、本文中から見出し引用指示を見つけることができました。


 stackを追加して、階層構造に対応しました。


 stackの中身は、[見出し,行番号]です。


 最初に、プログラムの引数である大元のroot.txtの[見出し,1]を積みます。

 これで、root.txtの構造に沿って処理が進められます。

 本文を一行読んで、行番号を進めます。

 本文が空になったら、スタックからその[見出し,*]を降ろします。

 本文の一行が見出し指示なら、[見出し,1]を積みます。

 スタックが空になったら終了です。


 次回でプログラムが完成します。




  • Xで共有
  • Facebookで共有
  • はてなブックマークでブックマーク

作者を応援しよう!

ハートをクリックで、簡単に応援の気持ちを伝えられます。(ログインが必要です)

応援したユーザー

応援すると応援コメントも書けます

新規登録で充実の読書を

マイページ
読書の状況から作品を自動で分類して簡単に管理できる
小説の未読話数がひと目でわかり前回の続きから読める
フォローしたユーザーの活動を追える
通知
小説の更新や作者の新作の情報を受け取れる
閲覧履歴
以前読んだ小説が一覧で見つけやすい
新規ユーザー登録無料

アカウントをお持ちの方はログイン

カクヨムで可能な読書体験をくわしく知る