Il existe divers extraits sur le Web qui vous donneraient une fonction permettant de retourner une taille lisible par l'homme à partir d'une taille en octets:
>>> human_readable(2048)
'2 kilobytes'
>>>
Mais existe-t-il une bibliothèque Python qui fournit cela?
Aborder le problème ci-dessus "une tâche trop petite pour nécessiter une bibliothèque" par une implémentation simple:
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)
Les soutiens:
Exemple:
>>> sizeof_fmt(168963795964)
'157.4GiB'
par Fred Cirera
Une bibliothèque qui possède toutes les fonctionnalités que vous semblez recherchées est humanize
. humanize.naturalsize()
semble faire tout ce que vous cherchez.
Voici ma version. Il n'utilise pas de boucle for. Il a une complexité constante, O ( 1 ), et est en théorie plus efficace que les réponses ici qui utilisent une boucle 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'
Pour rendre plus clair ce qui se passe, nous pouvons omettre le code pour le formatage de la chaîne. Voici les lignes qui font réellement le travail:
exponent = int(log(num, 1024))
quotient = num / 1024**exponent
unit_list[exponent]
Bien que je sache que cette question est ancienne, j’ai récemment proposé une version qui évite les boucles, en utilisant log2
pour déterminer l’ordre de taille qui sert également de décalage et d’index dans la liste de suffixes:
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])
Pourrait être considéré comme non rythmique pour sa lisibilité, cependant :)
Si vous utilisez Django installé, vous pouvez également essayer filesizeformat :
from Django.template.defaultfilters import filesizeformat
filesizeformat(1073741824)
=>
"1.0 GB"
Il doit toujours y avoir un de ces gars. Eh bien aujourd'hui c'est moi. Voici une solution à une ligne - ou deux lignes si vous comptez la signature de la fonction.
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
Une de ces bibliothèques est 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'
Utiliser des puissances de 1000 ou kibibytes serait plus pratique:
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. Ne faites jamais confiance à une bibliothèque qui imprime des milliers de personnes avec le suffixe K (majuscule) :)
Cela fera ce dont vous avez besoin dans presque toutes les situations, il est personnalisable avec des arguments optionnels et, comme vous pouvez le voir, est pretty beaucoup auto-documenté:
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)
Exemple de sortie:
>>> 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'
Personnalisations avancées:
>>> 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'
Ce code est compatible avec Python 2 et Python 3. La conformité PEP8 est un exercice pour le lecteur. Rappelez-vous, c'est le output qui est joli.
Mettre à jour:
Si vous avez besoin de milliers de virgules, appliquez simplement l'extension évidente:
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))
Par exemple:
>>> pretty_units(987654321098765432109876543210)
'816,968.5 YiB'
En sortant de la solution Sridhar Ratnakumar, j'aime un peu mieux ça. Fonctionne en Python 3.6+
def human_readable_size(size, decimal_places):
for unit in ['','KB','MB','GB','TB']:
if size < 1024.0:
break
size /= 1024.0
return f"{size:.{decimal_places}f}{unit}"
En se basant sur l'extrait fourni comme alternative à hurry.filesize (), voici un extrait qui donne des nombres de précision variables en fonction du préfixe utilisé. Ce n'est pas aussi concis que des extraits, mais j'aime les résultats.
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)
En me basant sur toutes les réponses précédentes, voici mon point de vue. C'est un objet qui stockera la taille du fichier en octets sous forme d'entier. Mais lorsque vous essayez d'imprimer l'objet, vous obtenez automatiquement une version lisible par l'homme.
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)
J'aime la précision fixe de la version décimale de senderle , alors voici une sorte d'hybride de cela avec la réponse de joctee ci-dessus (saviez-vous que vous pouvez prendre des journaux avec des bases non entières?):
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')
Le projet HumanFriendly aide avec cela .
import humanfriendly
humanfriendly.format_size(1024)
Le code ci-dessus donnera 1 Ko comme réponse.
Exemples peuvent être trouvés ici .
Que diriez-vous d'un simple 2 doublure:
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])
Voici comment cela fonctionne sous le capot:
Kb
, la réponse devrait donc être X KiB)file_size/value_of_closest_unit
avec l'unité.Cependant, cela ne fonctionne pas si la taille du fichier est 0 ou négatif (parce que le journal n'est pas défini pour les nombres 0 et -ve). Vous pouvez ajouter des contrôles supplémentaires pour eux:
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])
Exemples:
>>> humanizeFileSize(538244835492574234)
'478.06 PiB'
>>> humanizeFileSize(-924372537)
'881.55 MiB'
>>> humanizeFileSize(0)
'0 Bytes'
NOTE - Il y a une différence entre Kb et KiB. KB signifie 1000 octets, alors que KiB signifie 1024 octets. KB, MB, GB sont tous des multiples de 1000, alors que KiB, MiB, GiB, etc. sont tous des multiples de 1024. Pour en savoir plus ici
DiveIntoPython3 aussi parle de cette fonction.
Vous devriez utiliser "humaniser".
>>> humanize.naturalsize(1000000)
'1.0 MB'
>>> humanize.naturalsize(1000000, binary=True)
'976.6 KiB'
>>> humanize.naturalsize(1000000, gnu=True)
'976.6K'
Référence:
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
Django moderne a une balise de modèle autonome filesizeformat
:
Formate la valeur comme une taille de fichier human-readable
(c'est-à-dire '13 Ko ',' 4.1 MB ',' 102 octets ', etc.).
Par exemple:
{{ value|filesizeformat }}
Si la valeur est 123456789, la sortie serait 117,7 Mo.
Plus d'infos: https://docs.djangoproject.com/fr/1.10/ref/templates/builtins/#filesizeformatat
Ce que vous êtes sur le point de trouver ci-dessous n'est en aucun cas la solution la plus performante ou la plus courte parmi celles déjà publiées. Au lieu de cela, il se concentre sur un problème particulier qui manque à beaucoup d’autres réponses.
À savoir le cas où une entrée comme 999_995
est donnée:
Python 3.6.1 ...
...
>>> value = 999_995
>>> base = 1000
>>> math.log(value, base)
1.999999276174054
qui, tronqué au nombre entier le plus proche et appliqué à l’entrée donne
>>> order = int(math.log(value, base))
>>> value/base**order
999.995
Cela semble être exactement ce à quoi nous nous attendions jusqu'à ce que nous devions contrôler précision de la sortie . Et c'est à ce moment que les choses commencent à devenir un peu difficiles.
Avec la précision réglée à 2 chiffres, nous obtenons:
>>> round(value/base**order, 2)
1000 # K
au lieu de 1M
.
Comment pouvons-nous contrer cela?
Bien sûr, nous pouvons le vérifier explicitement:
if round(value/base**order, 2) == base:
order += 1
Mais pouvons-nous faire mieux? Pouvons-nous savoir de quelle manière la order
doit être réduite avant de passer à l'étape finale?
Il s'avère que nous pouvons.
En supposant une règle d’arrondi de 0,5 décimale, la condition if
ci-dessus se traduit par:
résultant 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]}'
donnant
>>> 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'
reportez-vous à la réponse de Sridhar Ratnakumar
, mise à jour pour:
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)
et la sortie d'exemple est:
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
Cette solution pourrait également vous intéresser, selon le fonctionnement de votre esprit:
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'