在Python中转换文件大小的更好方法


84

我正在使用一个读取文件并返回其大小(以字节为单位)的库。

然后,该文件大小将显示给最终用户。使他们更容易理解它,我明确的文件大小转换为MB通过将其除以1024.0 * 1024.0。当然可以,但是我想知道在Python中还有更好的方法吗?

更好的是,我的意思是也许一个stdlib函数可以根据我想要的类型操纵大小。就像我指定的一样MB,它会自动将其除以1024.0 * 1024.0。这些线路上有一些东西。


4
所以写一个。还要注意,许多系统现在使用MB表示10 ^ 6而不是2 ^ 20。
tc。

5
@AA,@tc:请记住,SI和IEC规范是kB (Kilo) for 1.000 Byteand KiB (Kibi) for 1.024 Byte。参见en.wikipedia.org/wiki/Kibibyte
鲍比(Bobby)

2
@Bobby:kB实际上是“千牛”,等于10000 dB。字节没有SI单位。IIRC,IEC建议使用KiB,但未定义kB或KB。
tc。

2
@tc。前缀千由SI定义为平均值1000。IEC定义的​​kB等使用SI前缀而不是2 ^ 10。
2013年

1
我的意思是前缀通常由SI定义,但数据大小的缩写不是:physics.nist.gov/cuu/Units/prefixes.html。这些是由IEC定义的​​:physics.nist.gov/cuu/Units/binary.html
2013年

Answers:


100

hurry.filesize,它将以字节为单位,如果有的话,将做出一个漂亮的字符串。

>>> from hurry.filesize import size
>>> size(11000)
'10K'
>>> size(198283722)
'189M'

或者,如果您希望1K == 1000(这是大多数用户的假设):

>>> from hurry.filesize import size, si
>>> size(11000, system=si)
'11K'
>>> size(198283722, system=si)
'198M'

它也具有IEC支持(但是没有记录):

>>> from hurry.filesize import size, iec
>>> size(11000, system=iec)
'10Ki'
>>> size(198283722, system=iec)
'189Mi'

因为它是由Awesome Martijn Faassen编写的,所以代码小巧,清晰且可扩展。编写自己的系统非常容易。

这是一个:

mysystem = [
    (1024 ** 5, ' Megamanys'),
    (1024 ** 4, ' Lotses'),
    (1024 ** 3, ' Tons'), 
    (1024 ** 2, ' Heaps'), 
    (1024 ** 1, ' Bunches'),
    (1024 ** 0, ' Thingies'),
    ]

像这样使用:

>>> from hurry.filesize import size
>>> size(11000, system=mysystem)
'10 Bunches'
>>> size(198283722, system=mysystem)
'189 Heaps'

1
嗯,现在我需要另辟。径。从“ 1 kb”到1024(整数)。
mlissner '18

2
仅在python 2中有效
e-info128

2
这个软件包可能很酷,但是许可证很奇怪,而且没有在线可用的源代码,这一事实令我很高兴避免。而且它似乎仅支持python2。
Almog Cohen

1
@AlmogCohen的资源是在线的,可以直接从PyPI获得(某些软件包没有Github存储库,只是一个PyPI页面),而且许可并不是那么晦涩,ZPL是Zope Public License,据我所知。 ,类似于BSD。我确实同意许可本身很奇怪:没有标准的“ LICENSE.txt”文件,每个源文件的顶部也没有序言。
sleblanc

1
为了获得兆字节,我使用按位移位运算符执行了以下方程式:MBFACTOR = float(1 << 20); mb= int(size_in_bytes) / MBFACTOR @LennartRegebro
alper

147

这是我用的:

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

注意:大小应以字节为单位发送。


11
如果您以字节为单位发送大小,则只需将“ B”添加为size_name的第一个元素。
tuxGurl

当文件的大小为0字节时,它将失败。未定义log(0,1024)!您应在此语句i = int(math.floor(math.log(size,1024)))之前检查0字节大小写。
genclik27

