我有一个Python程序,它需要读取文本文件作为输入。但是,其中一些文件可能是gzip压缩的。
是否有一种可跨平台、可从Python使用的方法来确定文件是否已经gzip压缩?
以下方法可靠吗?或者一般的文本文件会不会“意外地”看起来像gzip文件,导致误报呢?
try:
gzip.GzipFile(filename, 'r')
# compressed
# ...
except:
# not compressed
# ...
我有一个Python程序,它需要读取文本文件作为输入。但是,其中一些文件可能是gzip压缩的。
是否有一种可跨平台、可从Python使用的方法来确定文件是否已经gzip压缩?
以下方法可靠吗?或者一般的文本文件会不会“意外地”看起来像gzip文件,导致误报呢?
try:
gzip.GzipFile(filename, 'r')
# compressed
# ...
except:
# not compressed
# ...
gzip压缩文件的魔数是1f 8b
。虽然测试不是100%可靠,但“普通文本文件”以这两个字节开头的可能性非常小——在UTF-8中甚至不合法。
通常,gzip压缩文件的后缀名为.gz
。即使是gzip(1)
本身也不会解压缩没有该后缀名的文件,除非你使用--force
选项。理论上你可以这样做,但你仍然必须处理可能出现的IOError(无论如何都必须这样做)。
你的方法有一个问题,即gzip.GzipFile()
如果输入的是未压缩的文件,则不会抛出异常。只有稍后的read()
才会抛出异常。这意味着你可能需要两次实现程序逻辑。很丑陋。
有没有一种跨平台的、可用于 Python 的方式来确定文件是否经过 gzip 压缩?
接受的答案 解释了如何通常检测 gzip 压缩的文件: 检测前两个字节是否为 1f 8b
。然而,它并没有展示如何在 Python 中实现。
下面是其中一种方法:
def is_gz_file(filepath):
with open(filepath, 'rb') as test_f:
return test_f.read(2) == b'\x1f\x8b'
test_f.read(2) == b'\x1f\x8b'
。 - nemetroid1f 8b 08
。 - Mark Adler.gz
文件,那么 test_f.read(2)
在第一次调用时会抛出 OSError
异常,还需要使用 test_f.read(2) == b'\x1f\x8b'
检查字节吗?编辑:这似乎仅适用于 Python 3.7 及以上版本。 - Blade测试gzip文件的魔术数字是唯一可靠的方式。但是,从python3.7开始,不再需要自己比较字节了。gzip模块将为您比较字节,并在不匹配时引发异常!
从python3.7开始,这样做就行了
import gzip
with gzip.open(input_file, 'r') as fh:
try:
fh.read(1)
except OSError:
print('input_file is not a valid gzip file by OSError')
从Python 3.8开始,这也是有效的:
import gzip
with gzip.open(input_file, 'r') as fh:
try:
fh.read(1)
except gzip.BadGzipFile:
print('input_file is not a valid gzip file by BadGzipFile')
gzip
自身会在不是gzip文件时引发OSError
。
>>> with gzip.open('README.md', 'rb') as f:
... f.read()
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "/Users/dennis/.asdf/installs/python/3.6.6/lib/python3.6/gzip.py", line 276, in read
return self._buffer.read(size)
File "/Users/dennis/.asdf/installs/python/3.6.6/lib/python3.6/gzip.py", line 463, in read
if not self._read_gzip_header():
File "/Users/dennis/.asdf/installs/python/3.6.6/lib/python3.6/gzip.py", line 411, in _read_gzip_header
raise OSError('Not a gzipped file (%r)' % magic)
OSError: Not a gzipped file (b'# ')
可以结合其他方法来增加信心,例如检查MIME类型或在文件头中查找魔数(请参见其他答案的示例)并检查扩展名。
import pathlib
if '.gz' in pathlib.Path(filepath).suffixes:
# some more inexpensive checks until confident we can attempt to decompress
# ...
try ...
...
except OSError as e:
...
gzip.BadGzipFile
以此来实现。该错误仍然继承自OSError
。 - winni2k在Python3中似乎不能很好地工作...
import mimetypes
filename = "./datasets/test"
def file_type(filename):
type = mimetypes.guess_type(filename)
return type
print(file_type(filename))
返回 (无,无) 但来自 Unix 命令 "File"
:~> file datasets/test datasets/test: gzip 压缩数据,文件名为 "iostat_collection",来自 Unix,最后修改时间:2015 年 1 月 29 日,07:09:34
导入mimetypes模块。 它可以自动猜测你拥有的文件类型,以及是否压缩。
例如:
mimetypes.guess_type('blabla.txt.gz')
返回:
('text/plain','gzip')
mimetypes
仅检查文件名的结尾,实际上不会根据文件内容进行猜测。 - Odinulf