Вопрос по filesize, python, code-snippets – Многоразовая библиотека, чтобы получить удобочитаемую версию размера файла?

182

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

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

Но есть ли библиотека Python, которая обеспечивает это?

Я думаю, что это подпадает под заголовок «слишком маленькая задача, чтобы требовать библиотеку». Если вы посмотрите на источник для hurry.filesize, там только одна функция с дюжиной строк кода. И даже это может быть уплотнено. Ben Blank
Преимущество использования библиотеки состоит в том, что она обычно тестируется (содержит тесты, которые можно запускать в случае, если в результате одного редактирования возникает ошибка). Если вы добавите тесты, то это уже не «дюжина строк кода». :-) Sridhar Ratnakumar

Ваш Ответ

20   ответов
9

Одна такая библиотека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'
Также обратите внимание, что очень просто просто извлечь код из файла hurry.filesize и поместить его непосредственно в свой собственный код, если вы имеете дело с системами зависимостей и тому подобным. Оно примерно такое же короткое, как и фрагменты, которые люди предоставляют здесь.
@SridharRatnakumar, чтобы решить проблему чрезмерного приближения несколько разумно, пожалуйста, смотрите мои математическиеhack, Можно ли еще улучшить этот подход?
Однако эта библиотека не очень настраиваема. & GT; & GT; & GT; из размера спешите импортировать & gt; & gt; & gt; размер (1031053) & gt; & gt; & gt; размер (3033053) "2М"; Я ожидаю, что это покажет, например, «2.4M». или "2423К"; .. вместо явно аппроксимированного '2M'. Sridhar Ratnakumar
2

DiveIntoPython3 такжепереговоры об этой функции.

0

обращаться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
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)

Постскриптум Никогда не доверяйте библиотеке, которая печатает тысячи с суффиксом K (заглавными буквами) :)

P.S. Never trust a library that prints thousands with the K (uppercase) suffix :) Почему бы и нет? Код мог бы быть совершенно надежным, и автор просто не рассматривал случай для килограмма. Кажется, довольно глупо автоматически отклонять любой код на основе вашего правила ...
1

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

Форматирует значение какhuman-readable размер файла (то есть «13 КБ», «4,1 МБ», «102 байта» и т. д.).

Например:

{{ value|filesizeformat }}

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

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

7

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

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 - упражнение для читателя. Помните, что этоoutput это довольно.

Update:

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

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

Хотя я знаю, что этот вопрос древний, недавно я придумал версию, которая позволяет избежать циклов, используя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])

Впрочем, вполне может считаться непифоническим из-за его читабельности :)

Хотя мне нравится вещь log2, вы должны обрабатывать размер == 0!
@monsto правда, добавил :)
К вашему сведению: некоторым может понадобитьсяimport math там наверху.
Вам нужно завернуть либоsize или же(1 << (order * 10) вfloat() в последней строке (для python 2).
Любите, как это компактно! Спасибо, что поделились.
6

Отходя от решения Шридхара Ратнакумара, мне это нравится немного лучше. Работает в 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}"
2

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

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

Reference:

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

6

Перейдя к фрагменту, предоставленному в качестве альтернативы 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)
4

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

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)
Было бы еще лучше с__format__.
3

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

from math import log
def human_readable_bytes(x):
    # hybrid of https://stackoverflow.com/a/10171475/2595465
    #      with https://stackoverflow.com/a/5414105/2595465
    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 + ['&ap,os;, 'K', 'M', 'G', 'T', 'P'][illion]
    return (format_str % (x * 1.0 / (1024 ** illion))).lstrip('0')
2

Как насчет простого 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])

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

Calculates log2(filesize) Divides it by 10 to get the closest unit. (eg if size is 5000 bytes, the closest unit is Kb, so the answer should be X KiB) Returns file_size/value_of_closest_unit along with unit.

Однако он не работает, если размер файла равен 0 или отрицателен (поскольку журнал не определен для 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])

Examples:

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

NOTE - Есть разница между КБ и КиБ. KB означает 1000 байтов, тогда как KiB означает 1024 байта. КБ, МБ, ГБ - все, кратные 1000, тогда как КиБ, МиБ, ГиБ и т. Д. - кратны 1024.Подробнее об этом здесь

9

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

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

& # X2003;

>>> human_size(123)
123 bytes
>>> human_size(123456789)
117GB
К вашему сведению, вывод всегда будет округляться в меньшую сторону.
Не лучше ли назначить список по умолчанию для блоков внутри метода, чтобы избежать использования списка в качестве аргумента по умолчанию? (и используяunits=None вместо)
@ImanolEizaguirre В соответствии с лучшими практиками было бы неплохо сделать то, что вы предложили, чтобы вы случайно не вносили ошибки в программу. Однако эта функция в том виде, в каком она написана, безопасна, поскольку списком единиц никогда не манипулируют. Если бы им манипулировали, изменения были бы постоянными, и любые последующие вызовы функций получали бы управляемую версию списка в качестве аргумента по умолчанию для аргумента единиц.
3

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

import humanfriendly
humanfriendly.format_size(1024)

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

12

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

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

=>

"1.0 GB"
418

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

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)

Поддержка:

all currently known binary prefixes negative and positive numbers numbers larger than 1000 Yobibytes arbitrary units (maybe you like to count in Gibibits!)

Пример:

>>> sizeof_fmt(168963795964)
'157.4GiB'

отФред Сирера

прохладно! Мне это очень понравилось, я преобразовал его в Go lang:play.golang.org/p/68w_QCsE4F
Если вы хотите увеличить точность десятичного компонента, измените1 в строках 4 и 6 с той точностью, которую вы хотите.
конечно, было бы неплохо, если бы вся эта итерация для этой "слишком маленькой задачи" были захвачены и помещены в библиотеку с тестами.
@ MD004 Все наоборот. Префиксы определены так, что 1 КБ = 1000 В и 1 КБ = 1024 В.
просто мысль, но для любого (?) суффикса, кромеB (т.е. для блоков, отличных от байтов), вы хотите, чтобы коэффициент был1000.0 скорее, чем1024.0 нет?
30

Вот моя версия. Он не использует цикл 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]
Вероятно, следует добавить запятую для форматирования, так1000 покажет как1,000 bytes.
Томас Минор, ты совершенно прав.
Ответы с циклами for также O (1), потому что циклы for ограничены - их время вычислений не масштабируется с размером входных данных (у нас нет неограниченных префиксов SI).
Мой код наверняка может быть более оптимизирован. Однако я хотел показать, что эту задачу можно решить с постоянной сложностью.
Пока вы говорите об оптимизации такого короткого кода, почему бы не использовать if / elif / else? Последняя проверка num == 1 не требуется, если вы не ожидаете отрицательный размер файла. В остальном: хорошая работа, мне нравится эта версия.
1
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
89

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

Некоторые примеры использования данных из ОП:humanize.naturalsize(2048) # => '2.0 kB' , humanize.naturalsize(2048, binary=True) # => '2.0 KiB' humanize.naturalsize(2048, gnu=True) # => '2.0K'

Похожие вопросы