よちよちpython

独習 python/Qpython/Pydroid3/termux/Linux

フォルダ内の全ファイルをipynbの各セルに一気に書き込むプログラムを作る

こんまりの第三子妊娠を記念致しまして🤭、今回はフォルダ内のファイルを一気に片付けるプログラムを作ります。

はじめに


Python初心者のフォルダの中には「sample.py」「test.py」といった名前のファイルがたくさん散らかっていると思います。
ファイルの中身を見ると、リストの使い方だとかiffor構文の練習だとか、ファイルとして取っておくほど大して重要ではないものがほとんどですが、たまに重要なものが含まれている。

捨てようにも捨てられず…全チェックするのも面倒だし…

方法・仕様


片付けるには、全ファイルのテキスト内容を別ファイルにそのまま書き込んでもいいんですが、どうせなら「.pyファイル」は直ぐ実行確認できる形が便利かな。
ということで、JupyterNotebook形式の「.ipynbファイル」に一気に書き込むPythonプログラムにします。

JupyterNotebookをインストールすると、自動的にnbformatというものもインストールされています。これを使います。pip listコマンドで確認できます。
nbformatを使うと、.ipynbファイルを開かずに書き込みができる。

ipynbファイルの中身はjson形式の文字列です。メモ帳などで開くと{}で囲まれた辞書形式になっているのがわかる。そこにセルの情報などが書かれています。
nbformatを使うとセルの値を「markdown」か「code」かのセルのタイプごとに分けて書き込めます。



実装


説明は後回し抜きにしてプログラムを書きます。

files2Ipynb.py

import nbformat
import os
from glob import glob

# メインの関数
def files2Ipynb(files):
    """
    引数 : list
    ----------
    ファイル・フォルダ名のリスト

    戻り値 : nbformat.notebooknode.NotebookNode
    ----------
    辞書型

    ファイル拡張子ごとにセルのタイプを判別し、ファイル内容を各セルに格納した辞書型を生成する。
    """
    nb = nbformat.v4.new_notebook()
    nb_cells = []

    # ファイル・フォルダリストを各処理
    for fname in files:
        # フォルダなら無視
        if os.path.isdir(fname):
            pass

        # フォルダ以外なら処理する
        else:
            name, ext = os.path.splitext(fname)  # 名前と拡張子に分ける
            reader = readFile(fname)  # ファイル内容を読む

            # このファイル自身は無視する
            if fname == os.path.basename(__file__):
                pass

            # pyファイルなら
            elif ext == ".py":
                # codeセルに書き込み
                nb_cells.append(nbformat.v4.new_code_cell(reader))

            # pyファイル以外なら
            else:
                # markdownセルに書き込み
                nb_cells.append(nbformat.v4.new_markdown_cell(reader))

    nb['cells'] = nb_cells
    return nb


# 新規作成のipynbに書き込む関数
def write2Ipynb(nb):
    with open("ALL.ipynb", 'w') as f:
        nbformat.write(nb, f)


# ファイル内容を読む関数
def readFile(fname):
    with open(fname, "r") as f:
        reader = f.read()
    return reader


# 実行
files = glob("*")
nb = files2Ipynb(files)
write2Ipynb(nb)



実行


上記のプログラム「files2Ipynb.py」を実行すると、カレントディレクトリ内の全ファイルを、新規作成した「ALL.ipynb」に書き込む。実行毎に上書き保存。注意 :追記ではありません。

適当なディレクトリに適当にファイルとフォルダを作って実験します。

$ tree
.
├── Nothing  ←これだけ空フォルダ
├── lesson5.ipynb
├── sample1.py
├── test1.py
└── 般若心経.txt

1 directory, 4 files

4つのファイルと1つのフォルダを用意しました。



実行します。
上手く行けば、4つのファイル内容が新規作成されるALL.ipynbに書き込まれます。
※実行するカレントディレクトリは上のファイルが入ったフォルダに移動しています。実行ファイルの場所とは異なるディレクトリです。

$ python /なんとか/files2Ipynb.py

実行すると、↓

$ tree

.
├── ALL.ipynb  ←新規作成された
├── Nothing
├── lesson5.ipynb
├── sample1.py
├── test1.py
└── 般若心経.txt

1 directory, 5 files

ipynbファイルが新規作成された。



Jupyter Notebookを起動して「ALL.ipynb」を見てみます。

f:id:chayarokurokuro:20210129151808j:plain


上手く行きました。

  • 「pyファイル」 → codeセル
  • 「それ以外」 → markdownセル

に書き込まれています。
実験おわり。



参考リンク


PythonでJupyterノートブック「.ipynb」を生成する - Qiita
こちらの先頭に載っているコードを参考にしました。



おまけ %%writefile%load


%%writefileでセルをファイルに書き出し

JupyterNotebookのcodeセルの1行目にマジックコマンドで「%%writefile ファイル名」を書くと、そこのセル内容が「ファイル名.py」でカレントディレクトリに新規作成されます。



%loadでファイル内容をセルのIn[番号]部分に出力

逆に、1行目に「%load ファイル名」と書くと、指定したファイルの内容がそのセルのIn[番号]部分に実行できる状態で書き込まれ、1行目は「# %load ファイル名」と置き換わる。
ファイルからコピペする手間がいらず、無茶苦茶便利です。

一方、マジックコマンドの「%cat ファイル名」では出力部分にファイル内容が表示されるだけで、実行できる状態にならない。


【追記】
画像や動画ファイル、csvやxlsxファイル、zip等を読ませるとエラーになった。
条件分岐部分をいじれば結構できる子。

今回は以上です。