152 votes

Meilleure façon de convertir la taille des fichiers en Python

J'utilise une bibliothèque qui lit un fichier et renvoie sa taille en octets.

Cette taille de fichier est ensuite affichée à l'utilisateur final ; pour faciliter sa compréhension, je convertis explicitement la taille du fichier en MB en le divisant par 1024.0 * 1024.0 . Bien sûr, cela fonctionne, mais je me demande s'il y a une meilleure façon de faire cela en Python ?

Par mieux, j'entends peut-être une fonction stdlib qui peut manipuler les tailles en fonction du type que je souhaite. Par exemple, si je spécifie MB il le divise automatiquement par 1024.0 * 1024.0 . Quelque chose de ce genre.

266voto

sjcipher Points 2626

Voici ce que j'utilise :

import math

def convert_size(size_bytes):
   if size_bytes == 0:
       return "0B"
   size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
   i = int(math.floor(math.log(size_bytes, 1024)))
   p = math.pow(1024, i)
   s = round(size_bytes / p, 2)
   return "%s %s" % (s, size_name[i])

NB : la taille doit être envoyée en octets.

150voto

Lennart Regebro Points 52510

Il y a hurry.filesize qui prendra la taille en octets et en fera une belle chaîne.

>>> from hurry.filesize import size
>>> size(11000)
'10K'
>>> size(198283722)
'189M'

Ou si vous voulez que 1K == 1000 (ce que la plupart des utilisateurs supposent) :

>>> from hurry.filesize import size, si
>>> size(11000, system=si)
'11K'
>>> size(198283722, system=si)
'198M'

Il est également compatible avec la norme IEC (mais cela n'a pas été documenté) :

>>> from hurry.filesize import size, iec
>>> size(11000, system=iec)
'10Ki'
>>> size(198283722, system=iec)
'189Mi'

Parce qu'il est écrit par le génial Martijn Faassen, le code est petit, clair et extensible. Il est très facile d'écrire ses propres systèmes.

En voici une :

mysystem = [
    (1024 ** 5, ' Megamanys'),
    (1024 ** 4, ' Lotses'),
    (1024 ** 3, ' Tons'), 
    (1024 ** 2, ' Heaps'), 
    (1024 ** 1, ' Bunches'),
    (1024 ** 0, ' Thingies'),
    ]

Utilisé comme tel :

>>> from hurry.filesize import size
>>> size(11000, system=mysystem)
'10 Bunches'
>>> size(198283722, system=mysystem)
'189 Heaps'

62voto

ccpizza Points 2653

Au lieu d'un diviseur de taille de 1024 * 1024 vous pouvez utiliser le << opérateur de décalage bit à bit , c'est-à-dire 1<<20 pour obtenir des mégaoctets, 1<<30 pour obtenir des gigaoctets, etc.

Dans le scénario le plus simple, vous pouvez avoir, par exemple, une constante MBFACTOR = float(1<<20) qui peut alors être utilisé avec des octets, c'est-à-dire : megas = size_in_bytes/MBFACTOR .

Les mégaoctets sont généralement suffisants, sinon on peut utiliser une solution de ce type :

# bytes pretty-printing
UNITS_MAPPING = [
    (1<<50, ' PB'),
    (1<<40, ' TB'),
    (1<<30, ' GB'),
    (1<<20, ' MB'),
    (1<<10, ' KB'),
    (1, (' byte', ' bytes')),
]

def pretty_size(bytes, units=UNITS_MAPPING):
    """Get human-readable file sizes.
    simplified version of https://pypi.python.org/pypi/hurry.filesize/
    """
    for factor, suffix in units:
        if bytes >= factor:
            break
    amount = int(bytes / factor)

    if isinstance(suffix, tuple):
        singular, multiple = suffix
        if amount == 1:
            suffix = singular
        else:
            suffix = multiple
    return str(amount) + suffix

print(pretty_size(1))
print(pretty_size(42))
print(pretty_size(4096))
print(pretty_size(238048577))
print(pretty_size(334073741824))
print(pretty_size(96995116277763))
print(pretty_size(3125899904842624))

## [Out] ###########################
1 byte
42 bytes
4 KB
227 MB
311 GB
88 TB
2 PB

48voto

Peter F Points 149

Voici quelques modèles faciles à copier que vous pouvez utiliser si vous connaissez déjà la taille de l'unité que vous souhaitez. Si vous recherchez une fonction plus générique avec quelques options intéressantes, consultez ma mise à jour de FEB 2021 plus loin...

Octets

print(f"{os.path.getsize(filepath):,} B") 

Kilobits

print(f"{os.path.getsize(filepath)/(1<<7):,.0f} kb")

Kilo-octets

print(f"{os.path.getsize(filepath)/(1<<10):,.0f} KB")

Mégabits

print(f"{os.path.getsize(filepath)/(1<<17):,.0f} mb")

Mégaoctets

print(f"{os.path.getsize(filepath)/(1<<20):,.0f} MB")

Gigabits

print(f"{os.path.getsize(filepath)/(1<<27):,.0f} gb")

Gigaoctets

print(f"{os.path.getsize(filepath)/(1<<30):,.0f} GB")

Téraoctets

print(f"{os.path.getsize(filepath)/(1<<40):,.0f} TB")

MISE À JOUR FEB 2021 Voici mes fonctions mises à jour et étoffées pour a) obtenir la taille d'un fichier/dossier, b) convertir dans les unités souhaitées :

