Cách tốt hơn để chuyển đổi kích thước tệp bằng Python


84

Tôi đang sử dụng một thư viện đọc một tệp và trả về kích thước của nó theo byte.

Kích thước tệp này sau đó được hiển thị cho người dùng cuối; để giúp họ hiểu nó dễ dàng hơn, tôi đang chuyển đổi rõ ràng kích thước tệp thànhMB bằng cách chia nhỏ cho 1024.0 * 1024.0. Tất nhiên điều này hoạt động, nhưng tôi tự hỏi có cách nào tốt hơn để làm điều này bằng Python không?

Tốt hơn, ý tôi có lẽ là một hàm stdlib có thể điều chỉnh kích thước theo kiểu tôi muốn. Giống như nếu tôi chỉ định MB, nó sẽ tự động chia nó cho 1024.0 * 1024.0. Somethign về những dòng này.


4
Vì vậy, viết một. Cũng lưu ý rằng nhiều hệ thống hiện sử dụng MB có nghĩa là 10 ^ 6 thay vì 2 ^ 20.
tc.

5
@AA, @tc: Xin lưu ý rằng Tiêu chuẩn SI và IEC là kB (Kilo) for 1.000 ByteKiB (Kibi) for 1.024 Byte. Xem en.wikipedia.org/wiki/Kibibyte .
Bobby

2
@Bobby: kB thực sự có nghĩa là "kilobel", bằng 10000 dB. Không có đơn vị SI cho byte. IIRC, IEC khuyến nghị KiB nhưng không xác định kB hoặc KB.
tc.

2
@tc. Tiền tố kilo được SI định nghĩa có nghĩa là 1000. IEC định nghĩa kB, v.v. để sử dụng tiền tố SI thay vì 2 ^ 10.
ford

1
Ý tôi là các tiền tố được định nghĩa chung theo SI, nhưng các chữ viết tắt cho kích thước dữ liệu không phải là: Physics.nist.gov/cuu/Units/prefixes.html . Chúng được xác định bởi IEC: Physics.nist.gov/cuu/Units/binary.html
ford

Câu trả lời:


100

quick.filesize sẽ có kích thước tính bằng byte và tạo ra một chuỗi đẹp nếu có.

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

Hoặc nếu bạn muốn 1K == 1000 (đó là những gì hầu hết người dùng giả định):

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

Nó cũng có hỗ trợ IEC (nhưng điều đó không được ghi lại):

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

Bởi vì nó được viết bởi Awesome Martijn Faassen, mã nhỏ, rõ ràng và có thể mở rộng. Viết hệ thống của riêng bạn thật dễ dàng.

Đây là một:

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

Được sử dụng như vậy:

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

1
Hm, bây giờ tôi cần một cái để đi theo con đường khác. Từ "1 kb" đến 1024(một int).
mlissner 09/07/18

2
Chỉ hoạt động trong python 2
e-info128

2
Gói này có thể thú vị nhưng giấy phép kỳ quặc và thực tế là không có mã nguồn trực tuyến có sẵn khiến nó trở thành điều mà tôi rất vui khi tránh được. Và dường như nó cũng chỉ hỗ trợ python2.
Almog Cohen

1
@Almog Sau đó, nguồn trực tuyến, có sẵn trực tiếp từ PyPI (một số gói không có hệ thống lưu trữ Github, chỉ là một trang PyPI) và giấy phép không quá khó hiểu, ZPL là Giấy phép Công cộng Zope, theo hiểu biết của tôi , Giống BSD. Tôi đồng ý rằng bản thân việc cấp phép là kỳ lạ: không có tệp 'LICENSE.txt' tiêu chuẩn, cũng như không có phần mở đầu ở đầu mỗi tệp nguồn.
slblanc

1
Để có được megabyte, tôi đã thực hiện phương trình sau bằng cách sử dụng toán tử dịch chuyển theo chiều bit: MBFACTOR = float(1 << 20); mb= int(size_in_bytes) / MBFACTOR @LennartRegebro
alper

147

Đây là những gì tôi sử dụng:

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: kích thước phải được gửi bằng byte.


11
Nếu bạn đang gửi kích thước tính bằng byte thì chỉ cần thêm "B" làm phần tử đầu tiên của size_name.
tuxGurl

Khi bạn có byte kích thước bằng 0 của tệp, nó không thành công. log (0, 1024) không được xác định! Bạn nên kiểm tra trường hợp 0 ​​byte trước câu lệnh này i = int (math.floor (math.log (size, 1024))).
genclik27

genclik - bạn nói đúng. Tôi vừa gửi một bản chỉnh sửa nhỏ sẽ sửa lỗi này và cho phép chuyển đổi từ byte. Cảm ơn, SAPAM, cho bản gốc
FarmerGedden

HI @WHK như tuxGurl đã đề cập đến một cách khắc phục dễ dàng.
James

3
Trên thực tế, tên kích thước cần phải là ("B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"). Xem en.wikipedia.org/wiki/Mebibyte để biết thêm thông tin.
Alex

36

Thay vì ước số kích thước, 1024 * 1024bạn có thể sử dụng << toán tử dịch chuyển theo chiều bit , tức là 1<<20để lấy megabyte, 1<<30lấy gigabyte, v.v.

Trong kịch bản đơn giản nhất bạn có thể có ví dụ như một hằng số MBFACTOR = float(1<<20)mà sau đó có thể được sử dụng với byte, ví dụ: megas = size_in_bytes/MBFACTOR.

Megabyte thường là tất cả những gì bạn cần hoặc có thể sử dụng những thứ tương tự như thế này:

# 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
Có phải nó không >>?
Tjorriemorrie

