В Интернете существуют различные фрагменты, которые могут дать вам функцию, которая возвращает читаемый размер человека из размера в байтах:
>>> human_readable(2048)
'2 kilobytes'
>>>
Но есть ли библиотека Python, которая обеспечивает это?
В Интернете существуют различные фрагменты, которые могут дать вам функцию, которая возвращает читаемый размер человека из размера в байтах:
>>> 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)
Поддержка:
Пример:
>>> sizeof_fmt(168963795964)
'157.4GiB'
по Фред Cirera
B
(т.е. для блоков, отличных от байтов), вы бы хотели, чтобы коэффициент был, 1000.0
а не 1024.0
нет?
1
строках 4 и 6 на ту точность, которую вы хотите.
Библиотека, которая обладает всеми функциями, которые, как вам кажется, вы ищете humanize
. humanize.naturalsize()
кажется, делает все, что вы ищете.
humanize.naturalsize(2048) # => '2.0 kB'
,humanize.naturalsize(2048, binary=True) # => '2.0 KiB'
humanize.naturalsize(2048, gnu=True) # => '2.0K'
Вот моя версия. Он не использует цикл 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]
1000
будет отображаться как 1,000 bytes
.
unit_list = list(zip(['bytes', 'kB', 'MB', 'GB', 'TB', 'PB'], [0, 0, 1, 2, 2, 2]))
Следующее работает в 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}"
Хотя я знаю, что этот вопрос древний, недавно я придумал версию, которая избегает циклов, используя 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])
Впрочем, вполне может считаться непифоническим из-за его читабельности :)
size
или (1 << (order * 10)
в float()
в последней строке (для питона 2).
import math
там.
Всегда должен быть один из тех парней. Ну, сегодня это я. Вот однострочное решение - или две строки, если вы посчитаете сигнатуру функции.
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
units=None
вместо этого)
Если вы используете Django, вы также можете попробовать форматировать файлы :
from django.template.defaultfilters import filesizeformat
filesizeformat(1073741824)
=>
"1.0 GB"
Одна такая библиотека - спешите .
>>> from hurry.filesize import alternative
>>> size(1, system=alternative)
'1 byte'
>>> size(10, system=alternative)
'10 bytes'
>>> size(1024, system=alternative)
'1 KB'
Использование степеней 1000 или кибибайтов было бы более стандартным:
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)
PS Никогда не доверяйте библиотеке, которая печатает тысячи с суффиксом K (заглавными буквами) :)
P.S. Never trust a library that prints thousands with the K (uppercase) 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'
Вы должны использовать «гуманизировать».
>>> humanize.naturalsize(1000000)
'1.0 MB'
>>> humanize.naturalsize(1000000, binary=True)
'976.6 KiB'
>>> humanize.naturalsize(1000000, gnu=True)
'976.6K'
Ссылка:
Перейдя к фрагменту, предоставленному в качестве альтернативы 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)
Проект HumanFriendly помогает в этом .
import humanfriendly
humanfriendly.format_size(1024)
Приведенный выше код даст 1 КБ в качестве ответа.
Примеры можно найти здесь .
Опираясь на все предыдущие ответы, вот мой взгляд на это. Это объект, который будет хранить размер файла в байтах как целое число. Но когда вы пытаетесь напечатать объект, вы автоматически получаете читаемую человеком версию.
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)
Мне нравится фиксированная точность десятичной версии senderle , так что это своего рода гибрид этого с ответом joctee выше (знаете ли вы, что вы можете взять журналы с нецелыми основаниями?):
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')
DiveIntoPython3 также говорит об этой функции.
У современного Django есть собственный шаблон шаблона filesizeformat
:
Форматирует значение, например human-readable
размер файла (т. Е. «13 КБ», «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])
Вот как это работает под капотом:
Kb
, поэтому ответ должен быть X КиБ)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 означает 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
То, что вы собираетесь найти ниже, ни в коем случае не является самым эффективным или самым коротким решением среди уже опубликованных. Вместо этого он фокусируется на одной конкретной проблеме, которую пропускают многие другие ответы.
А именно случай, когда вводится как 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'