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


84

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

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

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


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

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

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


Я проголосую за это, потому что это можно решить только со стандартной библиотекой 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)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.