genclik-你是对的。我刚刚提交了一个较小的编辑,它将解决此问题,并允许从字节转换。谢谢,Sapam,原始
FarmerGedden

HI @WHK,作为tuxGurl提到了一个简单的解决方法。
詹姆斯

3
实际上,大小名称将需要为(“ B”,“ KiB”,“ MiB”,“ GiB”,“ TiB”,“ PiB”,“ EiB”,“ ZiB”,“ YiB”)。有关更多信息,请参见en.wikipedia.org/wiki/Mebibyte
亚历克斯

36

除了使用大小除数,1024 * 1024还可以使用<< 按位移位运算符,例如1<<20,获取兆字节,1<<30获取千兆字节等。

在最简单的情况下,您可以具有例如MBFACTOR = float(1<<20)可以与字节一起使用的常量,即:megas = size_in_bytes/MBFACTOR

通常只需要兆字节,否则可以使用如下所示的内容:

# bytes pretty-printing
UNITS_MAPPING = [
    (1<<50, ' PB'),
    (1<<40, ' TB'),
    (1<<30, ' GB'),
    (1<<20, ' MB'),
    (1<<10, ' KB'),
    (1, (' byte', ' bytes')),
]


def pretty_size(bytes, units=UNITS_MAPPING):
    """Get human-readable file sizes.
    simplified version of https://pypi.python.org/pypi/hurry.filesize/
    """
    for factor, suffix in units:
        if bytes >= factor:
            break
    amount = int(bytes / factor)

    if isinstance(suffix, tuple):
        singular, multiple = suffix
        if amount == 1:
            suffix = singular
        else:
            suffix = multiple
    return str(amount) + suffix

print(pretty_size(1))
print(pretty_size(42))
print(pretty_size(4096))
print(pretty_size(238048577))
print(pretty_size(334073741824))
print(pretty_size(96995116277763))
print(pretty_size(3125899904842624))

## [Out] ###########################
1 byte
42 bytes
4 KB
227 MB
311 GB
88 TB
2 PB

10
不是>>
Tjorriemorrie 2014年

2
@Tjorriemorrie:必须为左移,右移将使唯一的位下降并导致0
ccpizza

辉煌的答案。谢谢。
Borislav Aymaliev

我知道这是旧的,但这是正确的用法吗?def convert_to_mb(data_b):print(data_b /(1 << 20))
roastbeeef19年


12

如果您已经知道所需的内容,请参见下文,以一种快速且相对易于阅读的方式在一行代码中打印文件大小。这些单行代码将上述@ccpizza的出色答案与一些方便的格式化技巧结合在一起,我在这里阅读了如何将带有逗号的数字打印为数千个分隔符?

字节数

print ('{:,.0f}'.format(os.path.getsize(filepath))+" B")

长袍

print ('{:,.0f}'.format(os.path.getsize(filepath)/float(1<<7))+" kb")

千字节

print ('{:,.0f}'.format(os.path.getsize(filepath)/float(1<<10))+" KB")

兆位

print ('{:,.0f}'.format(os.path.getsize(filepath)/float(1<<17))+" mb")

兆字节

print ('{:,.0f}'.format(os.path.getsize(filepath)/float(1<<20))+" MB")

千兆位

print ('{:,.0f}'.format(os.path.getsize(filepath)/float(1<<27))+" gb")

千兆字节

print ('{:,.0f}'.format(os.path.getsize(filepath)/float(1<<30))+" GB")

兆兆字节

print ('{:,.0f}'.format(os.path.getsize(filepath)/float(1<<40))+" TB")

显然,他们假设您一开始就大致知道要处理的大小,在我的案例(西南伦敦电视台的视频编辑器)中,视频剪辑的大小为MB,有时为GB。


使用PATHLIB更新 为了回应Hildy的评论,以下是我的建议:仅使用Python标准库提供一对紧凑的函数(保持事物为“原子的”而不是合并它们):

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
>>> format_size("d:\\media\\bags of fun.avi")
'38 MB'
>>> format_size("d:\\media\\bags of fun.avi","KB")
'38,763 KB'
>>> format_size("d:\\media\\bags of fun.avi","kb")
'310,104 kb'

