Приложение формата размера Python (преобразование B в КБ, MB, GB, TB)

Я пытаюсь написать приложение для преобразования байтов в kb в mb в gb в tb. Вот что у меня есть до сих пор:

def size_format(b):
    if b < 1000:
              return '%i' % b + 'B'
    elif 1000 <= b < 1000000:
        return '%.1f' % float(b/1000) + 'KB'
    elif 1000000 <= b < 1000000000:
        return '%.1f' % float(b/1000000) + 'MB'
    elif 1000000000 <= b < 1000000000000:
        return '%.1f' % float(b/1000000000) + 'GB'
    elif 1000000000000 <= b:
        return '%.1f' % float(b/1000000000000) + 'TB'

Проблема в том, что при попытке приложения я получаю все после десятичного обнуления. Например, size_format(623) дает "623B", но с size_format(6200), вместо получения "6.2kb" я получаю "6.0kb". Любые идеи почему?

Ответ 1

Исправлена версия ответа Bryan_Rch:

def format_bytes(size):
    # 2**10 = 1024
    power = 2**10
    n = 0
    power_labels = {0 : '', 1: 'kilo', 2: 'mega', 3: 'giga', 4: 'tera'}
    while size > power:
        size /= power
        n += 1
    return size, power_labels[n]+'bytes'

Ответ 2

def humanbytes(B):
   'Return the given bytes as a human friendly KB, MB, GB, or TB string'
   B = float(B)
   KB = float(1024)
   MB = float(KB ** 2) # 1,048,576
   GB = float(KB ** 3) # 1,073,741,824
   TB = float(KB ** 4) # 1,099,511,627,776

   if B < KB:
      return '{0} {1}'.format(B,'Bytes' if 0 == B > 1 else 'Byte')
   elif KB <= B < MB:
      return '{0:.2f} KB'.format(B/KB)
   elif MB <= B < GB:
      return '{0:.2f} MB'.format(B/MB)
   elif GB <= B < TB:
      return '{0:.2f} GB'.format(B/GB)
   elif TB <= B:
      return '{0:.2f} TB'.format(B/TB)

tests = [1, 1024, 500000, 1048576, 50000000, 1073741824, 5000000000, 1099511627776, 5000000000000]

for t in tests: print '{0} == {1}'.format(t,humanbytes(t))

Вывод:

1 == 1.0 Byte
1024 == 1.00 KB
500000 == 488.28 KB
1048576 == 1.00 MB
50000000 == 47.68 MB
1073741824 == 1.00 GB
5000000000 == 4.66 GB
1099511627776 == 1.00 TB
5000000000000 == 4.55 TB

и для будущего меня здесь тоже в Perl:

sub humanbytes {
   my $B = shift;
   my $KB = 1024;
   my $MB = $KB ** 2; # 1,048,576
   my $GB = $KB ** 3; # 1,073,741,824
   my $TB = $KB ** 4; # 1,099,511,627,776

   if ($B < $KB) {
      return "$B " . (($B == 0 || $B > 1) ? 'Bytes' : 'Byte');
   } elsif ($B >= $KB && $B < $MB) {
      return sprintf('%0.02f',$B/$KB) . ' KB';
   } elsif ($B >= $MB && $B < $GB) {
      return sprintf('%0.02f',$B/$MB) . ' MB';
   } elsif ($B >= $GB && $B < $TB) {
      return sprintf('%0.02f',$B/$GB) . ' GB';
   } elsif ($B >= $TB) {
      return sprintf('%0.02f',$B/$TB) . ' TB';
   }
}

Ответ 3

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

def bytes_2_human_readable(number_of_bytes):
    if number_of_bytes < 0:
        raise ValueError("!!! number_of_bytes can't be smaller than 0 !!!")

    step_to_greater_unit = 1024.

    number_of_bytes = float(number_of_bytes)
    unit = 'bytes'

    if (number_of_bytes / step_to_greater_unit) >= 1:
        number_of_bytes /= step_to_greater_unit
        unit = 'KB'

    if (number_of_bytes / step_to_greater_unit) >= 1:
        number_of_bytes /= step_to_greater_unit
        unit = 'MB'

    if (number_of_bytes / step_to_greater_unit) >= 1:
        number_of_bytes /= step_to_greater_unit
        unit = 'GB'

    if (number_of_bytes / step_to_greater_unit) >= 1:
        number_of_bytes /= step_to_greater_unit
        unit = 'TB'

    precision = 1
    number_of_bytes = round(number_of_bytes, precision)

    return str(number_of_bytes) + ' ' + unit

Ответ 4

Еще одна версия в байтах человека, без циклов /if..else, в синтаксисе python3.

Тестовые числа, украденные из ответа @whereisalext.

Имейте в виду, это все еще эскиз, например, если числа достаточно велики, он проследит.

import math as m


MULTIPLES = ["B", "k{}B", "M{}B", "G{}B", "T{}B", "P{}B", "E{}B", "Z{}B", "Y{}B"]


def humanbytes(i, binary=False, precision=2):
    base = 1024 if binary else 1000
    multiple = m.trunc(m.log2(i) / m.log2(base))
    value = i / m.pow(base, multiple)
    suffix = MULTIPLES[multiple].format("i" if binary else "")
    return f"{value:.{precision}f} {suffix}"


