可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I am using a library that reads a file and returns its size in bytes.
This file size is then displayed to the end user; to make it easier for them to understand it, I am explicitly converting the file size to MB
by dividing it by 1024.0 * 1024.0
. Of course this works, but I am wondering is there a better way to do this in Python?
By better, I mean perhaps a stdlib function that can manipulate sizes according to the type I want. Like if I specify MB
, it automatically divides it by 1024.0 * 1024.0
. Somethign on these lines.
回答1:
There is hurry.filesize that will take the size in bytes and make a nice string out if it.
>>> from hurry.filesize import size
>>> size(11000)
'10K'
>>> size(198283722)
'189M'
Or if you want 1K == 1000 (which is what most users assume):
>>> from hurry.filesize import size, si
>>> size(11000, system=si)
'11K'
>>> size(198283722, system=si)
'198M'
It has IEC support as well (but that wasn't documented):
>>> from hurry.filesize import size, iec
>>> size(11000, system=iec)
'10Ki'
>>> size(198283722, system=iec)
'189Mi'
Because it's written by the Awesome Martijn Faassen, the code is small, clear and extensible. Writing your own systems is dead easy.
Here is one:
mysystem = [
(1024 ** 5, ' Megamanys'),
(1024 ** 4, ' Lotses'),
(1024 ** 3, ' Tons'),
(1024 ** 2, ' Heaps'),
(1024 ** 1, ' Bunches'),
(1024 ** 0, ' Thingies'),
]
Used like so:
>>> from hurry.filesize import size
>>> size(11000, system=mysystem)
'10 Bunches'
>>> size(198283722, system=mysystem)
'189 Heaps'
回答2:
Here is what I use:
import math
def convert_size(size_bytes):
if size_bytes == 0:
return "0B"
size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
i = int(math.floor(math.log(size_bytes, 1024)))
p = math.pow(1024, i)
s = round(size_bytes / p, 2)
return "%s %s" % (s, size_name[i])
NB : size should be sent in Bytes.
回答3:
Instead of a size divisor of 1024 * 1024
you could use the <<
bitwise shifting operator, i.e. 1<<20
to get megabytes, 1<<30
to get gigabytes, etc.
I defined a constant MBFACTOR = float(1<<20)
which can then be used with bytes, i.e.: megas = size_in_bytes/MBFACTOR
.
回答4:
Here is the compact function to calculate size
def GetHumanReadable(size,precision=2):
suffixes=['B','KB','MB','GB','TB']
suffixIndex = 0
while size > 1024 and suffixIndex < 4:
suffixIndex += 1 #increment the index of the suffix
size = size/1024.0 #apply the division
return "%.*f%s"%(precision,size,suffixes[suffixIndex])
For more detailed output and vice versa operation please refer: http://code.activestate.com/recipes/578019-bytes-to-human-human-to-bytes-converter/
回答5:
Just in case anyone's searching for the reverse of this problem (as I sure did) here's what works for me:
def get_bytes(size, suffix):
size = int(float(size))
suffix = suffix.lower()
if suffix == 'kb' or suffix == 'kib':
return size << 10
elif suffix == 'mb' or suffix == 'mib':
return size << 20
elif suffix == 'gb' or suffix == 'gib':
return size << 30
return False
回答6:
See below for a quick and relatively easy-to-read way to print file sizes in a single line of code if you already know what you want. These one-liners combine the great answer by @ccpizza above with some handy formatting tricks I read here How to print number with commas as thousands separators?.
Bytes
print ('{:,.0f}'.format(os.path.getsize(filepath))+" B")
Kilobits
print ('{:,.0f}'.format(os.path.getsize(filepath)/float(1<<7))+" kb")
Kilobytes
print ('{:,.0f}'.format(os.path.getsize(filepath)/float(1<<10))+" KB")
Megabits
print ('{:,.0f}'.format(os.path.getsize(filepath)/float(1<<17))+" mb")
Megabytes
print ('{:,.0f}'.format(os.path.getsize(filepath)/float(1<<20))+" MB")
Gigabits
print ('{:,.0f}'.format(os.path.getsize(filepath)/float(1<<27))+" gb")
Gigabytes
print ('{:,.0f}'.format(os.path.getsize(filepath)/float(1<<30))+" GB")
Terabytes
print ('{:,.0f}'.format(os.path.getsize(filepath)/float(1<<40))+" TB")
Obviously they assume you know roughly what size you're going to be dealing with at the outset, which in my case (video editor at South West London TV) is MB and occasionally GB for video clips.
In reply to Hildy's comment, here's my suggestion for a compact (3 line) function using just the Python standard library:
from os.path import getsize
def file_size(filepath, unit = "MB"):
bit_shift = {"B":0, "kb":7, "KB":10, "mb":17, "MB":20, "gb":27, "GB":30, "TB":40}
return '{:,.0f}'.format(getsize(filepath)/float(1<<bit_shift[unit]))+" "+unit
# Tests and test results
>>> file_size("d:\\media\\bags of fun.avi")
'38 MB'
>>> file_size("d:\\media\\bags of fun.avi","KB")
'38,763 KB'
>>> file_size("d:\\media\\bags of fun.avi","kb")
'310,104 kb'
回答7:
Here my two cents, which permits casting up and down, and adds customizable precision:
def convertFloatToDecimal(f=0.0, precision=2):
'''
Convert a float to string of decimal.
precision: by default 2.
If no arg provided, return "0.00".
'''
return ("%." + str(precision) + "f") % f
def formatFileSize(size, sizeIn, sizeOut, precision=0):
'''
Convert file size to a string representing its value in B, KB, MB and GB.
The convention is based on sizeIn as original unit and sizeOut
as final unit.
'''
assert sizeIn.upper() in {"B", "KB", "MB", "GB"}, "sizeIn type error"
assert sizeOut.upper() in {"B", "KB", "MB", "GB"}, "sizeOut type error"
if sizeIn == "B":
if sizeOut == "KB":
return convertFloatToDecimal((size/1024.0), precision)
elif sizeOut == "MB":
return convertFloatToDecimal((size/1024.0**2), precision)
elif sizeOut == "GB":
return convertFloatToDecimal((size/1024.0**3), precision)
elif sizeIn == "KB":
if sizeOut == "B":
return convertFloatToDecimal((size*1024.0), precision)
elif sizeOut == "MB":
return convertFloatToDecimal((size/1024.0), precision)
elif sizeOut == "GB":
return convertFloatToDecimal((size/1024.0**2), precision)
elif sizeIn == "MB":
if sizeOut == "B":
return convertFloatToDecimal((size*1024.0**2), precision)
elif sizeOut == "KB":
return convertFloatToDecimal((size*1024.0), precision)
elif sizeOut == "GB":
return convertFloatToDecimal((size/1024.0), precision)
elif sizeIn == "GB":
if sizeOut == "B":
return convertFloatToDecimal((size*1024.0**3), precision)
elif sizeOut == "KB":
return convertFloatToDecimal((size*1024.0**2), precision)
elif sizeOut == "MB":
return convertFloatToDecimal((size*1024.0), precision)
Add TB
, etc, as you wish.
回答8:
Here's a version that matches the output of ls -lh.
def human_size(num: int) -> str:
base = 1
for unit in ['B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']:
n = num / base
if n < 9.95 and unit != 'B':
# Less than 10 then keep 1 decimal place
value = "{:.1f}{}".format(n, unit)
return value
if round(n) < 1000:
# Less than 4 digits so use this
value = "{}{}".format(round(n), unit)
return value
base *= 1024
value = "{}{}".format(round(n), unit)
return value
回答9:
Here's another version of @romeo's reverse implementation that handles a single input string.
import re
def get_bytes(size_string):
try:
size_string = size_string.lower().replace(',', '')
size = re.search('^(\d+)[a-z]i?b$', size_string).groups()[0]
suffix = re.search('^\d+([kmgtp])i?b$', size_string).groups()[0]
except AttributeError:
raise ValueError("Invalid Input")
shft = suffix.translate(str.maketrans('kmgtp', '12345')) + '0'
return int(size) << int(shft)
回答10:
Similar to Aaron Duke's reply but more "pythonic" ;)
import re
RE_SIZE = re.compile(r'^(\d+)([a-z])i?b?$')
def to_bytes(s):
parts = RE_SIZE.search(s.lower().replace(',', ''))
if not parts:
raise ValueError("Invalid Input")
size = parts.group(1)
suffix = parts.group(2)
shift = suffix.translate(str.maketrans('kmgtp', '12345')) + '0'
return int(size) << int(shift)
回答11:
I'm new to programming. I came up with this following function that converts a given file size into readable format.
def file_size_converter(size):
magic = lambda x: str(round(size/round(x/1024), 2))
size_in_int = [int(1 << 10), int(1 << 20), int(1 << 30), int(1 << 40), int(1 << 50)]
size_in_text = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
for i in size_in_int:
if size < i:
g = size_in_int.index(i)
position = int((1024 % i) / 1024 * g)
ss = magic(i)
return ss + ' ' + size_in_text[position]
回答12:
This work correctly for all file sizes:
import math
from os.path import getsize
def convert_size(size):
if (size == 0):
return '0B'
size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
i = int(math.floor(math.log(size,1024)))
p = math.pow(1024,i)
s = round(size/p,2)
return '%s %s' % (s,size_name[i])
print(convert_size(getsize('file_name.zip')))