1
这是一种非常聪明的方法。我想知道是否可以将它们放入函数中,以传递是否需要kb。mb等。您甚至可以使用一个输入命令,询问您要哪个,如果您经常这样做,这将非常方便。
希尔迪,

见上文Hildy ...您还可以自定义字典行,如上面概述的@ lennart-regebro ...,这可能对存储管理很有用,例如“分区”,“集群”,“ 4TB磁盘”,“ DVD_RW”,“蓝光光盘”,“ 1GB记忆棒”或其他内容。
彼得F,

我还刚刚添加了Kb(千比特),Mb(兆比特)和Gb(千兆比特)-用户经常在网络或文件传输速度方面感到困惑,因此认为这很方便。
彼得F,

9

以防万一有人在寻找这个问题的反面(就像我确定的那样),这对我有用:

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

您不处理十进制数字(例如1.5GB)的情况。要修复它只是改变<< 10* 1024<< 20* 1024**2<< 30* 1024**3
E235

3

这里是:

def convert_bytes(size):
   for x in ['bytes', 'KB', 'MB', 'GB', 'TB']:
       if size < 1024.0:
           return "%3.1f %s" % (size, x)
       size /= 1024.0

   return size

2

这是我的两分钱,可以上下浮动,并增加了可自定义的精度:

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)

根据需要添加TB,等等。


我定投这件事,因为它可以计算出只需用Python标准库
Ciasto piekarz

1

这是与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

1
UNITS = {1000: ['KB', 'MB', 'GB'],
            1024: ['KiB', 'MiB', 'GiB']}

def approximate_size(size, flag_1024_or_1000=True):
    mult = 1024 if flag_1024_or_1000 else 1000
    for unit in UNITS[mult]:
        size = size / mult
        if size < mult:
            return '{0:.3f} {1}'.format(size, unit)

approximate_size(2123, False)

这在许多设置中可用。很高兴我遇到了这个评论。非常感谢。
索拉卜·in那

0

这是我的实现:

from bisect import bisect

def to_filesize(bytes_num, si=True):
    decade = 1000 if si else 1024
    partitions = tuple(decade ** n for n in range(1, 6))
    suffixes = tuple('BKMGTP')

    i = bisect(partitions, bytes_num)
    s = suffixes[i]

    for n in range(i):
        bytes_num /= decade

    f = '{:.3f}'.format(bytes_num)

    return '{}{}'.format(f.rstrip('0').rstrip('.'), s)

它将最多打印三个小数,并去除尾随的零和句点。布尔参数si将切换基于10和2的大小量级的用法。

这是它的对应物。它允许编写干净的配置文件,如{'maximum_filesize': from_filesize('10M')。它返回一个近似于预期文件大小的整数。我没有使用移位,因为源值是一个浮点数(它将接受from_filesize('2.15M')就可以了)。将其转换为整数/小数虽然可以,但是会使代码更加复杂,并且已经可以正常使用了。

def from_filesize(spec, si=True):
    decade = 1000 if si else 1024
    suffixes = tuple('BKMGTP')

    num = float(spec[:-1])
    s = spec[-1]
    i = suffixes.index(s)

    for n in range(i):
        num *= decade

    return int(num)

-1

这是@romeo的反向实现的另一个版本,它处理单个输入字符串。

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)

-1

类似于亚伦·杜克(Aaron Duke)的答复,但更多的是“ 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)

-1

我是编程新手。我想出了以下函数,它将给定的文件大小转换为可读格式。

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]

-2

这适用于所有文件大小:

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

3
真的值得复制“ sapam”中的答案吗?...不。...下次再发表评论。
生气

另一个答案必须复制到其他地方,然后再复制到这里……实际上,我们鼓励原始答案。
WesternGun
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.