В Інтернеті є різні фрагменти, які дають вам функцію повертати читаний людиною розмір з розміру байтів:
>>> 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()
останній рядок (або для python 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, ви також можете спробувати fileizeformat :
from django.template.defaultfilters import filesizeformat
filesizeformat(1073741824)
=>
"1.0 GB"
Одна з таких бібліотек є 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 або кібібайт було б більш стандартним:
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)
Мені подобається фіксована точність десяткової версії відправника , тож ось такий собі гібрид цього відповіді з відповіддю 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 KiB)file_size/value_of_closest_unit
разом із одиницею.Однак він не працює, якщо розмір файлів дорівнює 0 або негативний (оскільки журнал не визначений для 0 і -ве числа). Ви можете додати додаткові чеки для них:
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
Що ви збираєтеся знайти нижче, аж ніяк не є найефективнішим чи найкоротшим рішенням серед уже опублікованих. Натомість вона зосереджується на одному конкретному питанні яке багато інших відповідей не вистачає.
А саме випадок, коли вводиться подібне введення 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
відповіді «s, додано:
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'