Esistono vari frammenti sul Web che ti darebbero una funzione per restituire la dimensione leggibile umana dalla dimensione dei byte:
>>> human_readable(2048)
'2 kilobytes'
>>>
Ma c'è una libreria Python che fornisce questo?
Esistono vari frammenti sul Web che ti darebbero una funzione per restituire la dimensione leggibile umana dalla dimensione dei byte:
>>> human_readable(2048)
'2 kilobytes'
>>>
Ma c'è una libreria Python che fornisce questo?
Risposte:
Affrontare il problema "un'attività troppo piccola per richiedere una libreria" sopra descritta con un'implementazione semplice:
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)
supporti:
Esempio:
>>> sizeof_fmt(168963795964)
'157.4GiB'
di Fred Cirera
B
(cioè per unità diverse dai byte) vorresti che il fattore fosse 1000.0
piuttosto che 1024.0
no?
1
righe 4 e 6 con la precisione desiderata.
Una libreria che ha tutte le funzionalità che sembra stia cercando è humanize
. humanize.naturalsize()
sembra fare tutto quello che cerchi.
humanize.naturalsize(2048) # => '2.0 kB'
,humanize.naturalsize(2048, binary=True) # => '2.0 KiB'
humanize.naturalsize(2048, gnu=True) # => '2.0K'
Ecco la mia versione. Non utilizza un for-loop. Ha una complessità costante, O ( 1 ), ed è in teoria più efficiente delle risposte qui che usano un for-loop.
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'
Per chiarire cosa sta succedendo, possiamo omettere il codice per la formattazione della stringa. Ecco le linee che effettivamente fanno il lavoro:
exponent = int(log(num, 1024))
quotient = num / 1024**exponent
unit_list[exponent]
1000
mostrerebbe come 1,000 bytes
.
unit_list = list(zip(['bytes', 'kB', 'MB', 'GB', 'TB', 'PB'], [0, 0, 1, 2, 2, 2]))
I seguenti lavori in Python 3.6+, sono, a mio avviso, la risposta più semplice da comprendere qui, e consentono di personalizzare la quantità di cifre decimali utilizzate.
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}"
Mentre so che questa domanda è antica, di recente ho trovato una versione che evita i loop, usando log2
per determinare l'ordine delle dimensioni che funge anche da spostamento e un indice nell'elenco dei suffissi:
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])
Potrebbe ben essere considerato non pitonico per la sua leggibilità, però :)
size
o (1 << (order * 10)
nel float()
nell'ultima riga (per Python 2).
import math
lassù.
Dev'essere sempre uno di quei ragazzi. Bene oggi sono io. Ecco una soluzione a una riga o due righe se conti la firma della funzione.
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
invece)
Una di queste librerie è 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'
L'uso di potenze di 1000 o kibibyte sarebbe più standard-friendly:
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 Non fidarti mai di una libreria che ne stampa migliaia con il suffisso K (maiuscolo) :)
P.S. Never trust a library that prints thousands with the K (uppercase) suffix :)
Perchè no? Il codice potrebbe essere perfettamente valido e l'autore non ha preso in considerazione l'involucro del chilo. Sembra abbastanza ingannevole eliminare automaticamente qualsiasi codice in base alla tua regola ...
Questo farà ciò di cui hai bisogno in quasi ogni situazione, è personalizzabile con argomenti opzionali e, come puoi vedere, è praticamente autocompattante:
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)
Esempio di output:
>>> 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'
Personalizzazioni avanzate:
>>> 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'
Questo codice è compatibile con Python 2 e Python 3. La conformità PEP8 è un esercizio per il lettore. Ricorda, è l' output che è carino.
Aggiornare:
Se hai bisogno di migliaia di virgole, applica l'estensione ovvia:
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))
Per esempio:
>>> pretty_units(987654321098765432109876543210)
'816,968.5 YiB'
Dovresti usare "umanizzare".
>>> humanize.naturalsize(1000000)
'1.0 MB'
>>> humanize.naturalsize(1000000, binary=True)
'976.6 KiB'
>>> humanize.naturalsize(1000000, gnu=True)
'976.6K'
Riferimento:
Riffing sullo snippet fornito in alternativa a hurry.filesize (), ecco uno snippet che fornisce numeri di precisione variabili in base al prefisso utilizzato. Non è così conciso come alcuni frammenti, ma mi piacciono i risultati.
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)
Il progetto HumanFriendly aiuta in questo .
import humanfriendly
humanfriendly.format_size(1024)
Il codice sopra fornirà 1 KB come risposta.
Esempi sono disponibili qui .
Attingendo a tutte le risposte precedenti, ecco la mia opinione su di esso. È un oggetto che memorizzerà la dimensione del file in byte come numero intero. Ma quando provi a stampare l'oggetto, ottieni automaticamente una versione leggibile dall'uomo.
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)
Mi piace la precisione fissa della versione decimale di Senderle , quindi ecco una sorta di ibrido di quello con la risposta di joctee sopra (sapevi che potresti prendere i log con basi non intere?):
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 parla anche di questa funzione.
Il Django moderno ha un tag self template filesizeformat
:
Formatta il valore come a human-readable
dimensione del file (ad esempio "13 KB", "4,1 MB", "102 byte", ecc.).
Per esempio:
{{ value|filesizeformat }}
Se il valore è 123456789, l'output sarebbe 117,7 MB.
Maggiori informazioni: https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#filesizeformat
Che ne dici di un semplice 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])
Ecco come funziona sotto il cofano:
Kb
, quindi la risposta dovrebbe essere X KiB)file_size/value_of_closest_unit
insieme all'unità.Tuttavia, non funziona se la dimensione del file è 0 o negativa (perché il registro non è definito per i numeri 0 e -ve). È possibile aggiungere ulteriori controlli per loro:
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])
Esempi:
>>> humanizeFileSize(538244835492574234)
'478.06 PiB'
>>> humanizeFileSize(-924372537)
'881.55 MiB'
>>> humanizeFileSize(0)
'0 Bytes'
NOTA : esiste una differenza tra Kb e KiB. KB significa 1000 byte, mentre KiB significa 1024 byte. KB, MB, GB sono tutti multipli di 1000, mentre KiB, MiB, GiB ecc. Sono tutti multipli di 1024. Maggiori informazioni qui
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
Quello che stai per trovare di seguito non è affatto la soluzione più performante o più breve tra quelle già pubblicate. Invece, si concentra su un problema particolare che molte delle altre risposte mancano.
Vale a dire il caso in cui 999_995
viene fornito un input simile :
Python 3.6.1 ...
...
>>> value = 999_995
>>> base = 1000
>>> math.log(value, base)
1.999999276174054
che, troncato al numero intero più vicino e applicato nuovamente all'input, dà
>>> order = int(math.log(value, base))
>>> value/base**order
999.995
Questo sembra essere esattamente ciò che ci aspetteremmo fino a quando non saremo tenuti a controllare la precisione dell'output . E questo è quando le cose iniziano a diventare un po 'difficili.
Con la precisione impostata su 2 cifre otteniamo:
>>> round(value/base**order, 2)
1000 # K
invece di 1M
.
Come possiamo contrastarlo?
Certo, possiamo verificarlo esplicitamente:
if round(value/base**order, 2) == base:
order += 1
Ma possiamo fare di meglio? Possiamo sapere in che modo order
tagliare prima di fare il passo finale?
Si scopre che possiamo.
Supponendo una regola di arrotondamento decimale 0,5, la if
condizione precedente si traduce in:
con il risultato di
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'
fare riferimento Sridhar Ratnakumar
alla risposta, aggiornata 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)
e l'output di esempio è:
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
Questa soluzione potrebbe piacere anche a te, a seconda di come funziona la tua 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'