2
@Tjorriemorrie: nó phải là một dịch chuyển trái, chuyển sang phải sẽ làm giảm bit duy nhất và kết quả là 0.
ccpizza

Câu trả lời tuyệt vời. Cảm ơn bạn.
Borislav Aymaliev

Tôi biết cái này là cũ, nhưng liệu đây có phải là cách sử dụng chính xác? def convert_to_mb (data_b): print (data_b / (1 << 20))
roastbeeef 27/03 '19

22

Đây là hàm compact để tính toán kích thước

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

Để biết đầu ra chi tiết hơn và hoạt động ngược lại, vui lòng tham khảo: http://code.activestate.com/recipes/578019-bytes-to-human-human-to-bytes-converter/


12

Xem bên dưới để biết cách in kích thước tệp nhanh chóng và tương đối dễ đọc trong một dòng mã nếu bạn đã biết mình muốn gì. Những dấu gạch đầu dòng này kết hợp câu trả lời tuyệt vời của @ccpizza ở trên với một số thủ thuật định dạng tiện dụng Tôi đọc ở đây Làm thế nào để in số có dấu phẩy làm dấu phân cách hàng nghìn? .

Byte

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

Kilobits

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

Kilobyte

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

Megabits

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

Megabyte

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

Gigabits

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

Gigabyte

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

Terabyte

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

Rõ ràng là họ cho rằng bạn biết đại khái dung lượng bạn sẽ xử lý ngay từ đầu, trong trường hợp của tôi (trình biên tập video tại South West London TV) là MB và đôi khi là GB cho video clip.


CẬP NHẬT SỬ DỤNG PATHLIB Để trả lời nhận xét của Hildy, đây là gợi ý của tôi cho một cặp hàm nhỏ gọn (giữ mọi thứ ở dạng 'nguyên tử' thay vì hợp nhất chúng) chỉ bằng cách sử dụng thư viện chuẩn 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
Đó là một cách khá thông minh để làm điều đó. Tôi tự hỏi liệu bạn có thể đưa những thứ này vào một hàm mà bạn chuyển vào cho dù bạn muốn kb. mb's và vân vân. Bạn thậm chí có thể có một lệnh đầu vào hỏi bạn muốn cái nào, điều này sẽ khá thuận tiện nếu bạn thực hiện điều này nhiều.
Hildy

Xem ở trên, Hildy ... Bạn cũng có thể tùy chỉnh dòng từ điển như @ lennart-regebro được nêu ở trên ... có thể hữu ích cho việc quản lý lưu trữ, ví dụ: "Phân vùng", "Cụm", "Đĩa 4TB", "DVD_RW", " Đĩa Blu-Ray "," thẻ nhớ 1GB "hoặc bất cứ thứ gì.
Peter F

Tôi cũng vừa thêm Kb (Kilobit), Mb (Megabit) và Gb (Gigabit) - người dùng thường nhầm lẫn về tốc độ mạng hoặc tốc độ truyền tệp, vì vậy nghĩ rằng nó có thể hữu ích.
Peter F

9

Đề phòng trường hợp có ai đó đang tìm kiếm mặt trái của vấn đề này (như tôi chắc chắn đã làm) thì đây là những gì phù hợp với tôi:

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

Bạn không xử lý trường hợp các số thập phân như 1,5GB. Để khắc phục, chỉ cần thay đổi << 10thành * 1024, << 20đến * 1024**2<< 30đến * 1024**3.
E235

3

Nó đây:

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

Đây là hai xu của tôi, cho phép truyền lên và xuống, và thêm độ chính xác có thể tùy chỉnh:

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)

Thêm TB, v.v., như bạn muốn.


Tôi sẽ bỏ phiếu này lên vì nó có thể làm việc ra chỉ với các thư viện chuẩn python
Ciasto piekarz

1

Đây là phiên bản phù hợp với đầu ra của 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)

điều này có thể sử dụng được trong rất nhiều cài đặt. rất vui vì tôi đã xem qua bình luận này. cảm ơn rất nhiều.
Saurabh Jain

0

Đây là cách triển khai của tôi:

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)

Nó sẽ in tối đa ba số thập phân và nó xóa các số không ở sau và dấu chấm. Tham số boolean sisẽ chuyển đổi việc sử dụng độ lớn kích thước dựa trên 10 so với 2.

Đây là đối tác của nó. Nó cho phép ghi các tệp cấu hình sạch như {'maximum_filesize': from_filesize('10M'). Nó trả về một số nguyên gần đúng với kích thước tệp dự định. Tôi không sử dụng dịch chuyển bit vì giá trị nguồn là một số dấu phẩy động (nó sẽ chấp nhận from_filesize('2.15M')tốt). Chuyển đổi nó thành một số nguyên / thập phân sẽ hiệu quả nhưng làm cho mã phức tạp hơn và nó đã hoạt động như bình thường.

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

Đây là một phiên bản triển khai ngược khác của @ romeo xử lý một chuỗi đầu vào.

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

Tương tự như câu trả lời của Aaron Duke nhưng "pythonic" hơn;)

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

Tôi mới học lập trình. Tôi đã nghĩ ra hàm sau đây để chuyển đổi kích thước tệp nhất định thành định dạng có thể đọc được.

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

Điều này hoạt động chính xác cho tất cả các kích thước tệp:

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
nó có thực sự đáng để sao chép câu trả lời từ "sapam" .... không .... chỉ cần bình luận lần sau.
Angry 84

Một câu trả lời khác phải được sao chép ở một nơi khác và sau đó sao chép lại ở đây ... chúng tôi khuyến khích những câu trả lời gốc, trên thực tế.
WesternGun
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.