if __name__ == "__main__":
    sizes = [
        1, 1024, 500000, 1048576, 50000000, 1073741824, 5000000000,
        1099511627776, 5000000000000]

    for i in sizes:
        print(f"{i} == {humanbytes(i)}, {humanbytes(i, binary=True)}")

Результаты:

1 == 1.00 B, 1.00 B
1024 == 1.02 kB, 1.00 kiB
500000 == 500.00 kB, 488.28 kiB
1048576 == 1.05 MB, 1.00 MiB
50000000 == 50.00 MB, 47.68 MiB
1073741824 == 1.07 GB, 1.00 GiB
5000000000 == 5.00 GB, 4.66 GiB
1099511627776 == 1.10 TB, 1.00 TiB
5000000000000 == 5.00 TB, 4.55 TiB

Ответ 5

Вместо изменения кода вы можете изменить поведение деления:

from __future__ import division

Это обеспечивает "истинное" разделение по "классическому" стилю, используемому Python 2.x. См. PEP 238 - Изменение оператора отдела для получения более подробной информации.

Это поведение по умолчанию в Python 3.x

Ответ 6

Это компактная версия, которая преобразует B (байты) в любой более высокий порядок, такой как МБ, ГБ, без использования большого количества if...else в python. Я использую побитовый, чтобы иметь дело с этим. Также это позволяет возвращать вывод с плавающей запятой, если вы активируете параметр return_output в функции как True:

import math

def bytes_conversion(number, return_float=False):

    def _conversion(number, return_float=False):

        length_number = int(math.log10(number))

        if return_float:

           length_number = int(math.log10(number))
           return length_number // 3, '%.2f' % (int(number)/(1 << (length_number//3) *10))

        return length_number // 3, int(number) >> (length_number//3) * 10

    unit_dict = {
        0: "B",  1: "kB",
        2: "MB", 3: "GB",
        4: "TB", 5: "PB",
        6: "EB"
    }

    if return_float:

        num_length, number = _conversion(number, return_float=return_float)

    else:
        num_length, number = _conversion(number)

    return "%s %s" % (number, unit_dict[num_length])

#Example usage:
#print(bytes_conversion(491266116, return_float=True))

Это только несколько моих постов в StackOverflow. Пожалуйста, дайте мне знать, если у меня есть какие-либо ошибки или нарушения.

Ответ 7

хорошая идея для меня:

def convert_bytes(num):
    """
    this function will convert bytes to MB.... GB... etc
    """
    step_unit = 1000.0 #1024 bad the size

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

Ответ 8

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

return '%.1f' % float(b)/1000 + 'KB'

или даже просто

return '%.1f' % b/1000.0 + 'KB'

Ответ 9

Я улучшил, по моему мнению, ответ @whereisalext, чтобы он имел несколько более общую функцию, которая не требует добавления большего числа, если в оператор будут добавлены еще единицы:

AVAILABLE_UNITS = ['bytes', 'KB', 'MB', 'GB', 'TB']

def get_amount_and_unit(byte_amount):
    for index, unit in enumerate(AVAILABLE_UNITS):
        lower_threshold = 0 if index == 0 else 1024 ** (index - 1)
        upper_threshold = 1024 ** index
        if lower_threshold <= byte_amount < upper_threshold:
            if lower_threshold == 0:
                return byte_amount, unit
            else:
                return byte_amount / lower_threshold, AVAILABLE_UNITS[index - 1]
    # Default to the maximum
    max_index = len(AVAILABLE_UNITS) - 1
    return byte_amount / (1024 ** max_index), AVAILABLE_UNITS[max_index]

Обратите внимание, что это немного отличается от алгоритма @whereisalext:

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

Ответ 10

Делайте float (b) перед делением, например, float(b)/1000 вместо float(b/1000), так как и b и 1000 являются целыми числами, b/1000 по-прежнему является целым числом без десятичной части.

Ответ 11

Здесь нужно преобразовать байты в кило, мега, тера.

#From bytes to kilo, mega, tera
def  get_(size):

    #2**10 = 1024
    power = 2**10
    n = 1
    Dic_powerN = {1:'kilobytes', 2:'megabytes', 3:'gigabytes', 4:'Terabytes'}

    if size <= power**2 :
        size /=  power
        return size, Dic_powerN[n]

    else: 
        while size   >  power :
            n  += 1
            size /=  power**n

        return size, Dic_powerN[n]

Ответ 12

Выход без десятичных разрядов:

>>> format_file_size(12345678)
'11 MiB, 792 KiB, 334 bytes'

format_file_size(
    def format_file_size(fsize):
        result = []
        units = {s: u for s, u in zip(reversed([2 ** n for n in range(0, 40, 10)]), ['GiB', 'MiB', 'KiB', 'bytes'])}
        for s, u in units.items():
            t = fsize // s
            if t > 0:
                result.append('{} {}'.format(t, u))
            fsize = fsize % s
        return ', '.join(result) or '0 bytes'

Ответ 13

Очень простое решение:

SIZE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']

def get_readable_file_size(size_in_bytes):
    index = 0
    while size_in_bytes >= 1024:
        size_in_bytes /= 1024
        index += 1
    try:
        return f'{size_in_bytes} {SIZE_UNITS[index]}'
    except IndexError:
        return 'File too large'