Có nhiều đoạn mã khác nhau trên web sẽ cung cấp cho bạn một hàm để trả về kích thước có thể đọc được của con người từ kích thước byte:
>>> human_readable(2048)
'2 kilobytes'
>>>
Nhưng có một thư viện Python cung cấp cái này không?
Có nhiều đoạn mã khác nhau trên web sẽ cung cấp cho bạn một hàm để trả về kích thước có thể đọc được của con người từ kích thước byte:
>>> human_readable(2048)
'2 kilobytes'
>>>
Nhưng có một thư viện Python cung cấp cái này không?
Câu trả lời:
Giải quyết vấn đề "quá nhỏ để yêu cầu thư viện" ở trên bằng cách thực hiện đơn giản:
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)
Hỗ trợ:
Thí dụ:
>>> sizeof_fmt(168963795964)
'157.4GiB'
bởi Fred Cirera
B
(ví dụ: đối với các đơn vị không phải là byte), bạn muốn yếu tố này 1000.0
chứ không phải là 1024.0
không?
1
dòng 4 và 6 thành bất kỳ độ chính xác nào bạn muốn.
Một thư viện có tất cả các chức năng mà dường như bạn đang tìm kiếm humanize
. humanize.naturalsize()
dường như làm mọi thứ bạn đang tìm kiếm.
humanize.naturalsize(2048) # => '2.0 kB'
,humanize.naturalsize(2048, binary=True) # => '2.0 KiB'
humanize.naturalsize(2048, gnu=True) # => '2.0K'
Đây là phiên bản của tôi. Nó không sử dụng vòng lặp for. Nó có độ phức tạp không đổi, O ( 1 ) và về mặt lý thuyết hiệu quả hơn các câu trả lời ở đây sử dụng vòng lặp 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'
Để làm rõ hơn những gì đang diễn ra, chúng ta có thể bỏ qua mã cho định dạng chuỗi. Dưới đây là các dòng thực sự làm việc:
exponent = int(log(num, 1024))
quotient = num / 1024**exponent
unit_list[exponent]
1000
sẽ hiển thị dưới dạng 1,000 bytes
.
unit_list = list(zip(['bytes', 'kB', 'MB', 'GB', 'TB', 'PB'], [0, 0, 1, 2, 2, 2]))
Theo tôi, các công việc sau đây trong Python 3.6+ là câu trả lời dễ hiểu nhất ở đây và cho phép bạn tùy chỉnh số lượng vị trí thập phân được sử dụng.
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}"
Trong khi tôi biết câu hỏi này là cổ xưa, gần đây tôi đã đưa ra một phiên bản tránh các vòng lặp, sử dụng log2
để xác định thứ tự kích thước tăng gấp đôi khi thay đổi và một chỉ mục vào danh sách hậu tố:
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])
Mặc dù cũng có thể được coi là unpythonic cho khả năng đọc của nó :)
size
hoặc (1 << (order * 10)
trong float()
dòng cuối cùng (đối với python 2).
import math
lên đó.
Luôn luôn có một trong những kẻ đó. Chà, hôm nay là tôi. Đây là giải pháp một lớp lót - hoặc hai dòng nếu bạn đếm chữ ký hàm.
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
units=None
thay thế)
Nếu bạn đang sử dụng Django, bạn cũng có thể thử filesizeformat :
from django.template.defaultfilters import filesizeformat
filesizeformat(1073741824)
=>
"1.0 GB"
Một thư viện như vậy là rush.filesize .
>>> from hurry.filesize import alternative
>>> size(1, system=alternative)
'1 byte'
>>> size(10, system=alternative)
'10 bytes'
>>> size(1024, system=alternative)
'1 KB'
Sử dụng sức mạnh của 1000 hoặc kibibytes sẽ thân thiện với tiêu chuẩn hơn:
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 Không bao giờ tin tưởng một thư viện in hàng ngàn với hậu tố K (chữ hoa) :)
P.S. Never trust a library that prints thousands with the K (uppercase) suffix :)
Tại sao không? Mã có thể là âm thanh hoàn hảo và tác giả chỉ không xem xét vỏ cho kilo. Có vẻ như việc tự động loại bỏ bất kỳ mã nào dựa trên quy tắc của bạn ...
Điều này sẽ làm những gì bạn cần trong hầu hết mọi tình huống, có thể tùy chỉnh với các đối số tùy chọn và như bạn có thể thấy, là khá nhiều tài liệu tự ghi:
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)
Ví dụ đầu ra:
>>> 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'
Các tùy chỉnh nâng cao:
>>> 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'
Mã này tương thích với cả Python 2 và Python 3. Tuân thủ PEP8 là một bài tập cho người đọc. Hãy nhớ rằng, đó là đầu ra đẹp.
Cập nhật:
Nếu bạn cần hàng nghìn dấu phẩy, chỉ cần áp dụng tiện ích mở rộng rõ ràng:
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))
Ví dụ:
>>> pretty_units(987654321098765432109876543210)
'816,968.5 YiB'
Bạn nên sử dụng "nhân hóa".
>>> humanize.naturalsize(1000000)
'1.0 MB'
>>> humanize.naturalsize(1000000, binary=True)
'976.6 KiB'
>>> humanize.naturalsize(1000000, gnu=True)
'976.6K'
Tài liệu tham khảo:
Riffing trên đoạn trích được cung cấp thay thế cho rush.filesize (), đây là đoạn trích cung cấp các số chính xác khác nhau dựa trên tiền tố được sử dụng. Nó không phải là ngắn gọn như một số đoạn, nhưng tôi thích kết quả.
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)
Dự án HumanFriendly giúp với điều này .
import humanfriendly
humanfriendly.format_size(1024)
Đoạn mã trên sẽ cho 1KB làm câu trả lời.
Ví dụ có thể được tìm thấy ở đây .
Rút ra từ tất cả các câu trả lời trước đây, đây là của tôi về nó. Đó là một đối tượng sẽ lưu trữ kích thước tệp theo byte dưới dạng một số nguyên. Nhưng khi bạn cố gắng in đối tượng, bạn sẽ tự động có được một phiên bản có thể đọc được của con người.
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)
Tôi thích độ chính xác cố định của phiên bản thập phân của người gửi , vì vậy đây là một loại kết hợp với câu trả lời của joctee ở trên (bạn có biết bạn có thể lấy nhật ký với các cơ sở không nguyên không?):
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')
DiveIntoPython3 cũng nói về chức năng này.
Django hiện đại có thẻ mẫu tự filesizeformat
:
Định dạng giá trị như human-readable
kích thước tệp (ví dụ '13 KB ',' 4,1 MB ',' 102 byte ', v.v.).
Ví dụ:
{{ value|filesizeformat }}
Nếu giá trị là 123456789, đầu ra sẽ là 117,7 MB.
Thông tin thêm: https://docs.djangoproject.com/en/1.10/ref/temsheet/builtins/#filesizeformat
Làm thế nào về một 2 lót đơn giản:
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])
Đây là cách nó hoạt động dưới mui xe:
Kb
, vì vậy câu trả lời phải là X KiB)file_size/value_of_closest_unit
cùng với đơn vị.Tuy nhiên, nó không hoạt động nếu kích thước tệp là 0 hoặc âm (vì nhật ký không được xác định cho các số 0 và -ve). Bạn có thể thêm kiểm tra thêm cho họ:
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])
Ví dụ:
>>> humanizeFileSize(538244835492574234)
'478.06 PiB'
>>> humanizeFileSize(-924372537)
'881.55 MiB'
>>> humanizeFileSize(0)
'0 Bytes'
LƯU Ý - Có sự khác biệt giữa Kb và KiB. KB có nghĩa là 1000 byte, trong khi KiB có nghĩa là 1024 byte. KB, MB, GB đều là bội số của 1000, trong khi KiB, MiB, GiB, v.v ... đều là bội số của 1024. Thông tin thêm về nó ở đây
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
Những gì bạn sắp tìm thấy dưới đây không phải là giải pháp ngắn gọn hay hiệu quả nhất trong số những giải pháp đã được đăng. Thay vào đó, nó tập trung vào một vấn đề cụ thể mà nhiều câu trả lời khác bỏ lỡ.
Cụ thể là trường hợp khi đầu vào like 999_995
được đưa ra:
Python 3.6.1 ...
...
>>> value = 999_995
>>> base = 1000
>>> math.log(value, base)
1.999999276174054
trong đó, bị cắt cụt đến số nguyên gần nhất và được áp dụng trở lại đầu vào cho
>>> order = int(math.log(value, base))
>>> value/base**order
999.995
Đây dường như là chính xác những gì chúng ta mong đợi cho đến khi chúng tôi được yêu cầu kiểm soát độ chính xác đầu ra . Và đây là khi mọi thứ bắt đầu hơi khó khăn.
Với độ chính xác được đặt thành 2 chữ số, chúng tôi nhận được:
>>> round(value/base**order, 2)
1000 # K
thay vì 1M
.
Làm thế nào chúng ta có thể chống lại điều đó?
Tất nhiên, chúng ta có thể kiểm tra nó một cách rõ ràng:
if round(value/base**order, 2) == base:
order += 1
Nhưng chúng ta có thể làm tốt hơn không? Chúng ta có thể biết cách order
cắt giảm trước khi chúng ta thực hiện bước cuối cùng không?
Hóa ra chúng ta có thể.
Giả sử quy tắc làm tròn số thập phân 0,5, if
điều kiện trên chuyển thành:
dẫn đến
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]}'
cho
>>> 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'
tham khảo Sridhar Ratnakumar
câu trả lời, cập nhật vào:
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)
và ví dụ đầu ra là:
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
Giải pháp này cũng có thể hấp dẫn bạn, tùy thuộc vào cách bạn làm việc:
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'