ไลบรารีที่ใช้ซ้ำได้เพื่อให้ได้ขนาดไฟล์ที่มนุษย์อ่านได้


238

มีตัวอย่างบางส่วนบนเว็บที่จะให้ฟังก์ชันคืนค่าขนาดที่มนุษย์สามารถอ่านได้จากขนาดไบต์:

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

แต่มีห้องสมุด Python ที่ให้บริการนี้หรือไม่


2
ฉันคิดว่าสิ่งนี้อยู่ภายใต้หัวข้อของ "งานที่ต้องมีห้องสมุดเล็กเกินไป" หากคุณดูที่แหล่งที่มาของ hurry.filesize มีเพียงฟังก์ชั่นเดียวที่มีโค้ดเป็นโหล และแม้แต่ที่สามารถอัดได้
Ben Blank

8
ข้อดีของการใช้ไลบรารี่คือโดยปกติแล้วจะมีการทดสอบ (มีการทดสอบที่สามารถทำงานได้ในกรณีที่การแก้ไขของคนแนะนำบั๊ก) หากคุณเพิ่มการทดสอบก็ไม่ได้เป็น 'เส้นรหัสโหล' อีกต่อไป
Sridhar Ratnakumar

จำนวนการประดิษฐ์วงล้อใหม่ในชุมชนหลามนั้นบ้าและไร้สาระ เพียงแค่ ls -h /path/to/file.ext จะทำงาน ต้องบอกว่าคำตอบที่ยอมรับคือการทำงานที่ดี Kudo
Edward Aung

คำตอบ:


522

การกล่าวถึงปัญหา "งานที่ต้องใช้ห้องสมุดน้อยเกินไป" โดยการใช้งานที่ไม่ซับซ้อน:

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)

สนับสนุน:

  • คำนำหน้าไบนารีที่รู้จักทั้งหมดในปัจจุบัน
  • ตัวเลขติดลบและบวก
  • ตัวเลขที่มีขนาดใหญ่กว่า 1,000 Yobibytes
  • หน่วยตามอำเภอใจ (บางทีคุณอาจต้องการนับใน Gibibits!)

ตัวอย่าง:

>>> sizeof_fmt(168963795964)
'157.4GiB'

โดยFred Cirera


4
ควรมีช่องว่างระหว่างหมายเลขและหน่วย หากคุณกำลังเอาท์พุท html หรือลาเท็กซ์มันควรจะเป็นพื้นที่ที่ไม่ทำลาย
josch

3
แค่ความคิด แต่สำหรับ (?) ส่วนต่อท้ายอื่น ๆ ที่ไม่ใช่B(เช่นสำหรับหน่วยอื่นที่ไม่ใช่ไบต์) คุณต้องการให้ตัวประกอบเป็น1000.0มากกว่าไม่ใช่1024.0หรือไม่?
Anentropic

5
หากคุณต้องการเพิ่มความแม่นยำของส่วนประกอบทศนิยมให้เปลี่ยน1on 4 และ 6 เป็นความแม่นยำที่คุณต้องการ
Matthew G

44
แน่นอนว่าจะดีถ้าการวนซ้ำทั้งหมดใน "งานเล็กเกินไป" นี้ถูกจับและใส่เข้าไปในห้องสมุดด้วยการทดสอบ
ยอมรับ

6
@ MD004 เป็นวิธีอื่น ๆ คำนำหน้ามีการกำหนดเช่นนั้น 1 KB = 1,000 B และ 1 KiB = 1024 B
สิงหาคม

116

humanizeห้องสมุดที่มีทุกฟังก์ชันการทำงานที่ดูเหมือนว่าคุณกำลังมองหาอยู่ humanize.naturalsize()ดูเหมือนว่าจะทำทุกสิ่งที่คุณกำลังมองหา


9
ตัวอย่างบางส่วนโดยใช้ข้อมูลจาก OP นี้: humanize.naturalsize(2048) # => '2.0 kB' ,humanize.naturalsize(2048, binary=True) # => '2.0 KiB' humanize.naturalsize(2048, gnu=True) # => '2.0K'
RubenLaguna

32

นี่คือรุ่นของฉัน มันไม่ได้ใช้สำหรับวง มันมีความซับซ้อนอย่างต่อเนื่อง 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]

2
ในขณะที่คุณพูดถึงการเพิ่มประสิทธิภาพรหัสสั้น ๆ ทำไมไม่ใช้ถ้า / elif / else? การตรวจสอบครั้งล่าสุด num == 1 ไม่จำเป็นเว้นแต่คุณจะคาดหวังว่าขนาดไฟล์เชิงลบ มิฉะนั้น: งานที่ดีฉันชอบรุ่นนี้
เท็ด

