مكتبة قابلة لإعادة الاستخدام للحصول على نسخة مقروءة بشرية من حجم الملف؟

StackOverflow https://stackoverflow.com/questions/1094841

  •  11-09-2019
  •  | 
  •  

سؤال

هناك مقتطفات مختلفة على الويب التي من شأنها أن تعطيك وظيفة لإعادة الحجم القابل للقراءة من بايت الحجم:

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

ولكن هل هناك مكتبة بيثون توفر هذا؟

هل كانت مفيدة؟

المحلول

معالجة المشكلة أعلاه "مهمة صغيرة جدا لتطلب مكتبة" من خلال تطبيق مباشر:

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
  • وحدات تعسفية (ربما ترغب في الاعتماد في Gibibits!)

مثال:

>>> sizeof_fmt(168963795964)
'157.4GiB'

بواسطة فريد cirera.

نصائح أخرى

مكتبة لديها كل الوظائف التي يبدو أنها تبحث عنها هي humanize. humanize.naturalsize() يبدو أن تفعل كل ما تبحث عنه.

ها هي الخاص بي. لا يستخدم لحلقة واحدة. لديها تعقيد ثابت، س (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])

يمكن اعتباره بشكل غير صحيح لقراءة منهيته، رغم ذلك :)

يجب أن يكون هناك دائما أحد هؤلاء الرجال. حسنا اليوم هو لي. إليك محلول بطانة واحدا - أو سطرين إذا عدت توقيع الوظيفة.

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

إذا كنت تستخدم Django مثبتا، يمكنك أيضا محاولة filesizeformat.:

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

=>

"1.0 GB"

الأعمال التالية في Python 3.6+، هي، في رأيي، أسهل لفهم الإجابة هنا، وتتيح لك تخصيص كمية الأماكن العشرية المستخدمة.

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

واحدة من هذه المكتبة هي hurry.files.ize..

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

باستخدام أي من صلاحيات 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)

PS لا تثق أبدا في مكتبة يطبع الآلاف مع لاحقة K (الأحرف الكبيرة) :)

يتم توفير Riffing On The Snippet كبديل للمجرد ()، إليك مقتطف يعطي أرقاما بدقة متفاوتة بناء على البادئة المستخدمة. انها ليست مقتومة مثل بعض القصاصات، ولكني أحب النتائج.

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)

أنا أحب الدقة الثابتة لل الإصدار العشري ل Senderle, ، إذن إليك نوع من الهجين من ذلك مع إجابة 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')

يجب عليك استخدام "أنسنة".

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

مشروع الإنسان يساعد مع هذا.

import humanfriendly
humanfriendly.format_size(1024)

سيعطي الرمز أعلاه 1 كيلو بايت كإجابة.
أمثلة يمكن العثور عليها هنا.

Diveintopython3 أيضا محادثات حول هذه الوظيفة.

django الحديثة لديها علامة قالب الذات filesizeformat:

تنسيق القيمة مثل human-readable حجم الملف (أي '13 كيلو بايت '،' 4.1 ميغابايت '،' 102 بايت '، إلخ).

علي سبيل المثال:

{{ value|filesizeformat }}

إذا كانت القيمة 123456789، فسيكون الإخراج 117.7 ميغابايت.

مزيد من المعلومات: https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#FilesizeFormat.

ماذا عن بطانة بسيطة 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. يقسمها بنسبة 10 للحصول على أقرب وحدة. (على سبيل المثال إذا كان الحجم هو 5000 بايت، فإن أقرب وحدة هي Kb, ، لذلك يجب أن تكون الجواب X KIB)
  3. عائدات file_size/value_of_closest_unit جنبا إلى جنب مع وحدة.

ومع ذلك، لا يعمل إذا كان filesize هو 0 أو السلبي (لأن السجل غير محدد لمدة 0 وحدوث أرقام). يمكنك إضافة شيكات إضافية لهم:

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 بايت، في حين أن كيب يعني 1024 بايت. KB، MB، GB كلها مضاعفات 1000، في حين أن KIB، MIB، GIB وغيرها كلها مضاعفات 1024. المزيد عن ذلك هنا

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

ما أنت على وشك أن تجد أدناه ليس بأي حال من الأكدت أداء أو أقصر حلا بين تلك المنشورة بالفعل. بدلا من ذلك، يركز على مشكلة واحدة معينة أن العديد من الإجابات الأخرى تفوت.

وهي القضية عند إدخال مثل 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'

أشير 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'
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top