Многократно используемая библиотека для получения доступной для пользователя версии размера файла?

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

>>> human_readable(2048)
'2 kilobytes'
>>>

Но есть ли такая библиотека Python?

Ответ 1

Устранение проблемы "слишком маленькая задача, требующая библиотеки" путем простой реализации:

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)

Поддержка:

  • все известные в настоящее время бинарные префиксы
  • отрицательные и положительные числа
  • числа, превышающие 1000 Yobibytes
  • произвольные единицы (возможно, вам нравится считать в Гибибитах!)

Пример:

>>> sizeof_fmt(168963795964)
'157.4GiB'

Фред Сиера

Ответ 2

Библиотека, которая обладает всеми функциональными возможностями, которые, по вашему мнению, вы ищете, humanize. humanize.naturalsize() похоже, делает все, что вы ищете.

Ответ 3

Вот моя версия. Он не использует цикл for. Он имеет постоянную сложность, O (1) и теоретически более эффективен, чем ответы здесь, которые используют цикл 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'

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

exponent = int(log(num, 1024))
quotient = num / 1024**exponent
unit_list[exponent]

Ответ 4

Пока я знаю, что этот вопрос древний, я недавно придумал версию, которая позволяет избежать циклов, используя log2, чтобы определить порядок размеров, который удваивается как сдвиг и индекс в списке суффиксов:

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

Можно было бы считать непритязательным для его удобочитаемости, хотя:)

Ответ 5

Там всегда был один из тех парней. Ну сегодня это я. Здесь однострочное решение - или две строки, если вы подсчитаете подпись функции.

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

Ответ 6

Если вы используете установленную Django, вы также можете попробовать filesizeformat:

from django.template.defaultfilters import filesizeformat
filesizeformat(1073741824)

=>

"1.0 GB"

Ответ 7

Одна такая библиотека 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'

Ответ 8

Использование значений мощности 1000 или kibibytes будет более стандартным:

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. Никогда не доверяйте библиотеке, которая печатает тысячи с суффиксом K (верхний регистр):)

Ответ 9

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

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)

Пример вывода:

>>> 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'

Расширенные настройки:

>>> 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'

Этот код совместим как с Python 2, так и с Python 3. Соответствие PEP8 - упражнение для читателя. Помните, что этот результат довольно хорош.

Обновление:

Если вам нужны тысячи запятых, просто примените очевидное расширение:

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

Например:

>>> pretty_units(987654321098765432109876543210)
'816,968.5 YiB'

Ответ 10

Riffing на фрагменте, предоставленном в качестве альтернативы hurry.filesize(), представляет собой фрагмент, который дает различные номера точности на основе используемого префикса. Это не так сложно, как некоторые фрагменты, но мне нравятся результаты.

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)

Ответ 11

Следующее работает в 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}"

Ответ 12

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

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)

Ответ 13

Мне нравится фиксированная точность десятичной версии senderle, поэтому здесь есть своего рода гибрид с ответом на joctee выше (знаете ли вы, что вы можете брать журналы с не- целочисленные базы?):

from math import log
def human_readable_bytes(x):
    # hybrid of https://stackoverflow.com/a/10171475/2595465
    #      with /questions/39297/python-formatting-large-values/285199#285199
    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')

Ответ 14

Вы должны использовать "гуманизировать".

>>> humanize.naturalsize(1000000)
'1.0 MB'
>>> humanize.naturalsize(1000000, binary=True)
'976.6 KiB'
>>> humanize.naturalsize(1000000, gnu=True)
'976.6K'

Ссылка:

https://pypi.org/project/humanize/

Ответ 15

Проект HumanFriendly помогает в этом.

import humanfriendly
humanfriendly.format_size(1024)

Вышеприведенный код даст 1KB в качестве ответа.
Примеры можно найти здесь.

Ответ 17

Как насчет простой 2 лайнера:

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

Вот как он работает под капотом:

  • Вычисляет log 2 (размер файла)
  • Разделяет его на 10, чтобы получить ближайшую единицу. (например, если размер составляет 5000 байтов, ближайшая единица Kb, поэтому ответ должен быть X KiB)
  • Возвращает file_size/value_of_closest_unit вместе с единицей.

Однако он не работает, если размер файла равен 0 или отрицательный (поскольку журнал undefined для 0 и -ve номеров). Вы можете добавить дополнительные проверки для них:

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

<сильные > Примеры:

>>> humanizeFileSize(538244835492574234)
'478.06 PiB'
>>> humanizeFileSize(-924372537)
'881.55 MiB'
>>> humanizeFileSize(0)
'0 Bytes'

ПРИМЕЧАНИЕ. Существует различие между Kb и KiB. KB означает 1000 байтов, тогда как KiB означает 1024 байта. KB, MB, GB все кратные 1000, тогда как KiB, MiB, GiB и т.д. Все кратные 1024. Подробнее об этом здесь

Ответ 18

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

Ответ 19

Современный Django имеет собственный тег шаблона filesizeformat:

Форматирует значение как размер файла human-readable (т.е. "13 КБ", "4.1 МБ", "102 байта" и т.д.).

Например:

{{ value|filesizeformat }}

Если значение равно 123456789, выход будет равен 117,7 МБ.

Дополнительная информация: https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#filesizeformat

Ответ 20

То, что вы собираетесь найти ниже, ни в коем случае не является самым эффективным или самым коротким решением среди уже опубликованных. Вместо этого он фокусируется на одной конкретной проблеме, которую пропускают многие другие ответы.

А именно, случай, когда 999_995 как 999_995:

Python 3.6.1 ...
...
>>> value = 999_995
>>> base = 1000
>>> math.log(value, base)
1.999999276174054

который, будучи усеченным до ближайшего целого числа и примененным обратно к входу, дает

>>> order = int(math.log(value, base))
>>> value/base**order
999.995

Похоже, это именно то, что мы ожидали, пока нам не нужно контролировать точность вывода. И это когда вещи начинают становиться немного сложнее.

С точностью до 2 цифр получаем:

>>> round(value/base**order, 2)
1000 # K

вместо 1M.

Как мы можем противостоять этому?

Конечно, мы можем проверить это явно:

if round(value/base**order, 2) == base:
    order += 1

Но можем ли мы сделать лучше? Можем ли мы узнать, каким образом следует сократить order, прежде чем мы сделаем последний шаг?

Оказывается, мы можем.

Предполагая, что правило округления 0,5 десятичное, приведенное выше условие if преобразуется в:

enter image description here

в результате чего

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]}'

дающий

>>> 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'

Ответ 21

ответ Sridhar Ratnakumar, обновленный до:

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)

и пример вывода:

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

Ответ 22

Это решение может также обратиться к вам, в зависимости от того, как работает ваш разум:

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'