12章学習記録 » 履歴 » バージョン 4
Tatsuya ISHIGAKI, 2025/07/25 10:34
| 1 | 4 | Tatsuya ISHIGAKI | # 第12章 データ圧縮、アーカイブと永続化 |
|---|---|---|---|
| 2 | |||
| 3 | ## 12.1 gzip 圧縮ファイルを扱う gzip |
||
| 4 | gzip形式のファイル圧縮・展開 (gzip, gunzip コマンドと同等) |
||
| 5 | |||
| 6 | - gzip ファイルを圧縮、展開する |
||
| 7 | |||
| 8 | |関数名|解説|戻り値| |
||
| 9 | |---|---|---| |
||
| 10 | |`open(filename, mode='rb', compresslevel=9, encoding=None, errors=None, newline=None)`|gzip形式のファイルを開き、ファイルオブジェクトを返す<br>読み込みモードなら既存のファイルを開き、**書き込みモードならファイル作成する**(*1)|gzip.GzipFile| |
||
| 11 | |`compress(data, compresslevel=9, *, mtime=None)`|指定されたデータを gzip 圧縮する<br>データは bytes 型である必要がある|bytes| |
||
| 12 | |`decompress(data)`|指定された gzip 圧縮されたデータを展開し bytes オブジェクトを返す|bytes| |
||
| 13 | |||
| 14 | - (*1) gzip 仕様は追記に対応しておらず、open() の `mode` に追記 (`a` 等) を指定しても、新規書き込みとして扱われる |
||
| 15 | |||
| 16 | ```python |
||
| 17 | >>> import gzip |
||
| 18 | >>> with gzip.open("sample.gz", "wt") as f: |
||
| 19 | ... f.write("適当な文字列。" * 10) |
||
| 20 | ... |
||
| 21 | 70 |
||
| 22 | ``` |
||
| 23 | |||
| 24 | ``` |
||
| 25 | 適当な文字列。適当な文字列。適当な文字列。適当な文字列。適当な文字列。適当な文字列。適当な文字列。適当な文字列。適当な文字列。適当な文字列。 |
||
| 26 | ``` |
||
| 27 | |||
| 28 | ```python |
||
| 29 | >>> with gzip.open("sample.gz", "wt") as f: |
||
| 30 | ... f.write("\n適当な文字列。" * 10) |
||
| 31 | ... |
||
| 32 | 80 |
||
| 33 | ``` |
||
| 34 | |||
| 35 | ``` |
||
| 36 | |||
| 37 | 適当な文字列。 |
||
| 38 | 適当な文字列。 |
||
| 39 | 適当な文字列。 |
||
| 40 | 適当な文字列。 |
||
| 41 | 適当な文字列。 |
||
| 42 | 適当な文字列。 |
||
| 43 | 適当な文字列。 |
||
| 44 | 適当な文字列。 |
||
| 45 | 適当な文字列。 |
||
| 46 | 適当な文字列。 |
||
| 47 | ``` |
||
| 48 | |||
| 49 | - 圧縮、解凍 |
||
| 50 | |||
| 51 | ```python |
||
| 52 | >>> text = "適当な文字列。" * 10 |
||
| 53 | >>> text |
||
| 54 | '適当な文字列。適当な文字列。適当な文字列。適当な文字列。適当な文字列。適当な文字列。適当な文字列。適当な文字列。適当な 文字列。適当な文字列。' |
||
| 55 | >>> len(text) |
||
| 56 | 70 |
||
| 57 | >>> btext = text.encode("utf-8") # bytes 型へ |
||
| 58 | >>> len(btext) |
||
| 59 | 210 |
||
| 60 | >>> comp = gzip.compress(btext) # 圧縮 |
||
| 61 | >>> len(comp) |
||
| 62 | 46 |
||
| 63 | >>> decomp = gzip.decompress(comp) # 解凍 |
||
| 64 | >>> len(decomp) |
||
| 65 | 210 |
||
| 66 | >>> btext == decomp # bytes 型で同値 |
||
| 67 | True |
||
| 68 | >>> decomptext = decomp.decode("utf-8") |
||
| 69 | >>> len(decomptext) |
||
| 70 | 70 |
||
| 71 | >>> decomptext |
||
| 72 | '適当な文字列。適当な文字列。適当な文字列。適当な文字列。適当な文字列。適当な文字列。適当な文字列。適当な文字列。適当な 文字列。適当な文字列。' |
||
| 73 | >>> decomptext == text # str 型で同値 |
||
| 74 | True |
||
| 75 | ``` |
||
| 76 | |||
| 77 | ## 12.2 ZIP ファイルを扱う zipfile |
||
| 78 | ZIP形式でアーカイブされたファイルを扱う |
||
| 79 | |||
| 80 | - ZIP ファイルを操作する |
||
| 81 | - class `ZipFile(file, mode='r' compression=ZIP_STORED, allowZip64=True, compresslevel=None, *, strict_timstamps=True)` |
||
| 82 | - `file` 対象となる ZIP アーカイブ (ファイル名や path-like オブジェクト) |
||
| 83 | - `mode` |
||
| 84 | - `r` 読み込み、`w` 新規作成(既存を削除)、`a` 追記、`x` 新規作成(既存はエラー) |
||
| 85 | - `compression` ZIP 圧縮方法 |
||
| 86 | - `ZIP_DEFLATED` zlib モジュール必要 |
||
| 87 | - `ZIP_BZIP2` bz2 モジュール必要 |
||
| 88 | - `ZIP_LZMA` lzma モジュール必要 |
||
| 89 | - `allowZip64` True の場合、4GiB 以上の ZIP ファイル作成に ZIP64 形式にする |
||
| 90 | - `compresslevel` 圧縮レベル |
||
| 91 | - ZIP_DEFLATED : 0-9 |
||
| 92 | - ZIP_BZIP2 : 1-9 |
||
| 93 | - `strict_timestamps` False の場合、1980 年より前のタイムスタンプを 1980-01-01 に、2108年以降のタイムスタンプを 2107-12-31 に設定する |
||
| 94 | - **戻り値** zipfile.ZipFile |
||
| 95 | - 関数 `is_zipfile(filename)` |
||
| 96 | - `filename` ファイル名、path-like オブジェクト |
||
| 97 | - **戻り値** bool |
||
| 98 | |||
| 99 | - `ZipFile` オブジェクトのメソッド |
||
| 100 | |||
| 101 | |メソッド名|解説|戻り値| |
||
| 102 | |---|---|---| |
||
| 103 | |`infolist()`|`ZipInfo` のリストを返す|list| |
||
| 104 | |`namelist()`|アーカイブされているファイル名のリストを返す|list| |
||
| 105 | |`getinfo(name)`|指定されたファイルの `ZipInfo` を返す|ZipInfo| |
||
| 106 | |`open(name, mode='r', pwd=None, *, force_zip64=None)`|指定したファイルを開く (コンテキストマネージャー)|ZipExtFile| |
||
| 107 | |`extract(member, path=None, pwd=None)`|指定 (ファイル名、ZipInfo) されたファイルを展開し、パスを返す|str| |
||
| 108 | |`extractall(path=None, members=None, pwd=None)`|全ファイルを展開する|None| |
||
| 109 | |`read(name, pwd=None)`|指定されたファイルの中身を返す|bytes| |
||
| 110 | |`write(filename, arcname=None, compress_type=None)`|指定したファイルを ZIP ファイルへ書き込む<br>`arcname` を指定の場合、その名前でアーカイブされる|None| |
||
| 111 | |`writestr(zinfo_or_arcname, data, compress_type=None, compresslevel=None)`|指定したファイル名に対してデータ (str または bytes) を書き込む|None| |
||
| 112 | |`close()`|`ZipFile` を閉じる|None| |
||
| 113 | |||
| 114 | - `write()` `writestr()` の引数 `compress_type` の値は、`ZipFile` のパラメータ `compression` と同じ選択肢で、メソッドでの指定が優先される |
||
| 115 | |||
| 116 | - `ZipInfo` クラスは、ZIP ファイル内の1ファイル (ディレクトリ含む) の情報 |
||
| 117 | |||
| 118 | |属性名、メソッド名|解説|戻り値| |
||
| 119 | |---|---|---| |
||
| 120 | |`filename`|ファイル名|str| |
||
| 121 | |`date_time`|最終更新日時 (6要素タプル)|tuple| |
||
| 122 | |`compress_size`|圧縮後サイズ|int| |
||
| 123 | |`file_size`|圧縮前サイズ|int| |
||
| 124 | |`is_dir()`|ディレクトリの場合、True|bool| |
||
| 125 | |||
| 126 | - `ZipExtFile` クラスは、読み取り専用の file-like オブジェクト |
||
| 127 | |||
| 128 | |メソッド名|解説|戻り値| |
||
| 129 | |---|---|---| |
||
| 130 | |`read(size=-1, /)`|指定バイト数まで読み出す|bytes| |
||
| 131 | |`readline(size=-1, /)`|指定バイト数、行末までの小さい方まで読み出す|bytes| |
||
| 132 | |`readlines(hint=-1, /)`|指定行数まで読み出し、行のリストを返す<br>※通常、行処理には `for line in file:` を使用する|list| |
||
| 133 | |`seek(offset, whence=os.SEEK_SET, /)`|(略)|int| |
||
| 134 | |`tell()`|(略)|int| |
||
| 135 | |||
| 136 | ```python |
||
| 137 | >>> import zipfile |
||
| 138 | >>> zipfile.is_zipfile('python-3.9.4-docs-text.zip') # ZIPファイルかチェック |
||
| 139 | True |
||
| 140 | >>> zip = zipfile.ZipFile('python-3.9.4-docs-text.zip') # ZIPファイルを開く |
||
| 141 | >>> len(zip.namelist()) # ファイル数を確認 |
||
| 142 | 505 |
||
| 143 | >>> zip.namelist()[:2] # 先頭2件のファイル名を取得 |
||
| 144 | ['python-3.9.4-docs-text/', 'python-3.9.4-docs-text/contents.txt'] |
||
| 145 | >>> with zip.open('python-3.9.4-docs-text/contents.txt') as f: # ファイルを開く |
||
| 146 | ... contents = f.read() |
||
| 147 | ... |
||
| 148 | >>> contents[:40] # 先頭の40文字を確認 |
||
| 149 | b'Python Documentation contents\n**********' |
||
| 150 | ``` |
||
| 151 | |||
| 152 | ```python |
||
| 153 | >>> for name in zip.namelist(): # zipfileモジュールのドキュメントを探す |
||
| 154 | ... if 'zipfile' in name: |
||
| 155 | ... zipfile_doc = name |
||
| 156 | ... break |
||
| 157 | ... |
||
| 158 | >>> zipfile_doc # ファイル名を確認 |
||
| 159 | 'python-3.9.4-docs-text/library/zipfile.txt' |
||
| 160 | >>> zipfile_info = zip.getinfo(zipfile_doc) # ZipInfoを取得 |
||
| 161 | >>> zipfile_info.date_time # 最終更新日を確認 |
||
| 162 | (2021, 4, 10, 12, 7, 54) |
||
| 163 | >>> zip.extract(zipfile_info) # zipfileのマニュアルを展開する |
||
| 164 | '/Users/takanori/python-3.9.4-docs-text/library/zipfile.txt' |
||
| 165 | ``` |
||
| 166 | |||
| 167 | ```python |
||
| 168 | >>> with zipfile.ZipFile('example.zip', mode='w', zipfile.ZIP_DEFLATED) as wzip: |
||
| 169 | ... wzip.write('spam.txt') # ファイルを追加する |
||
| 170 | ... wzip.write('ham.txt', 'hamham.txt') # ファイルを別名で追加する |
||
| 171 | ... wzip.writestr('eggs.txt', 'たまご') # ファイル名を指定しテキストを直接書き込む |
||
| 172 | ... wzip.namelist() # ファイルを確認する |
||
| 173 | ... |
||
| 174 | ['spam.txt', 'hamham.txt', 'eggs.txt'] |
||
| 175 | >>> zipfile.is_zipfile('example.zip') # 正しいZIPファイルかを確認 |
||
| 176 | True |
||
| 177 | ``` |
||
| 178 | |||
| 179 | 1 | Tatsuya ISHIGAKI | ## 12.3 tar ファイルを扱う tarfile |
| 180 | gzip, bz2, lzma 形式で圧縮されたものを含む、tar 形式アーカイブを読み書きする |
||
| 181 | |||
| 182 | - tar ファイルを操作する |
||
| 183 | - 関数 `open(name=None, mode='r', fileobj=None, bufsize=20240, **kwargs)` |
||
| 184 | - `name` tar ファイルのファイル名 |
||
| 185 | - `mode` モード指定 `r` `w` 等 |
||
| 186 | - 圧縮形式を指定する場合は `r:gz` `w:xz` 等 (非圧縮指定は `w:` 等) |
||
| 187 | - 読み込み時は `r` だけでも、ファイル名拡張子から圧縮形式が自動判定される |
||
| 188 | - 圧縮ファイルは追記モードが使用できない (`a` `a:` のみ) |
||
| 189 | - `fileobj` tar ファイルのファイルオブジェクト |
||
| 190 | - `bufsize` ブロックサイズ (デフォルトでよい) |
||
| 191 | - **戻り値** `tarfile.TarFile` |
||
| 192 | - 関数 `is_tarfile(name)` |
||
| 193 | - `name` ファイル名、または file-like オブジェクト |
||
| 194 | - **戻り値** (bool) 指定ファイルが tar 形式アーカイブなら True |
||
| 195 | - `TarFile` オブジェクトのメソッド |
||
| 196 | |||
| 197 | |メソッド名|解説|戻り値| |
||
| 198 | |---|---|---| |
||
| 199 | |`getnames()`|tar ファイル内にアーカイブされているファイル名リスト|list| |
||
| 200 | |`getmember(name)`|指定ファイル名の `TarInfo` を返す|TarInfo| |
||
| 201 | |`getmembers()`|tar ファイル内にアーカイブされている全ファイルの `TarFile` のリストを返す|list| |
||
| 202 | |`extract(member, path="", set_attrs=True, *, numeric_owner=False)`|指定ファイルを指定場所へ展開<br>`member`:ファイル名または`TarInfo`|None| |
||
| 203 | |`extractall(path="", set_attrs=True, *, numeric_owner=False)`|全ファイルを指定場所へ展開|None| |
||
| 204 | |`extractfile(member)`|指定ファイルを開き、ファイルオブジェクトを返す<br>`member`:ファイル名または`TarInfo`|ファイルオブジェクト| |
||
| 205 | |`add(name, arcname=None, recursive=True, exclude=None, *, filter=None)`|指定ファイルを tar ファイルのアーカイブへ追加する<br>`arcname`を指定するとそのファイル名で追加<br>ディレクトリ指定では再帰追加可能|?| |
||
| 206 | |`close()`||None| |
||
| 207 | |||
| 208 | ```python |
||
| 209 | >>> import tarfile |
||
| 210 | >>> with tarfile.open("./testarchive.tar", "w:gz") as f: |
||
| 211 | ... f.add("./inArchive1.txt") |
||
| 212 | ... |
||
| 213 | >>> with tarfile.open("./testarchive.tar") as f: |
||
| 214 | ... f.getnames() |
||
| 215 | ... f.getmembers() |
||
| 216 | ... |
||
| 217 | ['./inArchive1.txt'] |
||
| 218 | [<TarInfo './inArchive1.txt' at 0x140b4104640>] |
||
| 219 | >>> with tarfile.open("./testarchive.tar") as f: |
||
| 220 | ... with f.extractfile("./inArchive1.txt") as elem: |
||
| 221 | ... elem.read().decode("sjis") |
||
| 222 | ... |
||
| 223 | 'アーカイブ内ファイル1' |
||
| 224 | >>> with tarfile.open("./testarchive.tar") as f: |
||
| 225 | ... info = f.getmember("./inArchive1.txt") |
||
| 226 | ... info |
||
| 227 | ... print(f"{info.name=}, {info.size=}, {info.mtime=}, {info.mode=:o}") |
||
| 228 | ... |
||
| 229 | <TarInfo './inArchive1.txt' at 0x140b4104940> |
||
| 230 | info.name='./inArchive1.txt', info.size=22, info.mtime=1753410842.0, info.mode=666 |
||
| 231 | >>> with tarfile.open("./testarchive.tar") as f: |
||
| 232 | ``` |
||
| 233 | 2 | Tatsuya ISHIGAKI | |
| 234 | ## 12.4 Python オブジェクトをシリアライズする pickle |
||
| 235 | Python オブジェクトをファイル等に保存可能なバイト列に変換 (**pickle 化** という)、復元 (**非 pickle 化** という) する |
||
| 236 | |||
| 237 | - JSON へのシリアライズ方法である json モジュールとの比較 |
||
| 238 | - JSON は文字列だが、pickle はバイナリ |
||
| 239 | - JSON は他のプログラミング言語でも利用可能だが、pickle は Python 固有フォーマット |
||
| 240 | - JSON は文字列、数値など一部のデータ型のみ扱うが、pickle は Python の様々なオブジェクトを扱える |
||
| 241 | |||
| 242 | - Python オブジェクトのシリアライズとデシリアライズ |
||
| 243 | |||
| 244 | |関数名|解説|戻り値| |
||
| 245 | |---|---|---| |
||
| 246 | |`dump(obj, file, protocol=None, *, fix_imports=True, buffer_callback=None)`|オブジェクトをシリアライズしてファイルへ保存する|None| |
||
| 247 | |`dumps(obj, protocol=None, *, fix_import=True, buffer_callback=None)`|オブジェクトをシリアライズしてバイト列を返す|bytes| |
||
| 248 | |`load(file, *, fix_imports=True, encoding="ASCII", errors="strict", buffers=None)`|ファイルの中身をデシリアライズし、結果オブジェクトを返す|object| |
||
| 249 | |`loads(data, /, *, fix_imports=True, encoding="ASCII", errors="strict", buffers=None)`|バイト列をデシリアライズし、結果オブジェクトを返す|object| |
||
| 250 | |||
| 251 | dumps, loads (bytes でのやり取り) の例 |
||
| 252 | ```python |
||
| 253 | >>> import pickle |
||
| 254 | >>> from datetime import datetime |
||
| 255 | >>> now = datetime.now() |
||
| 256 | >>> data = {"NOW-date": now} # 値が datetime の辞書データ作成 |
||
| 257 | >>> type(data) |
||
| 258 | <class 'dict'> |
||
| 259 | >>> data |
||
| 260 | {'NOW-date': datetime.datetime(2025, 7, 25, 16, 4, 0, 201811)} |
||
| 261 | >>> serialized = pickle.dumps(data) # シリアライズ |
||
| 262 | >>> serialized |
||
| 263 | b'\x80\x04\x958\x00\x00\x00\x00\x00\x00\x00}\x94\x8c\x08NOW-date\x94\x8c\x08datetime\x94\x8c\x08datetime\x94\x93\x94C\n\x07\xe9\x07\x19\x10\x04\x00\x03\x14S\x94\x85\x94R\x94s.' |
||
| 264 | >>> desirialized = pickle.loads(serialized) # デシリアライズ |
||
| 265 | >>> type(desirialized) |
||
| 266 | <class 'dict'> |
||
| 267 | >>> desirialized # 元のデータを復元できている |
||
| 268 | {'NOW-date': datetime.datetime(2025, 7, 25, 16, 4, 0, 201811)} |
||
| 269 | ``` |
||
| 270 | |||
| 271 | dump, load (ファイルでのやり取り) の例 |
||
| 272 | ```python |
||
| 273 | >>> data # 対象データ |
||
| 274 | {'NOW-date': datetime.datetime(2025, 7, 25, 16, 4, 0, 201811)} |
||
| 275 | >>> with open("nowdate.pkl", "wb") as f: |
||
| 276 | ... pickle.dump(data, f) # シリアライズしてファイルへ書き込み |
||
| 277 | ... |
||
| 278 | >>> with open("nowdate.pkl", "rb") as f: |
||
| 279 | ... obj = pickle.load(f) # ファイル内容をデシリアライズ |
||
| 280 | ... |
||
| 281 | >>> obj # 復元を確認 |
||
| 282 | {'NOW-date': datetime.datetime(2025, 7, 25, 16, 4, 0, 201811)} |
||
| 283 | ``` |
||
| 284 | |||
| 285 | `dump()` で生成されたファイルの様子 |
||
| 286 | 3 | Tatsuya ISHIGAKI |  |
| 287 | 2 | Tatsuya ISHIGAKI | |
| 288 | - ※ pickle 化にはプロトコルのバージョンが 0 から 5 まであり、新しいバージョンで pickle 化されたオブジェクトは古いバージョンで非 pickle 化できない (`pickle.DEFAULT_PROTOCOL` でデフォルト確認可能) |