Bessere Möglichkeit, Dateigrößen in Python zu konvertieren

84

Ich verwende eine Bibliothek, die eine Datei liest und ihre Größe in Bytes zurückgibt.

Diese Dateigröße wird dann dem Endbenutzer angezeigt. Um es ihnen leichter zu machen, es zu verstehen, konvertiere ich die Dateigröße explizit in, indem ich sie MBdurch dividiere 1024.0 * 1024.0. Natürlich funktioniert das, aber ich frage mich, ob es einen besseren Weg gibt, dies in Python zu tun.

Mit besser meine ich vielleicht eine stdlib-Funktion, die Größen entsprechend dem gewünschten Typ manipulieren kann. Wie wenn ich spezifiziere MB, teilt es es automatisch durch 1024.0 * 1024.0. Etwas in diesen Zeilen.

user225312
quelle
4
Also schreibe einen. Beachten Sie auch, dass viele Systeme MB jetzt verwenden, um 10 ^ 6 anstelle von 2 ^ 20 zu bedeuten.
tc.
5
@AA, @tc: Bitte beachten Sie, dass die SI- und IEC-Norm kB (Kilo) for 1.000 Byteund ist KiB (Kibi) for 1.024 Byte. Siehe en.wikipedia.org/wiki/Kibibyte .
Bobby
2
@ Bobby: kB bedeutet eigentlich "Kilobel", gleich 10000 dB. Es gibt keine SI-Einheit für Byte. IIRC, die IEC empfiehlt KiB, definiert jedoch nicht kB oder KB.
tc.
2
@tc. Das Präfix Kilo wird durch SI als 1000 definiert. Die IEC definiert kB usw., um das SI-Präfix anstelle von 2 ^ 10 zu verwenden.
Ford
1
Ich meine, die Präfixe werden im Allgemeinen durch SI definiert, aber die Abkürzungen für die Datengröße lauten nicht: physics.nist.gov/cuu/Units/prefixes.html . Diese werden von der IEC definiert: physics.nist.gov/cuu/Units/binary.html
ford

Antworten:

98

Es gibt hurry.filesize , das die Größe in Bytes annimmt und eine schöne Zeichenfolge erstellt, wenn dies der Fall ist .

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

Oder wenn Sie 1K == 1000 möchten (was die meisten Benutzer annehmen):

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

Es hat auch IEC-Unterstützung (aber das wurde nicht dokumentiert):

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

Da der Code von Awesome Martijn Faassen geschrieben wurde, ist er klein, klar und erweiterbar. Das Schreiben eigener Systeme ist kinderleicht.

Hier ist eine:

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

So verwendet:

>>> from hurry.filesize import size
>>> size(11000, system=mysystem)
'10 Bunches'
>>> size(198283722, system=mysystem)
'189 Heaps'
Lennart Regebro
quelle
1
Hm, jetzt brauche ich einen, um den anderen Weg zu gehen. Von "1 kb" bis 1024(ein int).
mlissner
2
Funktioniert nur in Python 2
e-info128
2
Dieses Paket mag cool sein, aber die seltsame Lizenz und die Tatsache, dass es keinen online verfügbaren Quellcode gibt, machen es zu etwas, das ich sehr gerne vermeiden würde. Und es scheint nur Python2 zu unterstützen.
Almog Cohen
1
@AlmogCohen die Quelle online ist, direkt von PyPI erhältlich (einige Pakete haben kein Github-Repository, nur eine PyPI-Seite) und die Lizenz ist nicht so dunkel, ZPL ist die Zope Public License, was nach meinem besten Wissen der Fall ist , BSD-ähnlich. Ich bin damit einverstanden, dass die Lizenzierung selbst seltsam ist: Es gibt weder eine Standarddatei 'LICENSE.txt' noch eine Präambel oben in jeder Quelldatei.
Sleblanc
1
Um Megabyte zu erhalten, habe ich die folgende Gleichung mit dem bitweisen Verschiebungsoperator durchgeführt: MBFACTOR = float(1 << 20); mb= int(size_in_bytes) / MBFACTOR @LennartRegebro
alper
146

Folgendes benutze ich:

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: Die Größe sollte in Bytes gesendet werden.

James
quelle
11
Wenn Sie die Größe in Bytes senden, fügen Sie einfach "B" als erstes Element von size_name hinzu.
TuxGurl
Wenn Sie ein Dateibyte der Größe 0 haben, schlägt dies fehl. log (0, 1024) ist nicht definiert! Sie sollten die 0-Byte-Groß- / Kleinschreibung vor dieser Anweisung überprüfen. I = int (math.floor (math.log (size, 1024))).
Genclik27
Genclik - du hast recht. Ich habe gerade eine kleinere Änderung eingereicht, die dies behebt und die Konvertierung von Bytes ermöglicht. Danke, Sapam, für das Original
FarmerGedden
HI @WHK als tuxGurl erwähnte, dass es eine einfache Lösung ist.
James
3
Eigentlich müssten die Größennamen sein ("B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"). Weitere Informationen finden Sie unter en.wikipedia.org/wiki/Mebibyte .
Alex
35

Anstelle eines Größenteilers von 1024 * 1024Ihnen könnten Sie den << bitweisen Verschiebungsoperator verwenden , dh 1<<20um Megabytes, 1<<30Gigabytes usw. zu erhalten.

Im einfachsten Szenario können Sie zB eine Konstante haben, MBFACTOR = float(1<<20)die dann mit Bytes verwendet werden kann, dh : megas = size_in_bytes/MBFACTOR.

Megabyte sind normalerweise alles, was Sie brauchen, oder auf andere Weise kann so etwas verwendet werden:

# 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
ccpizza
quelle
10
Ist es nicht >>?
Tjorriemorrie
2
@Tjorriemorrie: Es muss eine Verschiebung nach links sein, eine Verschiebung nach rechts wird das einzige Bit abfallen und dazu führen 0.
ccpizza
Geniale Antwort. Vielen Dank.
Borislav Aymaliev
Ich weiß, dass dies alt ist, aber wäre dies die richtige Verwendung? def convert_to_mb (data_b): print (data_b / (1 << 20))
roastbeeef
22

Hier ist die kompakte Funktion zur Berechnung der Größe

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

Weitere Informationen zur Ausgabe und umgekehrt finden Sie unter: http://code.activestate.com/recipes/578019-bytes-to-human-human-to-bytes-converter/

Pavan Gupta
quelle
12

Im Folgenden finden Sie eine schnelle und relativ einfach zu lesende Methode zum Drucken von Dateigrößen in einer einzigen Codezeile, wenn Sie bereits wissen, was Sie möchten. Diese Einzeiler kombinieren die großartige Antwort von @ccpizza oben mit einigen praktischen Formatierungstricks, die ich hier gelesen habe. Wie drucke ich Zahlen mit Kommas als Tausendertrennzeichen? .

Bytes

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

Gigabit

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

Offensichtlich gehen sie davon aus, dass Sie ungefähr wissen, mit welcher Größe Sie zu Beginn zu tun haben. In meinem Fall (Video-Editor bei South West London TV) ist dies MB und gelegentlich GB für Videoclips.


UPDATE MIT PATHLIB Als Antwort auf Hildys Kommentar ist hier mein Vorschlag für ein kompaktes Funktionspaar (Dinge "atomar" halten, anstatt sie zusammenzuführen), das nur die Python-Standardbibliothek verwendet:

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'
Peter F.
quelle
1
Das ist ein ziemlich kluger Weg, das zu tun. Ich frage mich, ob Sie diese in eine Funktion einfügen könnten, in der Sie übergeben, ob Sie KBs wollen. mb's und so weiter. Sie könnten sogar einen Eingabebefehl haben, der fragt, welchen Sie möchten, was sehr praktisch wäre, wenn Sie dies häufig tun.
Hildy
Siehe oben, Hildy ... Sie können auch die Wörterbuchzeile wie @ lennart-regebro anpassen, die oben beschrieben wurde ... was für die Speicherverwaltung nützlich sein kann, z. B. "Partition", "Cluster", "4 TB Festplatten", "DVD_RW", " Blu-Ray Disk "," 1 GB Speichersticks "oder was auch immer.
Peter F
Ich habe auch gerade Kb (Kilobit), Mb (Megabit) und Gb (Gigabit) hinzugefügt.
Peter F
9

Nur für den Fall, dass jemand nach der Umkehrung dieses Problems sucht (wie ich es sicher getan habe), funktioniert Folgendes für mich:

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
Romeo Mihalcea
quelle
Sie behandeln den Fall von Dezimalzahlen wie 1,5 GB nicht. Um dies zu beheben, ändern Sie einfach das << 10zu * 1024, << 20zu * 1024**2und << 30zu * 1024**3.
E235
3

Hier ist es:

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
Rhoitjadhav
quelle
2

Hier meine zwei Cent, die das Auf- und Abwerfen ermöglichen und anpassbare Präzision hinzufügen:

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)

Fügen Sie TBusw. hinzu, wie Sie möchten.

WesternGun
quelle
Ich werde darüber abstimmen, weil es nur mit der Python-Standardbibliothek ausgearbeitet werden kann
Ciasto piekarz
1

Hier ist eine Version, die der Ausgabe von ls -lh entspricht .

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
Keith
quelle
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)
Kamran Kausar
quelle
Dies ist in so vielen Einstellungen verwendbar. Ich bin froh, dass ich auf diesen Kommentar gestoßen bin. Danke vielmals.
Saurabh Jain
0

Hier ist meine Implementierung:

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)

Es werden bis zu drei Dezimalstellen gedruckt und nachgestellte Nullen und Punkte entfernt. Der boolesche Parameter sischaltet die Verwendung der 10-basierten gegenüber der 2-basierten Größengröße um.

Dies ist sein Gegenstück. Es ermöglicht das Schreiben sauberer Konfigurationsdateien wie {'maximum_filesize': from_filesize('10M'). Es wird eine Ganzzahl zurückgegeben, die sich der beabsichtigten Dateigröße annähert. Ich verwende keine Bitverschiebung, da der Quellwert eine Gleitkommazahl ist (es wird gut akzeptiert from_filesize('2.15M')). Das Konvertieren in eine Ganzzahl / Dezimalzahl würde funktionieren, macht den Code jedoch komplizierter und funktioniert bereits so wie er ist.

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)
Sleblanc
quelle
-1

Hier ist eine weitere Version der umgekehrten Implementierung von @ romeo, die eine einzelne Eingabezeichenfolge verarbeitet.

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)
Aaron Duke
quelle
-1

Ähnlich wie Aaron Dukes Antwort, aber eher "pythonisch";)

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)
nesh
quelle
-1

Ich bin neu in der Programmierung. Ich habe mir diese folgende Funktion ausgedacht, die eine bestimmte Dateigröße in ein lesbares Format konvertiert.

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]
user8637373
quelle
-2

Dies funktioniert korrekt für alle Dateigrößen:

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')))
Роман Арсеньев
quelle
3
War es wirklich wert, die Antwort von "Sapam" zu kopieren? Nein. Kommentieren Sie das nächste Mal.
Wütend 84
Eine andere Antwort muss woanders kopiert und dann wieder hierher kopiert werden ... wir empfehlen tatsächlich Originalantworten.
WesternGun