2
รหัสของฉันสามารถเพิ่มประสิทธิภาพได้แน่นอนยิ่งขึ้น อย่างไรก็ตามจุดของฉันคือการแสดงให้เห็นว่างานนี้สามารถแก้ไขได้ด้วยความซับซ้อนคงที่
joctee

37
คำตอบสำหรับลูปนั้นก็คือ O (1) เพราะสำหรับลูปนั้นถูก จำกัด ขอบเขต - เวลาการคำนวณของพวกเขาไม่ได้ปรับขนาดตามขนาดของอินพุต (เราไม่มีคำนำหน้า SI ที่ไม่มีขอบเขต)
Thomas Minor

1
อาจจะเพิ่มเครื่องหมายจุลภาคสำหรับการจัดรูปแบบเพื่อจะแสดงเป็น1000 1,000 bytes
iTayb

3
โปรดทราบว่าเมื่อใช้ Python 3 zip จะส่งคืนตัววนซ้ำดังนั้นคุณต้องล้อมด้วย list () unit_list = list(zip(['bytes', 'kB', 'MB', 'GB', 'TB', 'PB'], [0, 0, 1, 2, 2, 2]))
donarb

30

งานต่อไปนี้ใน 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}"

26

ในขณะที่ฉันรู้ว่าคำถามนี้โบราณฉันเพิ่งมากับรุ่นที่หลีกเลี่ยงลูปใช้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])

อาจถือได้ว่าเป็นเสียงที่ไม่ไพเราะเพราะมันอ่านง่าย :)


1
ในขณะที่ฉันชอบสิ่ง log2 คุณควรจัดการ size == 0!
Marti Nito

คุณจำเป็นต้องห่ออย่างใดอย่างหนึ่งsizeหรือ(1 << (order * 10)ในfloat()บรรทัดสุดท้าย (สำหรับ python 2)
ฮาร์วีย์

FYI: บางคนอาจต้องการที่import mathนั่น
monsto

@monsto จริงเพิ่ม :)
akaIDIOT

รักขนาดนี้กะทัดรัดแค่ไหน! ขอบคุณสำหรับการแชร์.
John Crawford

17

จะต้องเป็นหนึ่งในพวกนั้นเสมอ วันนี้ฉันเอง นี่คือวิธีแก้ปัญหาแบบหนึ่งซับหรือสองบรรทัดถ้าคุณนับลายเซ็นของฟังก์ชัน

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

1
FYI ผลลัพธ์จะถูกปัดเศษลงเสมอ
wp-overwatch.com

1
จะดีกว่าหรือไม่ที่จะกำหนดรายการเริ่มต้นสำหรับหน่วยภายในวิธีการเพื่อหลีกเลี่ยงการใช้รายการเป็นอาร์กิวเมนต์เริ่มต้น (และใช้units=Noneแทน)
Imanol

3
@ImanolEizaguirre แนวทางปฏิบัติที่ดีที่สุดจะระบุว่าเป็นความคิดที่ดีที่จะทำตามที่คุณแนะนำดังนั้นคุณจึงไม่ได้แนะนำบั๊กในโปรแกรม อย่างไรก็ตามฟังก์ชั่นนี้ตามที่เขียนมีความปลอดภัยเพราะรายการหน่วยไม่เคยถูกจัดการ หากมีการจัดการการเปลี่ยนแปลงจะเป็นแบบถาวรและการเรียกใช้ฟังก์ชันที่ตามมาจะได้รับเวอร์ชันที่ถูกจัดการของรายการเป็นอาร์กิวเมนต์เริ่มต้นสำหรับอาร์กิวเมนต์หน่วย
wp-overwatch.com

สำหรับ Python 3 หากคุณต้องการจุดทศนิยมให้ใช้แทน: `` `def human_size (fsize, units = ['bytes', 'KB', 'MB', 'MB', 'GB', 'TB', 'PB', 'EB']): return "{: .2f} {}". รูปแบบ (float (fsize), หน่วย [0]) ถ้า fsize <1024 human_size อื่น (fsize / 1024, หน่วย [1:]) `` `
Omer

15

หากคุณใช้ Django ติดตั้งอยู่คุณสามารถลองเปลี่ยนขนาดไฟล์ได้ :

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

=>

"1.0 GB"

1
ข้อเสียอย่างหนึ่งสำหรับฉันคือการใช้ GB แทน GiB แม้ว่ามันจะหารด้วย 1024
Pepedou

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'

