2013年11月23日土曜日

zip とか tar の中身を grep するツール

zipやtar などのアーカイブの中身を、いちいち展開することなくそのまま検索でき、 かつ、ShiftJis とか EUC など日本語コードの違いも、よしなに扱う grepライクな python スクリプトを作ってみた。
(ググって探せば、先人の作ったもっといいものが見つかるような気もするけど)

jzgrep.py

スクリプトのソースは ここ
動かすには、python2.6 以上が必要。でも、python3 では動かない。
動作確認は、linux と os-x10.6 でしか行なっていない。

使い方は、$ python jzgrep.py --help | less を参照。
$ python jzgrep.py --help
Usage: jzgrep.py [OPTIONS] PATTERN [FILE ...]
  PATTERN  -- 正規表現パターン (Python の `re' モジュールを使っています)
  FILE...  -- PATTERN で検索するファイル。省略時は stdin。
  OPTIONS
       --help -- このメッセージを表示して終了する
       -n     -- 行番号を表示
       -l     -- PATTERN に一致する行を含むファイル名だけを出力
       -H     -- 出力する行の先頭にファイル名を付ける。
                 (検索するファイルが複数の場合は、これがデフォルト)
       -h     -- 出力する行の先頭にファイル名を付けない
                 (検索するファイルが一つの場合は、これがデフォルト)
       -i     -- 大文字小文字を区別しない (re.IGNORECASE)
       -s     -- 警告メッセージを出さない (e.g [Warning]: Cannot decode ...)

対応しているファイル・タイプは、
  • 普通のテキストファイル
  • gzip, bzip2
  • 暗号化していない zip 類 (たぶん epub とか jar も)
  • tar とその圧縮ファイル( tar、 tar.gz 及び tar.bz2)
また、tar や zip の中の tar や zip 、さらにその中の tar や zip、 さらに… も再帰的に検索できる筈。

日本語エンコーディングは、今のところ
utf-8, cp932, euc-jp, iso2022_jp
の4種類だけに対応している。
(他の文字コードについては、ソース中の _encodings 変数や matchtext 関数を、 少しだけ書き換えれば対応できるかもしれない)

但し、jzgrep.py はとても遅い!
たぶん、異なる日本語コードの照合処理について、こんな感じで、
for enc in ("iso2022_jp", "utf-8", "euc-jp", "cp932")
  try:
    m = re.search(PATTERN, TEXTLINE.decode(enc))
    if m: return m
  except UnicodeError: pass
return None
総当りでチェックしているからだと思う。
それに、ほんのたまにだけど、 間違って余計なものにヒットしちゃうこともある。かな?

2013年11月4日月曜日

青空文庫の複数テキストを合本して kindle-paperwhite で読む

AozoraEpub3 という素晴らしいソフトのおかげで、青空文庫の小説を、kindle で自由に読めるようになった。
青空からテキストデータの zip をダウンロードして、それを、 AozoraEpub3で epub3 に変換し、さらに、 kindlegen で mobi に変換。あとは、usb ケーブルで kindle に転送すれだけでいい。
AozoraEpub3の作者の方には本当に感謝致します。

ただ、欲を言を言わせて貰うと、 短編やシリーズ物など、複数の小説データを合本する機能が欲しい。
まあ、TODOには優先度高として挙げられているので、 AozoraEpub3の将来のバージョンで合本の機能が追加されるのを待てばいいわけだが、 何でもかんでも人任せというのも情けない。

というわけで、 青空文庫の複数のテキストファイルを結合するスクリプトを作ってみた。
(本当は、epub レベルで結合するのが理想なんだけど、 epub3の仕様は、僕には難しすぎて無理…)

ソースコード (bindao.py)

ソースは ここ。 実行するには、python2.7 以上が必要。でも、python3 では動きません。
os-x 10.6.8 + kindle-pw でしか試していないけど、 たぶん、linux でも動作すると思う。

合本するロジックは、 「青空文庫の作品を合本して Epub 化する試み」 の方法を参考にさせて頂き、プラス、
  • 目次を階層化するために、個別作品の見出しレベルを変更
    合本上では、個別の構成作品の表題を大見出しに設定するので、 個別作品自体に大見出しがあれば、重複を避けるために、それぞれ、 大見出しを中見出しに、中見出しを小見出しに変換する。ただし、 個別作品に小見出しがあればこの変換はしない。

  • Zip のまま処理する。
    個別作品のzipアーカイブをいちいち展開する手間を省く。

  • 合本ファイルとしての、出力も zip 形式にする。
    個別作品のzip内に含まれる画像データ等を、 出力のzipファイルにも自動で埋め込ませる。

の3つの機能を施した。

使い方

ターミナルアプリなどから、shell上で、
$ python bindao.py 結合する青空形式のzipファイル....
を実行すると、
Top>
というプロンプトが出る。help と打つと、各コマンドの説明が表示されるが、 その中でも、よく使うコマンドは、
  • title 合本のタイトル
  • author 合本の著者名
  • bindzip
の3つ。
titleコマンドで合本の表題を、authorコマンドで著者名を設定し、最後に、 bindzip と打てば、bind.zip というファイルが作成される。
より詳しくは、$ python bindao.py -h を参照。

実際の例

青空文庫の小栗虫太郎の短編4本を合本してみた時のログ。
  1. まずは、青空文庫から zip ファイルをダウンロードする。
     $ curl -O http://www.aozora.gr.jp/cards/000125/files/4841_ruby_16998.zip
     $ curl -O http://www.aozora.gr.jp/cards/000125/files/666_ruby_2185.zip
     $ curl -O http://www.aozora.gr.jp/cards/000125/files/45230_ruby_26898.zip
     $ curl -O http://www.aozora.gr.jp/cards/000125/files/667_ruby_441.zip
    
  2. ダウンロードした zip を、bindao.py で結合する。
    (この場合は、-noauthor というオプションを指定しないと、 個別作品の表題の次にも著者名が書かれてしまう)
     $ python bindao.py -noauthor 4841_ruby_16998.zip 666_ruby_2185.zip \
                                   45230_ruby_26898.zip 667_ruby_441.zip
       Top> title 小栗虫太郎短編集 1
       Top> author 小栗虫太郎
       Top> show all
         合本の表題: 小栗虫太郎短編集 1
         合本の著者: 小栗虫太郎
         合本の副題: -未設定-
         構成ファイル(1): 4841_ruby_16998.zip 内の goko_satsujinjiken.txt
         ................................................................
         ................................................................
       Top> bindzip
         OK: bind.zip を出力しました
       Top> quit
      
  3. bind.zip の中身を確認
     $ unzip -l bind.zip 
        ------  ----     ----    ----
        258407  10-30-13 15:21   bind.txt
          9286  03-04-12 18:40   fig666_02.png
           674  03-04-12 18:39   fig666_01.png
           296  07-24-07 13:52   fig45230_01.png
           255  07-24-07 13:52   fig45230_02.png
          9834  02-27-06 05:33   fig45230_03.png
         12804  02-24-11 17:54   fig667_01.png
     $
    
  4. AozoraEpub3 を使って、bind.zip を epub ファイルに変換する
    $ java -jar AozoraEpub3.jar
    • 端末設定 で kindle-PW を選択
    • 表題: 本文内 「表題→著者名」を選択し、「先頭が発行者」のチェックを外す。
    • 目次設定の「目次(NCX)階層化」のチェックをいれる
    • 他はデフォルトのままで bind.zipをドラッグアンドドロップ
    • 「変換」をクリック
  5. kindlegen で mobi に変換
      $ kindlegen '[小栗虫太郎] 小栗虫太郎短編集 1.epub' -o oguri.mobi
    
  6. kindle を usb 接続して mobi ファイルをコピーする。
    osx の場合は、/Volumes/Kindle にマウントされるので、
    $ cp oguri.mobi /Volumes/Kindle/documents/
最後に、ファインダーなどで kindle device をアンマウントすることを忘れないように。

ありゃ、部分的に辞書が引けない場合がある!

AozoraEpub3 + bindao.py を使って、 小栗の他にも、久生十蘭、甲賀三郎、浜尾四郎など、 古き良き時代の探偵小説をかたっぱしから合本して、秋の読書を堪能してたんだけど、 なんだか部分的にkindleの辞書が引けない本があることに気づいた。
調べたい単語をロングタッチして、デジタル大辞泉を引いても、
「お探しの言葉が見つかりませんでした」
と出るばかり。でも、同じ言葉を同じ作品の別の段落で引くと、 ちゃんと辞書が引けたりする。
bindao.py のバグかとも思ったのだが、 AozoraEpub3単独で変換しても直らない。かといって、 この症状をググってみても全然引っかからない。 同じような問題で悩んでいる人は誰もいないようだ。もしかしたら、 僕の kindle-pw だけで起こる、固有の問題なのかもしれない。

で、いろいろ調べてみた。
なんとなく、文章量の長い段落でこの現象が起きているみたいなので、 epubファイル内本文のxhtmlファイルを開き、 問題の生じる段落で適当に句点の後(。の後)に改行をいれてみると、 辞書が引けるようになった。
でもこれだと、kindleで見た時に、改行が半角空白で表示されてしまう。
改行の代わりに、‌ という特殊シンボル(自分でも意味がわかっていない)を使っても駄目。
その他いろいろ試行錯誤の結果、 <span /> という、たぶん、意味のない空タグを、 句読点の後に入れてやると直ることがわかった。余分な半角空白も入らず、 かつ、どこでも辞書引きがOKになる。ただ mobi ファイルが太ってしまうが…

想像だけど、空白も改行もタグも途中に入らない日本語の長い段落というのは、 xhtml上では、長いワードだと認識されるのではないだろうか。 その異常に長いワードをkindleが解析する時に、 僕の環境に固有の何か他の事象が重なると、このような不具合が起きるのではないかと思う。

で、これも、 python のスクリプトでまとめてみた。
ソースコードは ここ
使い方は、AozoraEpub3 で epub を作成し、kindlegen を実行する前に、 shellからこのスクリプトを実行すればいい。
例えば、AozoraEpub3 が出力するファイル名を source.epub とすると、
 $ python aoepub3_hack.py source.epub dist.epub 
 $ kindlegen dist.epub
これで、空の span タグだらけの、変な dist.mobi が出来上がる。
(他のオプションなどは、 $ python aoepub3_hack.py -h を参照)

しばらく使ってみて、今のところ特に問題はない。
ただ、こんな解決方法はどう考えても醜い。本によっては、 <span /> タグを入れている事が原因で、 何か変な副作用が起きるかもしれない。
こんなことやらなくても、何か、css 関連の設定ですっきりと解決、 なんてことになればいいのだけど。