2012-11-12 14 views
8

Ho bisogno di convertire stringhe che contengono l'utilizzo della memoria in byte, come ad esempio: 1048576 (che è 1M) esattamente in quella, una versione leggibile dall'uomo e visa-versa.byte a lettura umana e ritorno. senza perdita di dati

Nota: Ho guardato già qui: Reusable library to get human readable version of file size?

E qui (anche se non è python): How to convert human readable memory size into bytes?

Nulla fino ad ora mi ha aiutato, così ho guardato altrove.

Ho trovato qualcosa che fa questo per me qui: http://code.google.com/p/pyftpdlib/source/browse/trunk/test/bench.py?spec=svn984&r=984#137 o, per brevi URL: http://goo.gl/zeJZl

Il Codice:

def bytes2human(n, format="%(value)i%(symbol)s"): 
    """ 
    >>> bytes2human(10000) 
    '9K' 
    >>> bytes2human(100001221) 
    '95M' 
    """ 
    symbols = ('B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') 
    prefix = {} 
    for i, s in enumerate(symbols[1:]): 
     prefix[s] = 1 << (i+1)*10 
    for symbol in reversed(symbols[1:]): 
     if n >= prefix[symbol]: 
      value = float(n)/prefix[symbol] 
      return format % locals() 
    return format % dict(symbol=symbols[0], value=n) 

E anche una funzione per la conversione nella direzione opposta (stesso sito) :

def human2bytes(s): 
    """ 
    >>> human2bytes('1M') 
    1048576 
    >>> human2bytes('1G') 
    1073741824 
    """ 
    symbols = ('B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') 
    letter = s[-1:].strip().upper() 
    num = s[:-1] 
    assert num.isdigit() and letter in symbols 
    num = float(num) 
    prefix = {symbols[0]:1} 
    for i, s in enumerate(symbols[1:]): 
     prefix[s] = 1 << (i+1)*10 
    return int(num * prefix[letter]) 

Questo è grande e tutto, ma ha una certa perdita di informazioni, ad esempio:

>>> bytes2human(10000) 
'9K' 
>>> human2bytes('9K') 
9216 

Per cercare di risolvere questo, a cambiare la formattazione sulla funzione bytes2human

Into: format="%(value).3f%(symbol)s")

che è più bello, che mi dà questi risultati:

>>> bytes2human(10000) 
'9.766K' 

ma quando Provo a riconvertirli con la funzione human2bytes:

>>> human2bytes('9.766K') 

Traceback (most recent call last): 
    File "<pyshell#366>", line 1, in <module> 
    human2bytes('9.766K') 
    File "<pyshell#359>", line 12, in human2bytes 
    assert num.isdigit() and letter in symbols 
AssertionError 

Questo è a causa del .

Quindi la mia domanda è: come posso convertire una versione leggibile di nuovo in byte-versione, senza perdita di dati?

Nota: so che 3 decimali è anche un po 'di perdita di dati. Ma ai fini di questa domanda, ignoriamo che per ora, posso sempre cambiarlo in qualcosa di più grande.

risposta

5

Così si scopre la risposta è stata molto più semplice di quanto pensassi - uno dei link che ho fornito effettivamente portato ad una versione molto più dettagliata della function:

che è in grado di affrontare qualsiasi ambito dò esso.

Ma grazie per il vostro aiuto:

Il codice copiato qui per i posteri:

## {{{ http://code.activestate.com/recipes/578019/ (r15) 
#!/usr/bin/env python 

""" 
Bytes-to-human/human-to-bytes converter. 
Based on: http://goo.gl/kTQMs 
Working with Python 2.x and 3.x. 

Author: Giampaolo Rodola' <g.rodola [AT] gmail [DOT] com> 
License: MIT 
""" 

