programing

사람이 읽을 수 있는 파일 크기 버전을 가져오시겠습니까?

coolbiz 2022. 11. 3. 23:04
반응형

사람이 읽을 수 있는 파일 크기 버전을 가져오시겠습니까?

바이트 크기에서 사람이 읽을 수 있는 크기를 반환하는 함수:

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

이거 어떻게 하는 거야?

위의 "라이브러리가 필요 없는 너무 작은 작업" 문제를 간단한 구현으로 해결합니다(f-string 사용, 그래서 Python 3.6+ 사용:

def sizeof_fmt(num, suffix="B"):
    for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]:
        if abs(num) < 1024.0:
            return f"{num:3.1f}{unit}{suffix}"
        num /= 1024.0
    return f"{num:.1f}Yi{suffix}"

지원:

  • 현재 알려진 모든 이진 접두사
  • 음수와 양수
  • 1000 Yobibytes보다 큰 수
  • 임의의 단위(기비트 단위로 셀 수 있습니다!)

예를 들어:

>>> sizeof_fmt(168963795964)
'157.4GiB'

Fred Cirera 지음

필요한 기능을 모두 갖춘 라이브러리는 입니다.humanize.naturalsize()당신이 찾는 모든 걸 다 해내는 것 같아요

예시( 「」 「」 )python 3.10)

import humanize

disk_sizes_list = [1, 100, 999, 1000,1024, 2000,2048, 3000, 9999, 10000, 2048000000, 9990000000, 9000000000000000000000]
for size in disk_sizes_list:
    natural_size = humanize.naturalsize(size)
    binary_size = humanize.naturalsize(size, binary=True)
    print(f" {natural_size} \t| {binary_size}\t|{size}")

산출량

 1 Byte     | 1 Byte    |1
 100 Bytes  | 100 Bytes |100
 999 Bytes  | 999 Bytes |999
 1.0 kB     | 1000 Bytes    |1000
 1.0 kB     | 1.0 KiB   |1024
 2.0 kB     | 2.0 KiB   |2000
 2.0 kB     | 2.0 KiB   |2048
 3.0 kB     | 2.9 KiB   |3000
 10.0 kB    | 9.8 KiB   |9999
 10.0 kB    | 9.8 KiB   |10000
 2.0 GB     | 1.9 GiB   |2048000000
 10.0 GB    | 9.3 GiB   |9990000000
 9.0 ZB     | 7.6 ZiB   |9000000000000000000000

다음은 Python 3.6+에서 작동하며, 여기서 가장 이해하기 쉬운 답변이며, 사용하는 소수 자릿수를 맞춤화할 수 있습니다.

def human_readable_size(size, decimal_places=2):
    for unit in ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB']:
        if size < 1024.0 or unit == 'PiB':
            break
        size /= 1024.0
    return f"{size:.{decimal_places}f} {unit}"

항상 그 중 한 명이 있을 거야오늘은 나야여기 한 줄의 선이 있습니다. 함수 시그니처를 세면 두 줄입니다.

