Hay varios fragmentos en la web que le darían una función para devolver el tamaño legible por humanos del tamaño de bytes:
>>> human_readable(2048)
'2 kilobytes'
>>>
Pero, ¿hay una biblioteca de Python que proporcione esto?
Hay varios fragmentos en la web que le darían una función para devolver el tamaño legible por humanos del tamaño de bytes:
>>> human_readable(2048)
'2 kilobytes'
>>>
Pero, ¿hay una biblioteca de Python que proporcione esto?
Respuestas:
Abordar el problema anterior "una tarea demasiado pequeña para requerir una biblioteca" mediante una implementación sencilla:
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)
Apoya:
Ejemplo:
>>> sizeof_fmt(168963795964)
'157.4GiB'
por Fred Cirera
B
(es decir, para unidades que no sean bytes), ¿desearía que el factor sea 1000.0
más que 1024.0
no?
1
líneas 4 y 6 a la precisión que desee.
Una biblioteca que tiene toda la funcionalidad que parece que estás buscando es humanize
. humanize.naturalsize()
parece hacer todo lo que estás buscando.
humanize.naturalsize(2048) # => '2.0 kB'
,humanize.naturalsize(2048, binary=True) # => '2.0 KiB'
humanize.naturalsize(2048, gnu=True) # => '2.0K'
Aquí está mi versión. No usa un bucle for. Tiene una complejidad constante, O ( 1 ), y en teoría es más eficiente que las respuestas aquí que usan un bucle 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'
Para que quede más claro lo que está sucediendo, podemos omitir el código para el formato de cadena. Aquí están las líneas que realmente hacen el trabajo:
exponent = int(log(num, 1024))
quotient = num / 1024**exponent
unit_list[exponent]
1000
lo que se mostrará como 1,000 bytes
.
unit_list = list(zip(['bytes', 'kB', 'MB', 'GB', 'TB', 'PB'], [0, 0, 1, 2, 2, 2]))
Lo siguiente funciona en Python 3.6+, es, en mi opinión, la respuesta más fácil de entender aquí, y le permite personalizar la cantidad de lugares decimales utilizados.
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}"
Si bien sé que esta pregunta es antigua, recientemente se me ocurrió una versión que evita los bucles y la uso log2
para determinar el orden de tamaño que funciona como un cambio y un índice en la lista de sufijos:
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])
Sin embargo, podría considerarse poco propónico por su legibilidad :)
size
o (1 << (order * 10)
en float()
la última línea (para python 2).
import math
allá arriba.
Siempre tiene que haber uno de esos tipos. Pues hoy soy yo. Aquí hay una solución de una línea, o dos líneas si cuenta la firma de la función.
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
lugar)
Si está utilizando Django instalado, también puede probar el formato de archivo :
from django.template.defaultfilters import filesizeformat
filesizeformat(1073741824)
=>
"1.0 GB"
Una de esas bibliotecas es date prisa .
>>> from hurry.filesize import alternative
>>> size(1, system=alternative)
'1 byte'
>>> size(10, system=alternative)
'10 bytes'
>>> size(1024, system=alternative)
'1 KB'
Usar potencias de 1000 o kibibytes sería más amigable con los estándares:
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)
PD: Nunca confíes en una biblioteca que imprima miles con el sufijo K (mayúscula) :)
P.S. Never trust a library that prints thousands with the K (uppercase) suffix :)
Por qué no? El código podría ser perfectamente sólido y el autor simplemente no consideró la carcasa por kilo. Parece bastante tonto descartar automáticamente cualquier código basado en su regla ...
Esto hará lo que necesita en casi cualquier situación, es personalizable con argumentos opcionales y, como puede ver, es bastante más auto-documentado:
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)
Salida de ejemplo:
>>> 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'
Personalizaciones avanzadas:
>>> 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'
Este código es compatible con Python 2 y Python 3. El cumplimiento de PEP8 es un ejercicio para el lector. Recuerda, es el salida lo que es bonito.
Actualizar:
Si necesita miles de comas, simplemente aplique la extensión obvia:
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))
Por ejemplo:
>>> pretty_units(987654321098765432109876543210)
'816,968.5 YiB'
Deberías usar "humanizar".
>>> humanize.naturalsize(1000000)
'1.0 MB'
>>> humanize.naturalsize(1000000, binary=True)
'976.6 KiB'
>>> humanize.naturalsize(1000000, gnu=True)
'976.6K'
Referencia:
Riffing en el fragmento proporcionado como una alternativa a hurry.filesize (), aquí hay un fragmento que proporciona números de precisión variables en función del prefijo utilizado. No es tan conciso como algunos fragmentos, pero me gustan los resultados.
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)
El proyecto HumanFriendly ayuda con esto .
import humanfriendly
humanfriendly.format_size(1024)
El código anterior le dará 1 KB como respuesta.
Los ejemplos se pueden encontrar aquí .
A partir de todas las respuestas anteriores, aquí está mi opinión al respecto. Es un objeto que almacenará el tamaño del archivo en bytes como un entero. Pero cuando intenta imprimir el objeto, obtiene automáticamente una versión legible para humanos.
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)
Me gusta la precisión fija de la versión decimal de senderle , así que aquí hay una especie de híbrido de eso con la respuesta de joctee anterior (¿sabías que podrías tomar registros con bases no enteras?):
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 también habla sobre esta función.
Django moderno tiene una etiqueta de plantilla propia filesizeformat
:
Formatea el valor como un human-readable
tamaño de archivo (es decir, '13 KB ',' 4.1 MB ',' 102 bytes ', etc.).
Por ejemplo:
{{ value|filesizeformat }}
Si el valor es 123456789, la salida sería 117.7 MB.
Más información: https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#filesizeformat
¿Qué tal un simple 2 liner:
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])
Así es como funciona debajo del capó:
Kb
, por lo que la respuesta debería ser X KiB)file_size/value_of_closest_unit
junto con la unidad.Sin embargo, no funciona si el tamaño del archivo es 0 o negativo (porque el registro no está definido para los números 0 y -ve). Puede agregar controles adicionales para ellos:
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])
Ejemplos:
>>> humanizeFileSize(538244835492574234)
'478.06 PiB'
>>> humanizeFileSize(-924372537)
'881.55 MiB'
>>> humanizeFileSize(0)
'0 Bytes'
NOTA - Hay una diferencia entre Kb y KiB. KB significa 1000 bytes, mientras que KiB significa 1024 bytes. KB, MB, GB son múltiplos de 1000, mientras que KiB, MiB, GiB, etc. son múltiplos de 1024. Más información aquí.
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
Lo que está a punto de encontrar a continuación no es, de ninguna manera, la solución más eficiente o más corta entre las que ya se publicaron. En cambio, se enfoca en un tema particular que muchas de las otras respuestas pierden.
A saber, el caso cuando 999_995
se da una entrada como :
Python 3.6.1 ...
...
>>> value = 999_995
>>> base = 1000
>>> math.log(value, base)
1.999999276174054
que, al truncarse al entero más cercano y aplicarse nuevamente a la entrada, da
>>> order = int(math.log(value, base))
>>> value/base**order
999.995
Esto parece ser exactamente lo que esperaríamos hasta que se nos requiera controlar la precisión de salida . Y aquí es cuando las cosas comienzan a ponerse un poco difíciles.
Con la precisión establecida en 2 dígitos obtenemos:
>>> round(value/base**order, 2)
1000 # K
en lugar de 1M
.
¿Cómo podemos contrarrestar eso?
Por supuesto, podemos verificarlo explícitamente:
if round(value/base**order, 2) == base:
order += 1
¿Pero podemos hacerlo mejor? ¿Podemos saber de qué maneraorder
debe cortar antes de dar el paso final?
Resulta que podemos.
Suponiendo una regla de redondeo decimal de 0.5, la if
condición anterior se traduce en:
Resultando en
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]}'
dando
>>> 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
respuesta de referido , actualizada a:
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)
y el resultado de ejemplo es:
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
Esta solución también puede atraerlo, dependiendo de cómo funcione su mente:
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'