【问题标题】:bytes to human readable, and back. without data loss字节到人类可读,然后返回。无数据丢失
【发布时间】:2012-10-31 21:01:18
【问题描述】:

我需要将包含内存使用情况的字符串(例如:1048576(即 1M))转换为人类可读的版本,反之亦然。

注意:我已经看过这里了: Reusable library to get human readable version of file size?

在这里(即使它不是 python): How to convert human readable memory size into bytes?

到目前为止没有任何帮助,所以我在别处寻找。

我在这里找到了适合我的东西:http://code.google.com/p/pyftpdlib/source/browse/trunk/test/bench.py?spec=svn984&r=984#137,或者,对于较短的 URL:http://goo.gl/zeJZl

代码:

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)

还有一个以其他方式进行转换的功能(同一站点):

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

这很好,但它有一些信息丢失,例如:

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

为了解决这个问题,我更改了函数 bytes2human 的格式

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

哪个更好,给我这些结果:

>>> bytes2human(10000)
'9.766K'

但是当我尝试使用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

这是因为.

所以我的问题是,我怎样才能将人类可读的版本转换回字节版本,而不会丢失数据?

注意:我知道小数点后 3 位也是一点点数据丢失。但就这个问题而言,让我们暂时忽略这一点,我总是可以将其更改为更大的内容。

【问题讨论】:

    标签: python memory converter human-readable


    【解决方案1】:

    事实证明,答案比我想象的要简单得多——我提供的链接之一实际上导致了function 的更详细版本:

    它能够处理我给它的任何范围。

    但感谢您的帮助:

    这里复制的代码供后人使用:

    ## {{{ 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/ }}}
    

    【讨论】:

      【解决方案2】:

      你在最后一个笔记中几乎回答了你自己的问题,那里。

      human2bytes(s) 中,输入字符串——例如9.766K——被分成两部分,数字和前缀。在断言之后(正如您正确观察到的是引发错误的原因),数字乘以前缀表示的相应值,即9.766 * 1000 = 9766。 “避免”数据丢失的唯一方法是接受足够精确的浮点值作为输入。

      为了使human2bytes 接受浮点输入,您可以从断言中删除num.isdigit(),然后使用try-except 或check it by some other means 包装类型转换num = float(num)

      【讨论】:

      • +1 用于提高精确浮点值的点。您基本上无法在两者之间执行对称转换 - 为了简洁起见,人类可读的形式会截断值。
      猜你喜欢
      • 2020-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-07
      • 2020-06-18
      • 2015-09-26
      • 2017-07-07
      相关资源
      最近更新 更多