人間が判読できるバージョンのファイルサイズを取得するための再利用可能なライブラリ?
-
11-09-2019 - |
質問
Web 上には、バイト サイズから人間が判読できるサイズを返す関数を提供するさまざまなスニペットがあります。
>>> human_readable(2048)
'2 kilobytes'
>>>
しかし、これを提供する Python ライブラリはあるのでしょうか?
解決
上記の「タスクが小さすぎてライブラリを必要としない」問題は、次のような簡単な実装によって解決されます。
def sizeof_fmt(num, suffix='B'):
for unit in ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']:
if abs(num) < 1024.0:
return "%3.1f%s%s" % (num, unit, suffix)
num /= 1024.0
return "%.1f%s%s" % (num, 'Yi', suffix)
サポート:
- 現在知られているすべて バイナリ接頭辞
- 負の数と正の数
- 1000ヨビバイトを超える数値
- 任意の単位 (ギビビットで数えたいかもしれません!)
例:
>>> sizeof_fmt(168963795964)
'157.4GiB'
による フレッド・シレラ
他のヒント
あなたが探していると思われるすべての機能を持つライブラリは humanize
のです。 humanize.naturalsize()
は、あなたが探しているすべてを行うようです。
ここに私のバージョンです。これは、forループを使用していません。それは、Oを一定の複雑さを持っている(の1 )、および理論的にはforループを使用し、ここでの回答よりも効率的です。
from math import log
unit_list = zip(['bytes', 'kB', 'MB', 'GB', 'TB', 'PB'], [0, 0, 1, 2, 2, 2])
def sizeof_fmt(num):
"""Human friendly file size"""
if num > 1:
exponent = min(int(log(num, 1024)), len(unit_list) - 1)
quotient = float(num) / 1024**exponent
unit, num_decimals = unit_list[exponent]
format_string = '{:.%sf} {}' % (num_decimals)
return format_string.format(quotient, unit)
if num == 0:
return '0 bytes'
if num == 1:
return '1 byte'
で何が起こっているのか、それをより明確にするために、我々は文字列フォーマットのためのコードを省略することができます。ここでは、実際に仕事をする行は次のとおりです:
exponent = int(log(num, 1024))
quotient = num / 1024**exponent
unit_list[exponent]
、私は最近、接尾辞リストにシフトし、指数を兼ねサイズの順序を決定するためにlog2
を使用して、ループを回避したバージョンを思い付います:
from math import log2
_suffixes = ['bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
def file_size(size):
# determine binary order in steps of size 10
# (coerce to int, // still returns a float)
order = int(log2(size) / 10) if size else 0
# format file size
# (.4g results in rounded numbers for exact matches and max 3 decimals,
# should never resort to exponent values)
return '{:.4g} {}'.format(size / (1 << (order * 10)), _suffixes[order])
もかかわらず、その読みやすさのためにunpythonic考えられる:)
は常にそれらの人の1があるように持っています。さて、今日はそれが私です。あなたが関数のシグネチャを数える場合は二行
- ここでワンライナーソリューションです。def human_size(bytes, units=[' bytes','KB','MB','GB','TB', 'PB', 'EB']):
""" Returns a human readable string reprentation of bytes"""
return str(bytes) + units[0] if bytes < 1024 else human_size(bytes>>10, units[1:])
>>> human_size(123)
123 bytes
>>> human_size(123456789)
117GB
あなたはまた、 filesizeformat <を試すことができます/>
from django.template.defaultfilters import filesizeformat
filesizeformat(1073741824)
=>
"1.0 GB"
はPython 3.6以降では、次の作品を、私の意見では、ここに答えを理解するのが最も簡単です、あなたが使用し小数点以下の量をカスタマイズできます。
def human_readable_size(size, decimal_places=3):
for unit in ['B','KiB','MiB','GiB','TiB']:
if size < 1024.0:
break
size /= 1024.0
return f"{size:.{decimal_places}f}{unit}"
そのようなライブラリがある hurry.filesizeするます。
>>> from hurry.filesize import alternative
>>> size(1, system=alternative)
'1 byte'
>>> size(10, system=alternative)
'10 bytes'
>>> size(1024, system=alternative)
'1 KB'
1000の力や kibibytes のいずれかを使用することにより、より標準的な優しいだろう
def sizeof_fmt(num, use_kibibyte=True):
base, suffix = [(1000.,'B'),(1024.,'iB')][use_kibibyte]
for x in ['B'] + map(lambda x: x+suffix, list('kMGTP')):
if -base < num < base:
return "%3.1f %s" % (num, x)
num /= base
return "%3.1f %s" % (num, x)
P.S。 K(大文字)接尾辞で何千もを印刷し、ライブラリを信用しない:)
(hurry.filesizeの代替として提供スニペット)にリフ、ここで使用されるプレフィックスに基づいて変化する精度の数値を与えるスニペットです。これは、いくつかのスニペットのように簡潔ではありませんが、私は結果が好きです。
def human_size(size_bytes):
"""
format a size in bytes into a 'human' file size, e.g. bytes, KB, MB, GB, TB, PB
Note that bytes/KB will be reported in whole numbers but MB and above will have greater precision
e.g. 1 byte, 43 bytes, 443 KB, 4.3 MB, 4.43 GB, etc
"""
if size_bytes == 1:
# because I really hate unnecessary plurals
return "1 byte"
suffixes_table = [('bytes',0),('KB',0),('MB',1),('GB',2),('TB',2), ('PB',2)]
num = float(size_bytes)
for suffix, precision in suffixes_table:
if num < 1024.0:
break
num /= 1024.0
if precision == 0:
formatted_size = "%d" % num
else:
formatted_size = str(round(num, ndigits=precision))
return "%s %s" % (formatted_size, suffix)
これは、ほぼすべての状況で必要なことを実行し、オプションの引数でカスタマイズ可能です。そして、ご覧のとおり、 かわいい 多くの自己文書化:
from math import log
def pretty_size(n,pow=0,b=1024,u='B',pre=['']+[p+'i'for p in'KMGTPEZY']):
pow,n=min(int(log(max(n*b**pow,1),b)),len(pre)-1),n*b**pow
return "%%.%if %%s%%s"%abs(pow%(-pow-1))%(n/b**float(pow),pre[pow],u)
出力例:
>>> pretty_size(42)
'42 B'
>>> pretty_size(2015)
'2.0 KiB'
>>> pretty_size(987654321)
'941.9 MiB'
>>> pretty_size(9876543210)
'9.2 GiB'
>>> pretty_size(0.5,pow=1)
'512 B'
>>> pretty_size(0)
'0 B'
高度なカスタマイズ:
>>> pretty_size(987654321,b=1000,u='bytes',pre=['','kilo','mega','giga'])
'987.7 megabytes'
>>> pretty_size(9876543210,b=1000,u='bytes',pre=['','kilo','mega','giga'])
'9.9 gigabytes'
このコードは Python 2 と Python 3 の両方と互換性があります。PEP8 への準拠は、読者のための演習です。覚えておいてください、それは 出力 それはきれいです。
アップデート:
数千のカンマが必要な場合は、明らかな拡張子を適用するだけです。
def prettier_size(n,pow=0,b=1024,u='B',pre=['']+[p+'i'for p in'KMGTPEZY']):
r,f=min(int(log(max(n*b**pow,1),b)),len(pre)-1),'{:,.%if} %s%s'
return (f%(abs(r%(-r-1)),pre[r],u)).format(n*b**pow/b**float(r))
例えば:
>>> pretty_units(987654321098765432109876543210)
'816,968.5 YiB'
以前のすべての答えから描画、ここではそれが私の感想です。これは、整数としてバイト単位でファイルサイズを格納するオブジェクトです。あなたがオブジェクトを印刷しようとすると、しかし、あなたは自動的に人間が読めるバージョンを取得します。
class Filesize(object):
"""
Container for a size in bytes with a human readable representation
Use it like this::
>>> size = Filesize(123123123)
>>> print size
'117.4 MB'
"""
chunk = 1024
units = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB']
precisions = [0, 0, 1, 2, 2, 2]
def __init__(self, size):
self.size = size
def __int__(self):
return self.size
def __str__(self):
if self.size == 0: return '0 bytes'
from math import log
unit = self.units[min(int(log(self.size, self.chunk)), len(self.units) - 1)]
return self.format(unit)
def format(self, unit):
if unit not in self.units: raise Exception("Not a valid file size unit: %s" % unit)
if self.size == 1 and unit == 'bytes': return '1 byte'
exponent = self.units.index(unit)
quotient = float(self.size) / self.chunk**exponent
precision = self.precisions[exponent]
format_string = '{:.%sf} {}' % (precision)
return format_string.format(quotient, unit)
私はので、ここでは(上記jocteeの答えを持つことのハイブリッドのようなものだ、のsenderleの小数バージョンでの固定精度が好きあなたは非整数の塩基でログを取ることができる知っていました):
from math import log
def human_readable_bytes(x):
# hybrid of https://stackoverflow.com/a/10171475/2595465
# with https://stackoverflow.com/a/5414105/2595465
if x == 0: return '0'
magnitude = int(log(abs(x),10.24))
if magnitude > 16:
format_str = '%iP'
denominator_mag = 15
else:
float_fmt = '%2.1f' if magnitude % 3 == 1 else '%1.2f'
illion = (magnitude + 1) // 3
format_str = float_fmt + ['', 'K', 'M', 'G', 'T', 'P'][illion]
return (format_str % (x * 1.0 / (1024 ** illion))).lstrip('0')
あなたは "ヒト化" 使用する必要があります。
>>> humanize.naturalsize(1000000)
'1.0 MB'
>>> humanize.naturalsize(1000000, binary=True)
'976.6 KiB'
>>> humanize.naturalsize(1000000, gnu=True)
'976.6K'
参考ます:
HumanFriendlyプロジェクトは、このにしてを助けます。
import humanfriendly
humanfriendly.format_size(1024)
DiveIntoPython3も交渉はこの機能についてを。
現代Djangoは自己テンプレートタグfilesizeformat
があります:
human-readable
ファイルサイズ(すなわち、'13 KB」、 '4.1メガバイト'、 '102バイト'、等)のような値をフォーマットします。
例
{{ value|filesizeformat }}
の値が123456789である場合、出力は117.7メガバイトであろう。
詳細情報: https://docs.djangoproject.com /en/1.10/ref/templates/builtins/#filesizeformatする
シンプルな 2 ライナーはどうでしょうか。
def humanizeFileSize(filesize):
p = int(math.floor(math.log(filesize, 2)/10))
return "%.3f%s" % (filesize/math.pow(1024,p), ['B','KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'][p])
内部でどのように動作するかは次のとおりです。
- ログを計算します2(ファイルサイズ)
- 10 で割ると、最も近い単位が得られます。(たとえば、サイズが 5000 バイトの場合、最も近い単位は
Kb
, 、したがって、答えは X KiB になるはずです) - 戻り値
file_size/value_of_closest_unit
ユニットと一緒に。
ただし、ファイルサイズが 0 または負の場合は機能しません (0 と -ve の数値に対してログが定義されていないため)。追加のチェックを追加できます。
def humanizeFileSize(filesize):
filesize = abs(filesize)
if (filesize==0):
return "0 Bytes"
p = int(math.floor(math.log(filesize, 2)/10))
return "%0.2f %s" % (filesize/math.pow(1024,p), ['Bytes','KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'][p])
例:
>>> humanizeFileSize(538244835492574234)
'478.06 PiB'
>>> humanizeFileSize(-924372537)
'881.55 MiB'
>>> humanizeFileSize(0)
'0 Bytes'
注記 - KbとKiBには違いがあります。KB は 1000 バイトを意味し、KiB は 1024 バイトを意味します。KB、MB、GB はすべて 1000 の倍数ですが、KiB、MiB、GiB などはすべて 1024 の倍数です。 詳細についてはこちら
def human_readable_data_quantity(quantity, multiple=1024):
if quantity == 0:
quantity = +0
SUFFIXES = ["B"] + [i + {1000: "B", 1024: "iB"}[multiple] for i in "KMGTPEZY"]
for suffix in SUFFIXES:
if quantity < multiple or suffix == SUFFIXES[-1]:
if suffix == SUFFIXES[0]:
return "%d%s" % (quantity, suffix)
else:
return "%.1f%s" % (quantity, suffix)
else:
quantity /= multiple
これから説明するものは、すでに投稿されているソリューションの中で最もパフォーマンスが高く、最短のソリューションというわけではありません。代わりに、次の点に焦点を当てます。 特定の問題が 1 つあります 他の多くの答えは見逃しています。
つまり、次のように入力した場合 999_995
与えられる:
Python 3.6.1 ...
...
>>> value = 999_995
>>> base = 1000
>>> math.log(value, base)
1.999999276174054
これを最も近い整数に切り捨てて入力に戻すと、次のようになります。
>>> order = int(math.log(value, base))
>>> value/base**order
999.995
これは、制御が必要になるまでは、まさに私たちが期待していたもののようです。 出力精度. 。そして、これが物事が少し難しくなり始めるときです。
精度を 2 桁に設定すると、次のようになります。
>>> round(value/base**order, 2)
1000 # K
の代わりに 1M
.
どうすればそれに対抗できるでしょうか?
もちろん、それを明示的にチェックすることもできます。
if round(value/base**order, 2) == base:
order += 1
しかし、もっと良くできるでしょうか?どちらの方向に進むか知ることができますか? order
最終ステップを行う前にカットする必要がありますか?
それができることが分かりました。
小数点以下四捨五入ルールを 0.5 と仮定すると、上記は if
条件は次のように変換されます。
その結果
def abbreviate(value, base=1000, precision=2, suffixes=None):
if suffixes is None:
suffixes = ['', 'K', 'M', 'B', 'T']
if value == 0:
return f'{0}{suffixes[0]}'
order_max = len(suffixes) - 1
order = log(abs(value), base)
order_corr = order - int(order) >= log(base - 0.5/10**precision, base)
order = min(int(order) + order_corr, order_max)
factored = round(value/base**order, precision)
return f'{factored:,g}{suffixes[order]}'
与える
>>> abbreviate(999_994)
'999.99K'
>>> abbreviate(999_995)
'1M'
>>> abbreviate(999_995, precision=3)
'999.995K'
>>> abbreviate(2042, base=1024)
'1.99K'
>>> abbreviate(2043, base=1024)
'2K'
に更新し、Sridhar Ratnakumar
の答えを参照してください。
def formatSize(sizeInBytes, decimalNum=1, isUnitWithI=False, sizeUnitSeperator=""):
"""format size to human readable string"""
# https://en.wikipedia.org/wiki/Binary_prefix#Specific_units_of_IEC_60027-2_A.2_and_ISO.2FIEC_80000
# K=kilo, M=mega, G=giga, T=tera, P=peta, E=exa, Z=zetta, Y=yotta
sizeUnitList = ['','K','M','G','T','P','E','Z']
largestUnit = 'Y'
if isUnitWithI:
sizeUnitListWithI = []
for curIdx, eachUnit in enumerate(sizeUnitList):
unitWithI = eachUnit
if curIdx >= 1:
unitWithI += 'i'
sizeUnitListWithI.append(unitWithI)
# sizeUnitListWithI = ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']
sizeUnitList = sizeUnitListWithI
largestUnit += 'i'
suffix = "B"
decimalFormat = "." + str(decimalNum) + "f" # ".1f"
finalFormat = "%" + decimalFormat + sizeUnitSeperator + "%s%s" # "%.1f%s%s"
sizeNum = sizeInBytes
for sizeUnit in sizeUnitList:
if abs(sizeNum) < 1024.0:
return finalFormat % (sizeNum, sizeUnit, suffix)
sizeNum /= 1024.0
return finalFormat % (sizeNum, largestUnit, suffix)
と出力例である:
def testKb():
kbSize = 3746
kbStr = formatSize(kbSize)
print("%s -> %s" % (kbSize, kbStr))
def testI():
iSize = 87533
iStr = formatSize(iSize, isUnitWithI=True)
print("%s -> %s" % (iSize, iStr))
def testSeparator():
seperatorSize = 98654
seperatorStr = formatSize(seperatorSize, sizeUnitSeperator=" ")
print("%s -> %s" % (seperatorSize, seperatorStr))
def testBytes():
bytesSize = 352
bytesStr = formatSize(bytesSize)
print("%s -> %s" % (bytesSize, bytesStr))
def testMb():
mbSize = 76383285
mbStr = formatSize(mbSize, decimalNum=2)
print("%s -> %s" % (mbSize, mbStr))
def testTb():
tbSize = 763832854988542
tbStr = formatSize(tbSize, decimalNum=2)
print("%s -> %s" % (tbSize, tbStr))
def testPb():
pbSize = 763832854988542665
pbStr = formatSize(pbSize, decimalNum=4)
print("%s -> %s" % (pbSize, pbStr))
def demoFormatSize():
testKb()
testI()
testSeparator()
testBytes()
testMb()
testTb()
testPb()
# 3746 -> 3.7KB
# 87533 -> 85.5KiB
# 98654 -> 96.3 KB
# 352 -> 352.0B
# 76383285 -> 72.84MB
# 763832854988542 -> 694.70TB
# 763832854988542665 -> 678.4199PB
このソリューションは、あなたの心がどのように動作するかに応じて、あなたにアピールするかもしれません。
from pathlib import Path
def get_size(path = Path('.')):
""" Gets file size, or total directory size """
if path.is_file():
size = path.stat().st_size
elif path.is_dir():
size = sum(file.stat().st_size for file in path.glob('*.*'))
return size
def format_size(path, unit="MB"):
""" Converts integers to common size units used in computing """
bit_shift = {"B": 0,
"kb": 7,
"KB": 10,
"mb": 17,
"MB": 20,
"gb": 27,
"GB": 30,
"TB": 40,}
return "{:,.0f}".format(get_size(path) / float(1 << bit_shift[unit])) + " " + unit
# Tests and test results
>>> get_size("d:\\media\\bags of fun.avi")
'38 MB'
>>> get_size("d:\\media\\bags of fun.avi","KB")
'38,763 KB'
>>> get_size("d:\\media\\bags of fun.avi","kb")
'310,104 kb'