プロジェクト

全般

プロフィール

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
  ![nowdata.jpg](nowdate.jpg "nowdata.pkl")
287 2 Tatsuya ISHIGAKI
288
  - ※ pickle 化にはプロトコルのバージョンが 0 から 5 まであり、新しいバージョンで pickle 化されたオブジェクトは古いバージョンで非 pickle 化できない (`pickle.DEFAULT_PROTOCOL` でデフォルト確認可能)