3
อย่างไรก็ตามไลบรารีนี้ไม่สามารถปรับแต่งได้มาก >>> จาก hurry.filesize ขนาดการนำเข้า >>> ขนาด (1031053) >>> ขนาด (3033053) '2M' ฉันคาดว่ามันจะแสดงเช่น '2.4M' หรือ '2423K' .. แทนการประมาณโจ่งแจ้ง ' 2M'
Sridhar Ratnakumar

โปรดทราบว่ามันง่ายมากที่จะหยิบโค้ดออกมาจาก hurry.filesize และวางมันลงในโค้ดของคุณโดยตรงหากคุณกำลังติดต่อกับระบบการพึ่งพาและสิ่งที่คล้ายกัน สั้นพอ ๆ กับตัวอย่างที่ผู้คนให้ไว้ที่นี่
mlissner

@SridharRatnakumar เพื่อแก้ไขปัญหามากกว่าประมาณค่อนข้างชาญฉลาดโปรดดูทางคณิตศาสตร์ของฉันสับ สามารถปรับปรุงวิธีการต่อไปได้หรือไม่?
คิวเมนตัส

9

การใช้กำลังทั้ง 1,000 หรือ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 (ตัวพิมพ์ใหญ่) :)


P.S. Never trust a library that prints thousands with the K (uppercase) suffix :)ทำไมจะไม่ล่ะ? รหัสอาจจะฟังดูสมบูรณ์แบบและผู้แต่งไม่ได้พิจารณาตัวเครื่องเป็นกิโล ดูเหมือนว่าชาวเอเชียจะยกเลิกรหัสใด ๆ โดยอัตโนมัติตามกฎของคุณ ...
ดักลาสกาสเคล

7

สิ่งนี้จะทำสิ่งที่คุณต้องการในเกือบทุกสถานการณ์สามารถปรับแต่งได้ด้วยอาร์กิวเมนต์ที่เป็นตัวเลือกและอย่างที่คุณเห็นมันเป็นเอกสารที่ค่อนข้างดี:

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'


6

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)

6

โครงการ HumanFriendly ช่วยด้วยนี้

import humanfriendly
humanfriendly.format_size(1024)

รหัสด้านบนจะให้ 1KB เป็นคำตอบ
ตัวอย่างสามารถพบได้ที่นี่


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)

3

ฉันชอบความแม่นยำที่แน่นอนของเวอร์ชันทศนิยมของผู้ส่งดังนั้นนี่เป็นไฮบริดของคำตอบของโจcteeด้านบน (คุณรู้หรือไม่ว่าคุณสามารถบันทึกด้วยฐานที่ไม่ใช่จำนวนเต็มได้):

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

ทันสมัย ​​Django มีแท็กแม่แบบด้วยตนเอง filesizeformat :

จัดรูปแบบค่าเช่น a human-readableขนาดไฟล์ (เช่น '13 KB ',' 4.1 MB ',' 102 bytes 'ฯลฯ )

ตัวอย่างเช่น:

{{ value|filesizeformat }}

หากค่าเป็น 123456789 เอาต์พุตจะเป็น 117.7 MB

ข้อมูลเพิ่มเติม: https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#filesizeformat


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

นี่คือวิธีการทำงานภายใต้ประทุน:

  1. คำนวณ log 2 (ขนาดไฟล์)
  2. หารด้วย 10 เพื่อให้ได้หน่วยที่ใกล้ที่สุด (เช่นถ้าขนาด 5000 ไบต์หน่วยที่ใกล้เคียงที่สุดคือKbดังนั้นคำตอบควรเป็น 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 หมายถึง 1,000 ไบต์ในขณะที่ KiB หมายถึง 1024 ไบต์ KB, MB, GB เป็นทวีคูณทั้งหมด 1,000 ในขณะที่ KiB, MiB, GiB และอื่น ๆ เป็นทวีคูณของ 1024 ทั้งหมดเพิ่มเติมที่นี่


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

1

สิ่งที่คุณกำลังจะค้นหาด้านล่างไม่ได้หมายความว่าเป็นทางออกที่มีประสิทธิภาพหรือสั้นที่สุดในบรรดาโพสต์ที่ได้โพสต์ไปแล้ว แต่จะมุ่งเน้นไปที่ประเด็นเฉพาะหนึ่งที่หลาย ๆ คำตอบไม่ได้รับ

เป็นกรณีเมื่อป้อนชอบ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'

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

0

วิธีแก้ปัญหานี้อาจดึงดูดคุณได้เช่นกันขึ้นอยู่กับว่าจิตใจของคุณทำงานอย่างไร:

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'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.