Лучший способ конвертировать размеры файлов в Python - PullRequest
52 голосов
/ 04 марта 2011

Я использую библиотеку, которая читает файл и возвращает его размер в байтах.

Этот размер файла затем отображается конечному пользователю;чтобы им было легче это понять, я явно конвертирую размер файла в MB, разделив его на 1024.0 * 1024.0.Конечно, это работает, но мне интересно, есть ли лучший способ сделать это в Python?

Лучше я имею в виду, возможно, функцию stdlib, которая может манипулировать размерами в соответствии с желаемым типом.Например, если я укажу MB, он автоматически делится на 1024.0 * 1024.0.Somethign на этих линиях.

Ответы [ 13 ]

95 голосов
/ 12 февраля 2013

Вот что я использую:

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])

Примечание: размер должен быть отправлен в байтах.

65 голосов
/ 04 марта 2011

Существует hurry.filesize , который примет размер в байтах и ​​из него получится хорошая строка.

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

Или, если вы хотите 1K == 1000 (чточто большинство пользователей предполагают):

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

Он также имеет поддержку IEC (но это не было задокументировано):

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

Поскольку он написан Awesome Martijn Faassen, кодмаленький, четкий и расширяемый.Писать собственные системы очень просто.

Вот один из них:

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

Используется примерно так:

>>> from hurry.filesize import size
>>> size(11000, system=mysystem)
'10 Bunches'
>>> size(198283722, system=mysystem)
'189 Heaps'
20 голосов
/ 16 октября 2012

Вместо делителя размера 1024 * 1024 вы можете использовать << битовый оператор смещения , то есть 1<<20 для получения мегабайт, 1<<30 для получения гигабайт и т. Д.

Я определил константу MBFACTOR = float(1<<20), которую затем можно использовать с байтами, то есть: megas = size_in_bytes/MBFACTOR.

15 голосов
/ 14 августа 2015

Вот компактная функция для расчета размера

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])

Для более подробного вывода и наоборот, пожалуйста, обратитесь: http://code.activestate.com/recipes/578019-bytes-to-human-human-to-bytes-converter/

7 голосов
/ 07 октября 2018

См. Ниже быстрый и относительно простой для чтения способ печати размеров файлов в одной строке кода, если вы уже знаете, что хотите.Эти однострочники объединяют отличный ответ @ ccpizza выше с некоторыми удобными приемами форматирования, которые я читал здесь Как напечатать число с запятыми в качестве разделителей тысяч? .

байт

print ('{:,.0f}'.format(os.path.getsize(filepath))+" B")

килобит

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

килобайт

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

мегабит

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

мегабайт

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

гигабит

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

Гигабайты

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

Терабайты

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

Очевидно, они предполагают, что вы примерно знаете, с каким размером вы будете иметь дело с самого начала, чтов моем случае (видеоредактор в South West London TV) имеет размер MB, а иногда и GB для видеоклипов.


ОБНОВЛЕНИЕ С ИСПОЛЬЗОВАНИЕМ PATHLIB В ответ на комментарий Хильди, вот мое предложение длякомпактная пара функций (сохраняя вещи «атомарными», а не объединяя их), используя только стандартную библиотеку Python:

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'
7 голосов
/ 31 октября 2016

На всякий случай, если кто-то ищет решение этой проблемы (как я уверен, сделал), вот что работает для меня:

def get_bytes(size, suffix):
    size = int(float(size))
    suffix = suffix.lower()

    if suffix == 'kb' or suffix == 'kib':
        return size << 10
    elif suffix == 'mb' or suffix == 'mib':
        return size << 20
    elif suffix == 'gb' or suffix == 'gib':
        return size << 30

    return False
2 голосов
/ 06 ноября 2017

Вот мои два цента, которые позволяют повышать и понижать, и добавляет настраиваемую точность:

def convertFloatToDecimal(f=0.0, precision=2):
    '''
    Convert a float to string of decimal.
    precision: by default 2.
    If no arg provided, return "0.00".
    '''
    return ("%." + str(precision) + "f") % f

