Pythonでファイルサイズを変換するためのより良い方法


84

ファイルを読み取り、そのサイズをバイト単位で返すライブラリを使用しています。

このファイルサイズはエンドユーザーに表示されます。彼らがそれを理解しやすくするために、私はMBそれをで割ることによってファイルサイズを明示的に変換しています1024.0 * 1024.0。もちろんこれは機能しますが、Pythonでこれを行うためのより良い方法があるのだろうかと思います。

より良いのは、おそらく、必要なタイプに応じてサイズを操作できるstdlib関数を意味します。を指定した場合と同様にMB、自動的にで除算され1024.0 * 1024.0ます。これらの線の何か。


4
だから1つ書いてください。また、多くのシステムでMBが2 ^ 20ではなく10 ^ 6を意味するようになっていることにも注意してください。
TC。

5
@ AA、@ tc:SIおよびIECの基準はkB (Kilo) for 1.000 Byteおよびであることに注意してくださいKiB (Kibi) for 1.024 Byteen.wikipedia.org/wiki/Kibibyteを参照してください。
ボビー

2
@Bobby:kBは実際には「キロベル」を意味し、10000dBに相当します。バイトのSI単位はありません。IIRC、IECはKiBを推奨していますが、kBまたはKBは定義していません。
TC。

2
@tc。接頭辞キロはSIによって1000を意味するように定義されています。IECは2 ^ 10の代わりにSI接頭辞を使用するようにkBなどを定義しました。
フォード2013

1
つまり、プレフィックスは一般にSIによって定義されますが、データサイズの略語は次のとおりではありません:physics.nist.gov/cuu/Units/prefixes.html。それらはIECによって定義されています:physics.nist.gov/cuu/Units/binary.html
フォード

回答:


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によって作成されているため、コードは小さく、明確で、拡張可能です。独自のシステムを作成するのはとても簡単です。

これが1つです:

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つが必要です。「1kb」から1024(int)まで。
mlissner 2018

2
Python 2でのみ機能します
e-info1 2819

2
このパッケージはかっこいいかもしれませんが、奇妙なライセンスとオンラインで利用可能なソースコードがないという事実により、私はそれを避けたいと思っています。また、python2のみをサポートしているようです。
アルモグコーヘン

1
@AlmogCohenソースはオンラインで、PyPIから直接入手でき(一部のパッケージにはGithubリポジトリがなく、PyPIページのみです)、ライセンスはそれほど曖昧ではありません。ZPLはZopeパブリックライセンスです。これは私の知る限りです。 、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
サイズをバイト単位で送信する場合は、size_nameの最初の要素として「B」を追加するだけです。
tuxGurl 2013年

ファイルのサイズが0バイトの場合、失敗します。log(0、1024)が定義されていません!このステートメントの前に0バイトの大文字と小文字をチェックする必要がありますi = int(math.floor(math.log(size、1024)))。
genclik27 2014年

genclik-その通りです。これを修正し、バイトからの変換を有効にするマイナーな編集を送信しました。Sapam、オリジナルをありがとう
FarmerGedden 2014

tuxGurlが簡単に修正できると述べたHI @ WHK。
ジェームズ

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:左シフトである必要があります。右シフトでは、ビットが1つだけ削除され、結果はになり0ます。
ccpizza 2018

素晴らしい答え。ありがとうございました。
Borislav Aymaliev 2018

私はこれが古いことを知っていますが、これは正しい使用法でしょうか?def convert_to_mb(data_b):print(data_b /(1 << 20))
roastbeeef19年

22

これがサイズを計算するためのコンパクトな関数です

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

より詳細な出力およびその逆の操作については、http//code.activestate.com/recipes/578019-bytes-to-human-human-to-bytes-converter/を参照してください。


12

必要なものがすでにわかっている場合に、ファイルサイズを1行のコードで印刷するための、すばやく比較的読みやすい方法については、以下を参照してください。これらのワンライナーは、上記の@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")

明らかに、彼らはあなたが最初に扱う予定のサイズを大まかに知っていると想定しています。私の場合(South West London TVのビデオエディタ)は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など。どれが欲しいかを尋ねる入力コマンドを持つこともできます。これをたくさん行うと非常に便利です。
ヒルディ2018年

上記のヒルディを参照してください...上記の@ lennart-regebroのように辞書の行をカスタマイズすることもできます...これはストレージ管理に役立つ可能性があります(例:「パーティション」、「クラスター」、「4TBディスク」、「DVD_RW」、「ブルーレイディスク」、「1GBメモリスティック」など。
ピーター・F

また、Kb(Kilobit)、Mb(Megabit)、Gb(Gigabit)も追加しました。ユーザーはネットワークやファイル転送の速度に関して混乱することが多いので、便利かもしれないと思いました。
ピーター・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進数の場合は扱っていません。それだけで変更修正する<< 10には* 1024<< 20* 1024**2して<< 30まで* 1024**3
E2 3519年

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

ここに私の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 2018

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)

これは非常に多くの設定で使用できます。このコメントに出くわしてよかったです。どうもありがとう。
Saurabh Jain

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)

最大3つの小数を出力し、後続のゼロとピリオドを取り除きます。ブールパラメータ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

アーロンデュークの返事に似ていますが、より「パイソン的」です;)

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」から答えをコピーする価値は本当にありましたか....いいえ....次回コメントするだけです。
怒り84

別の回答を別の場所にコピーしてから、ここでもう一度コピーする必要があります...実際、元の回答をお勧めします。
WesternGun 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.