def human_size(bytes, units=[' bytes','KB','MB','GB','TB', 'PB', 'EB']):
    """ Returns a human readable string representation 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

엑사바이트보다 더 큰 사이즈가 필요한 경우, 조금 더 큰 사이즈입니다.

def human_size(bytes, units=[' bytes','KB','MB','GB','TB', 'PB', 'EB']):
    return str(bytes) + units[0] if bytes < 1024 else human_size(bytes>>10, units[1:]) if units[1:] else f'{bytes>>10}ZB'

여기 제 버전이 있습니다.for-loop은 사용하지 않습니다.이것은 일정한 복잡도 O(1)를 가지며 이론적으로는 for-loop을 사용하는 답보다 효율적입니다.

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]

루프를 해 냈습니다.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])

하지만 가독성 때문에 조음적이지 않은 것으로 여겨질 수 있다.

설치된 Django를 사용하는 경우 filesizeformat을 사용할 수도 있습니다.

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

=>

"1.0 GB"

"인간화"를 사용해야 합니다.

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

그러한 라이브러리 중 하나는 filesize이다.

>>> from hurry.filesize import alternative
>>> size(1, system=alternative)
'1 byte'
>>> size(10, system=alternative)
'10 bytes'
>>> size(1024, system=alternative)
'1 KB'

1000 또는 키비바이트의 검정력을 사용하는 것이 보다 표준적인 방법입니다.

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(대문자) 접미사를 사용하여 수천 개를 인쇄하는 라이브러리를 신뢰하지 마십시오.

Human Friendly 프로젝트가 이를 지원합니다.

import humanfriendly
humanfriendly.format_size(1024)

1KB입니다.
예시는 여기에서 찾을있습니다.

filesize() 대신 제공된 스니펫에 리핑(Riffing)을 붙입니다.여기에는 사용된 프리픽스에 따라 다양한 정밀도를 나타내는 스니펫이 있습니다.일부 토막처럼 간결하지는 않지만, 결과는 마음에 듭니다.

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)

이는 거의 모든 상황에서 필요한 작업을 수행할 수 있으며, 옵션 인수를 사용하여 커스터마이즈할 수 있으며, 보다시피 거의 자체 문서화되어 있습니다.

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'

지금까지의 모든 답변에서 인용하면, 이것에 대한 나의 견해는 이렇다.파일 크기를 바이트 단위로 정수로 저장하는 개체입니다.그러나 오브젝트를 인쇄하려고 하면 자동으로 사람이 읽을 수 있는 버전이 나타납니다.

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)

템플릿 태그가 있습니다.filesizeformat:

의 형식을 '아까보다'와 같이 지정합니다.human-readable13KB, 4.1MB', '102')

예를 들어 다음과 같습니다.

{{ value|filesizeformat }}

값이 123456789일 경우 출력은 117.7MB가 됩니다.

상세정보 : https://docs.djangoproject.com/en/1.10/ref/templates/builtins/ #filesize format

저는 Senderle의 10진수 버전이 고정 정밀도가 좋기 때문에 위의 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 + ['', 'K', 'M', 'G', 'T', 'P'][illion]
    return (format_str % (x * 1.0 / (1024 ** illion))).lstrip('0')

심플한 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])

후드 아래에서 작동하는 방법은 다음과 같습니다.

  1. 로그(파일 크기)를 계산합니다2.
  2. 바이트의 경우 (예를 들어 인 경우 가장 (5000 바이트)입니다Kb은 X )) X KiB로 해야 합니다.
  3. file_size/value_of_closest_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])

예:

>>> 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의 배수입니다.자세한 것은 이쪽

아래에 게재된 솔루션 중 가장 성능이 뛰어나거나 가장 짧은 솔루션은 결코 아닙니다.대신, 다른 많은 답변들이 놓치고 있는 한 가지 특정 문제에 초점을 맞추고 있습니다.

, ,, 음, 음, 음, 음과 같은 이 되는 입니다.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조건은 다음과 같이 변환됩니다.

여기에 이미지 설명 입력

결과적으로

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'

사람이 읽을 수 있는 형태로 파일 크기를 가져오기 위해 다음 함수를 만들었습니다.

import os

def get_size(path):
    size = os.path.getsize(path)
    if size < 1024:
        return f"{size} bytes"
    elif size < 1024*1024:
        return f"{round(size/1024, 2)} KB"
    elif size < 1024*1024*1024:
        return f"{round(size/(1024*1024), 2)} MB"
    elif size < 1024*1024*1024*1024:
        return f"{round(size/(1024*1024*1024), 2)} GB"
>>> get_size("a.txt")
1.4KB
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

이 기능은 대부분의 프로젝트에서 매우 편리한 라이브러리인 Boltons에서 사용할 수 있는 경우 사용할 수 있습니다.

>>> bytes2human(128991)
'126K'
>>> bytes2human(100001221)
'95M'
>>> bytes2human(0, 2)
'0.00B'

다음은 다음 중 하나를 사용하는 옵션:while:

def number_format(n):
   n2, n3 = n, 0
   while n2 >= 1e3:
      n2 /= 1e3
      n3 += 1
   return '%.3f' % n2 + ('', ' k', ' M', ' G')[n3]

s = number_format(9012345678)
print(s == '9.012 G')

https://docs.python.org/reference/compound_stmts.html#while

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

또, 이 솔루션은, 고객의 마음가짐의 동작에 따라서는, 다음과 같이 어필할 수도 있습니다.

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'

언급URL : https://stackoverflow.com/questions/1094841/get-human-readable-version-of-file-size

반응형