def formatFileSize(size, sizeIn, sizeOut, precision=0):
    '''
    Convert file size to a string representing its value in B, KB, MB and GB.
    The convention is based on sizeIn as original unit and sizeOut
    as final unit. 
    '''
    assert sizeIn.upper() in {"B", "KB", "MB", "GB"}, "sizeIn type error"
    assert sizeOut.upper() in {"B", "KB", "MB", "GB"}, "sizeOut type error"
    if sizeIn == "B":
        if sizeOut == "KB":
            return convertFloatToDecimal((size/1024.0), precision)
        elif sizeOut == "MB":
            return convertFloatToDecimal((size/1024.0**2), precision)
        elif sizeOut == "GB":
            return convertFloatToDecimal((size/1024.0**3), precision)
    elif sizeIn == "KB":
        if sizeOut == "B":
            return convertFloatToDecimal((size*1024.0), precision)
        elif sizeOut == "MB":
            return convertFloatToDecimal((size/1024.0), precision)
        elif sizeOut == "GB":
            return convertFloatToDecimal((size/1024.0**2), precision)
    elif sizeIn == "MB":
        if sizeOut == "B":
            return convertFloatToDecimal((size*1024.0**2), precision)
        elif sizeOut == "KB":
            return convertFloatToDecimal((size*1024.0), precision)
        elif sizeOut == "GB":
            return convertFloatToDecimal((size/1024.0), precision)
    elif sizeIn == "GB":
        if sizeOut == "B":
            return convertFloatToDecimal((size*1024.0**3), precision)
        elif sizeOut == "KB":
            return convertFloatToDecimal((size*1024.0**2), precision)
        elif sizeOut == "MB":
            return convertFloatToDecimal((size*1024.0), precision)

Добавить TB и т. Д., Как вы хотите.

0 голосов
/ 21 мая 2019

Вот моя реализация:

from bisect import bisect

def to_filesize(bytes_num, si=True):
    decade = 1000 if si else 1024
    partitions = tuple(decade ** n for n in range(1, 6))
    suffixes = tuple('BKMGTP')

    i = bisect(partitions, bytes_num)
    s = suffixes[i]

    for n in range(i):
        bytes_num /= decade

    f = '{:.3f}'.format(bytes_num)

    return '{}{}'.format(f.rstrip('0').rstrip('.'), s)

Он напечатает до трех знаков после запятой и удалит конечные нули и точки. Логический параметр si переключает использование величины размера от 10 до 2.

Это его аналог. Это позволяет писать чистые файлы конфигурации, такие как {'maximum_filesize': from_filesize('10M'). Возвращает целое число, которое приблизительно соответствует предполагаемому размеру файла. Я не использую сдвиг битов, потому что исходное значение является числом с плавающей запятой (оно будет принимать from_filesize('2.15M') просто отлично). Преобразование его в целое / десятичное число будет работать, но усложнит код, и он уже работает как есть.

def from_filesize(spec, si=True):
    decade = 1000 if si else 1024
    suffixes = tuple('BKMGTP')

    num = float(spec[:-1])
    s = spec[-1]
    i = suffixes.index(s)

    for n in range(i):
        num *= decade

    return int(num)
0 голосов
/ 01 декабря 2018

Вот версия, которая соответствует выводу ls -lh .

def human_size(num: int) -> str:
    base = 1
    for unit in ['B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']:
        n = num / base
        if n < 9.95 and unit != 'B':
            # Less than 10 then keep 1 decimal place
            value = "{:.1f}{}".format(n, unit)
            return value
        if round(n) < 1000:
            # Less than 4 digits so use this
            value = "{}{}".format(round(n), unit)
            return value
        base *= 1024
    value = "{}{}".format(round(n), unit)
    return value
0 голосов
/ 20 сентября 2017

Я новичок в программировании. Я придумал следующую функцию, которая преобразует файл определенного размера в читаемый формат.

def file_size_converter(size):
    magic = lambda x: str(round(size/round(x/1024), 2))
    size_in_int = [int(1 << 10), int(1 << 20), int(1 << 30), int(1 << 40), int(1 << 50)]
    size_in_text = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
    for i in size_in_int:
        if size < i:
            g = size_in_int.index(i)
            position = int((1024 % i) / 1024 * g)
            ss = magic(i)
            return ss + ' ' + size_in_text[position]
...