from pathlib import Path

def get_path_size(path = Path('.'), recursive=False):
    """
    Gets file size, or total directory size

    Parameters
    ----------
    path: str | pathlib.Path
        File path or directory/folder path

    recursive: bool
        True -> use .rglob i.e. include nested files and directories
        False -> use .glob i.e. only process current directory/folder

    Returns
    -------
    int:
        File size or recursive directory size in bytes
        Use cleverutils.format_bytes to convert to other units e.g. MB
    """
    path = Path(path)
    if path.is_file():
        size = path.stat().st_size
    elif path.is_dir():
        path_glob = path.rglob('*.*') if recursive else path.glob('*.*')
        size = sum(file.stat().st_size for file in path_glob)
    return size

def format_bytes(bytes, unit, SI=False):
    """
    Converts bytes to common units such as kb, kib, KB, mb, mib, MB

    Parameters
    ---------
    bytes: int
        Number of bytes to be converted

    unit: str
        Desired unit of measure for output

    SI: bool
        True -> Use SI standard e.g. KB = 1000 bytes
        False -> Use JEDEC standard e.g. KB = 1024 bytes

    Returns
    -------
    str:
        E.g. "7 MiB" where MiB is the original unit abbreviation supplied
    """
    if unit.lower() in "b bit bits".split():
        return f"{bytes*8} {unit}"
    unitN = unit[0].upper()+unit[1:].replace("s","")  # Normalised
    reference = {"Kb Kib Kibibit Kilobit": (7, 1),
                 "KB KiB Kibibyte Kilobyte": (10, 1),
                 "Mb Mib Mebibit Megabit": (17, 2),
                 "MB MiB Mebibyte Megabyte": (20, 2),
                 "Gb Gib Gibibit Gigabit": (27, 3),
                 "GB GiB Gibibyte Gigabyte": (30, 3),
                 "Tb Tib Tebibit Terabit": (37, 4),
                 "TB TiB Tebibyte Terabyte": (40, 4),
                 "Pb Pib Pebibit Petabit": (47, 5),
                 "PB PiB Pebibyte Petabyte": (50, 5),
                 "Eb Eib Exbibit Exabit": (57, 6),
                 "EB EiB Exbibyte Exabyte": (60, 6),
                 "Zb Zib Zebibit Zettabit": (67, 7),
                 "ZB ZiB Zebibyte Zettabyte": (70, 7),
                 "Yb Yib Yobibit Yottabit": (77, 8),
                 "YB YiB Yobibyte Yottabyte": (80, 8),
                 }
    key_list = '\n'.join(["     b Bit"] + [x for x in reference.keys()]) +"\n"
    if unitN not in key_list:
        raise IndexError(f"\n\nConversion unit must be one of:\n\n{key_list}")
    units, divisors = [(k,v) for k,v in reference.items() if unitN in k][0]
    if SI:
        divisor = 1000**divisors[1]/8 if "bit" in units else 1000**divisors[1]
    else:
        divisor = float(1 << divisors[0])
    value = bytes / divisor
    return f"{value:,.0f} {unitN}{(value != 1 and len(unitN) > 3)*'s'}"

