Лучший способ конвертировать размеры файлов в Python [закрыто]

84

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

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

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

user225312
источник
5
Так что напишите один. Также обратите внимание, что многие системы теперь используют MB для обозначения 10 ^ 6 вместо 2 ^ 20.
тк.
6
@AA, @tc: Имейте в виду, что нормы SI и IEC - kB (Kilo) for 1.000 Byteи KiB (Kibi) for 1.024 Byte. См. En.wikipedia.org/wiki/Kibibyte .
Бобби,
2
@Bobby: kB фактически означает "килобель", равный 10000 дБ. Для байта не существует единицы СИ. IIRC, IEC рекомендует KiB, но не определяет kB или KB.
тк.
2
@tc. Префикс kilo определяется в SI как 1000. IEC определил kB и т. Д. Для использования префикса SI вместо 2 ^ 10.
Ford
1
Я имею в виду, что префиксы обычно определяются SI, а сокращения для размера данных - нет: Physics.nist.gov/cuu/Units/prefixes.html . Они определены IEC: Physics.nist.gov/cuu/Units/binary.html
ford

Ответы:

101

Существует 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'

Поскольку он написан Прекрасным Мартином Фаассеном, код небольшой, понятный и расширяемый. Написать свою собственную систему очень просто.

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

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'
Леннарт Регебро
источник
1
Хм, теперь мне нужен другой, чтобы пойти другим путем. От «1 кб» до 1024(int).
mlissner 09
2
Работает только в python 2
e-info128
2
Этот пакет может быть крутым, но странная лицензия и тот факт, что в сети нет исходного кода, делают его тем, чего я бы очень рад избежать. А также, похоже, поддерживает только python2.
Альмог Коэн
1
@AlmogCohen, источник находится в сети, доступен прямо из PyPI (у некоторых пакетов нет репозитория Github, только страница PyPI), и лицензия не такая уж непонятная, ZPL - это общественная лицензия Zope, которая, насколько мне известно , BSD-подобный. Я согласен с тем, что само лицензирование является странным: нет стандартного файла LICENSE.txt и нет преамбулы в верхней части каждого исходного файла.
sleblanc
1
Для того , чтобы получить мегабайт я следующее уравнение с помощью побитового оператора Shifting: MBFACTOR = float(1 << 20); mb= int(size_in_bytes) / MBFACTOR @LennartRegebro
Alper
151

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

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: размер должен быть отправлен в байтах.

Джеймс
источник
11
Если вы отправляете размер в байтах, просто добавьте «B» в качестве первого элемента size_name.
tuxGurl
Когда размер файла равен 0, он не работает. log (0, 1024) не определен! Вы должны проверить регистр 0 байт перед этим оператором i = int (math.floor (math.log (size, 1024))).
genclik27
genclik - ты прав. Я только что отправил небольшое изменение, которое исправит это и включит преобразование из байтов. Спасибо, Сапам, за оригинал
FarmerGedden
Привет @WHK, поскольку tuxGurl упомянул, что это простое решение.
Джеймс
3
На самом деле имена размеров должны быть («B», «KiB», «MiB», «GiB», «TiB», «PiB», «EiB», «ZiB», «YiB»). См. En.wikipedia.org/wiki/Mebibyte для получения дополнительной информации.
Alex
38

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

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

Обычно все, что вам нужно, это мегабайты, или можно использовать что-то вроде этого:

# 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
ccpizza
источник
10
Не правда >>ли?
Tjorriemorrie 08
2
@Tjorriemorrie: это должен быть сдвиг влево, сдвиг вправо отбросит единственный бит и приведет к 0.
ccpizza
Блестящий ответ. Спасибо.
Борислав Аймалиев 08
Я знаю, что это старый, но будет ли это правильным использованием? def convert_to_mb (data_b): print (data_b / (1 << 20))
roastbeeef
24

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

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/

Паван Гупта
источник
15

См. Ниже быстрый и относительно простой для чтения способ печати размеров файлов в одной строке кода, если вы уже знаете, что хотите. Эти однострочники сочетают в себе отличный ответ @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) составляет МБ, а иногда и ГБ для видеоклипов.


ОБНОВЛЕНИЕ С ИСПОЛЬЗОВАНИЕМ 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
>>> format_size("d:\\media\\bags of fun.avi")
'38 MB'
>>> format_size("d:\\media\\bags of fun.avi","KB")
'38,763 KB'
>>> format_size("d:\\media\\bags of fun.avi","kb")
'310,104 kb'
Питер Ф
источник
1
Это довольно умный способ сделать это. Интересно, можете ли вы поместить их в функцию, в которой вы передаете, хотите ли вы kb. мб и так далее. У вас даже может быть команда ввода, которая спрашивает, какую из них вы хотите, что было бы очень удобно, если вы будете делать это часто.
Hildy
См. Выше, Хильди ... Вы также можете настроить строку словаря, например @ lennart-regebro, описанную выше ... которая может быть полезна для управления хранилищем, например, "Раздел", "Кластер", "Диски 4 ТБ", "DVD_RW", " Blu-Ray Disk "," карты памяти 1GB "или что-то еще.
Peter F
Я также только что добавил Kb (килобит), Mb (мегабит) и Gb (гигабит) - пользователей часто путают с точки зрения скорости сети или передачи файлов, поэтому подумал, что это может быть удобно.
Peter F
9

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

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
Ромео Михалча
источник
Вы не обрабатываете десятичные числа, такие как 1,5 ГБ. Чтобы исправить это просто изменить , << 10чтобы * 1024, << 20чтобы * 1024**2и << 30в * 1024**3.
E235
3

Вот:

def convert_bytes(size):
   for x in ['bytes', 'KB', 'MB', 'GB', 'TB']:
       if size < 1024.0:
           return "%3.1f %s" % (size, x)
       size /= 1024.0

   return size
Rhoitjadhav
источник
2

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

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и тд, как хотите.

WesternGun
источник
Я проголосую за это, потому что это можно решить только со стандартной библиотекой python
Ciasto piekarz
1
UNITS = {1000: ['KB', 'MB', 'GB'],
            1024: ['KiB', 'MiB', 'GiB']}

def approximate_size(size, flag_1024_or_1000=True):
    mult = 1024 if flag_1024_or_1000 else 1000
    for unit in UNITS[mult]:
        size = size / mult
        if size < mult:
            return '{0:.3f} {1}'.format(size, unit)

approximate_size(2123, False)
Камран Каусар
источник
это можно использовать во многих настройках. рад, что наткнулся на этот комментарий. большое спасибо.
Саураб Джайн,
0

Вот версия, которая соответствует выводу 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

Я хотел двухстороннее преобразование, и я хотел использовать поддержку Python 3 format (), чтобы сделать его максимально питоническим. Может быть, попробовать модуль библиотеки datasize? https://pypi.org/project/datasize/

$ pip install -qqq datasize
$ python
...
>>> from datasize import DataSize
>>> 'My new {:GB} SSD really only stores {:.2GiB} of data.'.format(DataSize('750GB'),DataSize(DataSize('750GB') * 0.8))
'My new 750GB SSD really only stores 558.79GiB of data.'
Джереми
источник
-1

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

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)
слеблан
источник