# see: http://goo.gl/kTQMs 
SYMBOLS = { 
    'customary'  : ('B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'), 
    'customary_ext' : ('byte', 'kilo', 'mega', 'giga', 'tera', 'peta', 'exa', 
         'zetta', 'iotta'), 
    'iec'   : ('Bi', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'), 
    'iec_ext'  : ('byte', 'kibi', 'mebi', 'gibi', 'tebi', 'pebi', 'exbi', 
         'zebi', 'yobi'), 
} 

def bytes2human(n, format='%(value).1f %(symbol)s', symbols='customary'): 
    """ 
    Convert n bytes into a human readable string based on format. 
    symbols can be either "customary", "customary_ext", "iec" or "iec_ext", 
    see: http://goo.gl/kTQMs 

     >>> bytes2human(0) 
     '0.0 B' 
     >>> bytes2human(0.9) 
     '0.0 B' 
     >>> bytes2human(1) 
     '1.0 B' 
     >>> bytes2human(1.9) 
     '1.0 B' 
     >>> bytes2human(1024) 
     '1.0 K' 
     >>> bytes2human(1048576) 
     '1.0 M' 
     >>> bytes2human(1099511627776127398123789121) 
     '909.5 Y' 

     >>> bytes2human(9856, symbols="customary") 
     '9.6 K' 
     >>> bytes2human(9856, symbols="customary_ext") 
     '9.6 kilo' 
     >>> bytes2human(9856, symbols="iec") 
     '9.6 Ki' 
     >>> bytes2human(9856, symbols="iec_ext") 
     '9.6 kibi' 

     >>> bytes2human(10000, "%(value).1f %(symbol)s/sec") 
     '9.8 K/sec' 

     >>> # precision can be adjusted by playing with %f operator 
     >>> bytes2human(10000, format="%(value).5f %(symbol)s") 
     '9.76562 K' 
    """ 
    n = int(n) 
    if n < 0: 
     raise ValueError("n < 0") 
    symbols = SYMBOLS[symbols] 
    prefix = {} 
    for i, s in enumerate(symbols[1:]): 
     prefix[s] = 1 << (i+1)*10 
    for symbol in reversed(symbols[1:]): 
     if n >= prefix[symbol]: 
      value = float(n)/prefix[symbol] 
      return format % locals() 
    return format % dict(symbol=symbols[0], value=n) 

def human2bytes(s): 
    """ 
    Attempts to guess the string format based on default symbols 
    set and return the corresponding bytes as an integer. 
    When unable to recognize the format ValueError is raised. 

     >>> human2bytes('0 B') 
     0 
     >>> human2bytes('1 K') 
     1024 
     >>> human2bytes('1 M') 
     1048576 
     >>> human2bytes('1 Gi') 
     1073741824 
     >>> human2bytes('1 tera') 
     1099511627776 

     >>> human2bytes('0.5kilo') 
     512 
     >>> human2bytes('0.1 byte') 
     0 
     >>> human2bytes('1 k') # k is an alias for K 
     1024 
     >>> human2bytes('12 foo') 
     Traceback (most recent call last): 
      ... 
     ValueError: can't interpret '12 foo' 
    """ 
    init = s 
    num = "" 
    while s and s[0:1].isdigit() or s[0:1] == '.': 
     num += s[0] 
     s = s[1:] 
    num = float(num) 
    letter = s.strip() 
    for name, sset in SYMBOLS.items(): 
     if letter in sset: 
      break 
    else: 
     if letter == 'k': 
      # treat 'k' as an alias for 'K' as per: http://goo.gl/kTQMs 
      sset = SYMBOLS['customary'] 
      letter = letter.upper() 
     else: 
      raise ValueError("can't interpret %r" % init) 
    prefix = {sset[0]:1} 
    for i, s in enumerate(sset[1:]): 
     prefix[s] = 1 << (i+1)*10 
    return int(num * prefix[letter]) 


if __name__ == "__main__": 
    import doctest 
    doctest.testmod() 
## end of http://code.activestate.com/recipes/578019/ }}} 
4

Stai praticamente rispondendo alla tua stessa domanda nella tua ultima nota, lì.

In human2bytes(s), la stringa di input - 9.766K per esempio - è divisa in due parti, il numero e il prefisso. Dopo l'asserzione (che come si osserva correttamente è ciò che genera l'errore), il numero viene moltiplicato per il valore corrispondente che rappresenta il prefisso, quindi 9.766 * 1000 = 9766. L'unico modo per "evitare" la perdita di dati è accettare un valore in virgola mobile sufficientemente preciso come input.

Al fine di rendere human2bytes accettare input in virgola mobile, si potrebbe rimuovere num.isdigit() dal affermazione e poi avvolgere la fusione di caratteri num = float(num) con try-tranne, o check it by some other means.

+0

+1 per l'aumento del punto circa preciso valore a virgola mobile. Fondamentalmente non puoi eseguire una traduzione simmetrica tra i due: la forma leggibile dall'uomo tronca i valori per brevità. – synthesizerpatel

Problemi correlati