# Tests 
>>> assert format_bytes(1,"b") == '8 b'
>>> assert format_bytes(1,"bits") == '8 bits'
>>> assert format_bytes(1024, "kilobyte") == "1 Kilobyte"
>>> assert format_bytes(1024, "kB") == "1 KB"
>>> assert format_bytes(7141000, "mb") == '54 Mb'
>>> assert format_bytes(7141000, "mib") == '54 Mib'
>>> assert format_bytes(7141000, "Mb") == '54 Mb'
>>> assert format_bytes(7141000, "MB") == '7 MB'
>>> assert format_bytes(7141000, "mebibytes") == '7 Mebibytes'
>>> assert format_bytes(7141000, "gb") == '0 Gb'
>>> assert format_bytes(1000000, "kB") == '977 KB'
>>> assert format_bytes(1000000, "kB", SI=True) == '1,000 KB'
>>> assert format_bytes(1000000, "kb") == '7,812 Kb'
>>> assert format_bytes(1000000, "kb", SI=True) == '8,000 Kb'
>>> assert format_bytes(125000, "kb") == '977 Kb'
>>> assert format_bytes(125000, "kb", SI=True) == '1,000 Kb'
>>> assert format_bytes(125*1024, "kb") == '1,000 Kb'
>>> assert format_bytes(125*1024, "kb", SI=True) == '1,024 Kb'

MISE À JOUR OCT 2022

_Ma réponse à un commentaire récent était trop longue, alors voici quelques explications supplémentaires sur la magie 1<<20 ! Je remarque également que flotteur n'est pas nécessaire, je l'ai donc supprimé des exemples ci-dessus._

Comme indiqué dans une autre réponse (ci-dessus), "<<" est appelé un "opérateur bitwise". Il convertit le côté gauche en binaire et déplace les chiffres binaires de 20 places vers la gauche (dans ce cas). Lorsque nous comptons normalement en décimal, le nombre total de chiffres indique si nous avons atteint les dizaines, les centaines, les milliers, les millions, etc. C'est la même chose en binaire, sauf que le nombre de chiffres indique s'il s'agit de bits, d'octets, de kilo-octets, de méga-octets, etc. Ainsi, .... 1<<20 est en fait la même chose que le 1 (binaire) suivi de 20 zéros (binaires) ou, si vous vous souvenez de la conversion du binaire au décimal : 2 à la puissance 20 (2**20), ce qui équivaut à 1048576. Dans les extraits ci-dessus, os.path.getsize renvoie une valeur en BYTES et 1048576 octets correspondent strictement à un Mébibyte (MiB) et plus couramment à un Mégaoctet (MB).

31voto

Pavan Gupta Points 2644

Voici la fonction compacte qui permet de calculer la taille

def GetHumanReadable(size,precision=2):
    suffixes=['B','KB','MB','GB','TB']
    suffixIndex = 0
    while size > 1024 and suffixIndex < 4:
        suffixIndex += 1 #increment the index of the suffix
        size = size/1024.0 #apply the division
    return "%.*f%s"%(precision,size,suffixes[suffixIndex])

Pour plus de détails sur la sortie et l'opération inverse, veuillez vous référer : http://code.activestate.com/recipes/578019-bytes-to-human-human-to-bytes-converter/

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X