在将binay转换为float时,python struct“unpack需要一个长度为8的字节对象”。

时间:2022-07-12 18:18:09

I'm currently trying to transform binaries into floats and viceversa using the struct module. My function works with certain values, like 2.0 or 14.0, but it doesn't with values like 1.0.

我目前正在尝试使用struct模块将二进制文件转换为浮点数和viceversa。我的函数使用某些值,比如2.0或14.0,但它没有1.0这样的值。

I took the code from another question, and changed it from python2 to python3.

我从另一个问题中提取了代码,并将它从python2改为python3。

import struct

def bin_to_float(b):
    """ convert binary string to float """
    bf = int_to_bytes(int(b, 2), 8)  # 8 bytes needed for IEEE 754 binary64
    bf = bytes(bf, 'UTF-8')
    print(bf)
    return struct.unpack('<d', bf)[0]

def int_to_bytes(n, minlen):  # helper function
    """ int/long to byte string """
    nbits = n.bit_length() + (1 if n < 0 else 0)  # plus one for any sign bit
    nbytes = (nbits+7)//8  # number of whole bytes
    bytes = []
    for i in range(nbytes):
        bytes.append(chr(n & 0xff))
        n >>= 8   
    # zero pad if needed
    if minlen > 0 and len(bytes) < minlen:
        bytes.extend((minlen-len(bytes)) * '0')
    bytes.reverse()  # put high bytes at beginning
    return ''.join(bytes)

# tests

def float_to_bin(f):
    """ convert float to binary string """
    ba = struct.pack('>d', f)
    s = ''.join('{:08b}'.format(b) for b in ba)
    # strip off leading zeros
    for i in range(len(s)):
        if s[i] != '0':
             break
    else:  # all zeros
        s = '0'
        i = 0
    return s[i:]

import math

floats = [2.0, 1.0, -14.0, 12.546, math.pi]


for f in floats:
    binary = float_to_bin(f)
    print ('float_to_bin(%f): %r' % (f, binary))
    float = bin_to_float(binary)
    print ('bin_to_float(%r): %f' % (binary, float))
    print ()

The problem seems to be that when I encode the str in bytes, I get 9 bytes instead of 8, but this happens only sometimes. This is the console readings:

问题似乎是,当我用字节来编码str时,我得到9个字节而不是8个字节,但这只是偶尔发生。这是控制台的读数:

float_to_bin(2.000000): '100000000000000000000000000000000000000000000000000000000000000'
b'@\x00\x00\x00\x00\x00\x00\x00'
bin_to_float('100000000000000000000000000000000000000000000000000000000000000'): 0.000000

float_to_bin(1.000000): '11111111110000000000000000000000000000000000000000000000000000'
b'?\xc3\xb0\x00\x00\x00\x00\x00\x00'
Traceback (most recent call last):
  File "C:/Users/arzuffi pc test/Desktop/prova struct.py", line 47, in <module>
    float = bin_to_float(binary)
  File "C:/Users/arzuffi pc test/Desktop/prova struct.py", line 8, in bin_to_float
    return struct.unpack('<d', bf)[0]
struct.error: unpack requires a bytes object of length 8

I can't seem to figure out why this happens and how to prevent it from happening :/

我似乎弄不明白为什么会发生这种情况,以及如何防止这种情况发生。

Does anybody knows why this is?

有人知道为什么吗?

Thank you very much in advance.

非常感谢你的帮助。

EDIT: J.J. Hakala answered brilliantly, so I'll post the answer up here:

编辑:J.J.哈卡拉出色地回答了,所以我将在这里张贴答案:

import struct

def bin_to_float(b):
    """ convert binary string to float """
    return struct.unpack('<d', struct.pack('<Q', int(b, 2)))[0]

def float_to_bin(f):
    """ convert float to binary string """
    return '{:b}'.format(struct.unpack('<Q', struct.pack('<d', f))[0])

If someone wants to do the same with 32 bits float, this should be good:

如果有人想用32位浮点运算,这应该很好:

import struct

def bin_to_float(b):
    """ convert binary string to float """
    return struct.unpack('<f', struct.pack('<L', int(b, 2)))[0]
def float_to_bin(f):
    """ convert float to binary string """
    return '{:b}'.format(struct.unpack('<L', struct.pack('<f',f))[0])

Thank you very much everybody!!!

非常感谢大家!!!

3 个解决方案

#1


2  

struct.pack / struct.unpack could be used to implement the functions like this

结构体。包/结构体。unpack可以用来实现这样的功能。

def bin_to_float(b):
    return struct.unpack('<d', struct.pack('<Q', int(b, 2)))[0]

def float_to_bin(f):
    return '{:b}'.format(struct.unpack('<Q', struct.pack('<d', f))[0])

As for the original question,

至于最初的问题,

bf = bytes(bf, 'UTF-8')

seems to be the culprit since it may change the length of bf.

似乎是罪魁祸首,因为它可能改变bf的长度。

#2


2  

Not sure why you're going to all this trouble really. There are much easier ways to perform this sort of conversion:

不知道你为什么要这么麻烦。有更简单的方法来执行这种转换:

import ctypes

# Create pointer classes once up front for brevity/performance later
# Assumes ctypes.sizeof(ctypes.c_double) is 8 bytes; could add assert for this
PDOUBLE = ctypes.POINTER(ctypes.c_double)
PU64 = ctypes.POINTER(ctypes.c_uint64)

def float_to_bin(f):
    d = ctypes.c_double(f)       # Convert to true C double
    pd = PDOUBLE(d)              # Make pointer to it
    pu64 = ctypes.cast(pd, PU64) # Cast pointer to unsigned int type of same size
    return '{:b}'.format(pu64[0]) # Read value as unsigned int and convert to bin

def bin_to_float(b):
    u64 = ctypes.c_uint64(int(b, 2)) # Convert bin form to unsigned int
    pu64 = PU64(ul)                  # Make pointer to it
    pd = ctypes.cast(pu64, PDOUBLE)  # Cast pointer to double pointer
    return pd[0]                     # Read double value as Python float

It could be even shorter, but I wanted to limit the work on each line for illustration purposes.

它可能更短,但我想限制每一行的工作以作说明。

The precise cause of your problem is a little unclear, but the constant switching between bytes and str, and the trickiness of padding and unpadding properly, the possible off-by-one errors in stuff like calculating the precise bit and byte lengths, etc. could all be messing this up. The code above does the job much more directly and portably, without needing to fiddle with the nitty gritty.

您的问题的确切原因有一点不清楚,但是字节和str之间的持续切换,以及适当的填充和不填充的欺骗,在计算精确的位和字节长度等方面可能会出现的错误,都可能会把这个问题搞得一团糟。上面的代码可以更直接、更方便地完成工作,而不需要修改细节。

#3


0  

If you are using structure in storing value in the binary file, to avoid

如果在二进制文件中使用结构存储值,则要避免。

struct.error: unpack requires a bytes object of length ...

you may do this.

你可以这样做。

with open('file_name.txt', mode='rb') as file:

    v = 0 # Is a variable starts from 0
    size_ = 9 # It is the size of structure. In this case('ff?') 4 + 4 + 1 = 9

    while True:
        file.seek(v * size_)

        try:
            data = unpack('ff?', file.read(size_))
            print(data)

        except:
            print('File ends')
            break

        v += 1

Here size_ is a variable depends on the size you pack. Since you are getting

这里size_是一个变量,取决于您所包的大小。因为你越来越

'struct.error: unpack requires a bytes object of length 8'

size_ = 8.

size_ = 8。

#1


2  

struct.pack / struct.unpack could be used to implement the functions like this

结构体。包/结构体。unpack可以用来实现这样的功能。

def bin_to_float(b):
    return struct.unpack('<d', struct.pack('<Q', int(b, 2)))[0]

def float_to_bin(f):
    return '{:b}'.format(struct.unpack('<Q', struct.pack('<d', f))[0])

As for the original question,

至于最初的问题,

bf = bytes(bf, 'UTF-8')

seems to be the culprit since it may change the length of bf.

似乎是罪魁祸首,因为它可能改变bf的长度。

#2


2  

Not sure why you're going to all this trouble really. There are much easier ways to perform this sort of conversion:

不知道你为什么要这么麻烦。有更简单的方法来执行这种转换:

import ctypes

# Create pointer classes once up front for brevity/performance later
# Assumes ctypes.sizeof(ctypes.c_double) is 8 bytes; could add assert for this
PDOUBLE = ctypes.POINTER(ctypes.c_double)
PU64 = ctypes.POINTER(ctypes.c_uint64)

def float_to_bin(f):
    d = ctypes.c_double(f)       # Convert to true C double
    pd = PDOUBLE(d)              # Make pointer to it
    pu64 = ctypes.cast(pd, PU64) # Cast pointer to unsigned int type of same size
    return '{:b}'.format(pu64[0]) # Read value as unsigned int and convert to bin

def bin_to_float(b):
    u64 = ctypes.c_uint64(int(b, 2)) # Convert bin form to unsigned int
    pu64 = PU64(ul)                  # Make pointer to it
    pd = ctypes.cast(pu64, PDOUBLE)  # Cast pointer to double pointer
    return pd[0]                     # Read double value as Python float

It could be even shorter, but I wanted to limit the work on each line for illustration purposes.

它可能更短,但我想限制每一行的工作以作说明。

The precise cause of your problem is a little unclear, but the constant switching between bytes and str, and the trickiness of padding and unpadding properly, the possible off-by-one errors in stuff like calculating the precise bit and byte lengths, etc. could all be messing this up. The code above does the job much more directly and portably, without needing to fiddle with the nitty gritty.

您的问题的确切原因有一点不清楚,但是字节和str之间的持续切换,以及适当的填充和不填充的欺骗,在计算精确的位和字节长度等方面可能会出现的错误,都可能会把这个问题搞得一团糟。上面的代码可以更直接、更方便地完成工作,而不需要修改细节。

#3


0  

If you are using structure in storing value in the binary file, to avoid

如果在二进制文件中使用结构存储值,则要避免。

struct.error: unpack requires a bytes object of length ...

you may do this.

你可以这样做。

with open('file_name.txt', mode='rb') as file:

    v = 0 # Is a variable starts from 0
    size_ = 9 # It is the size of structure. In this case('ff?') 4 + 4 + 1 = 9

    while True:
        file.seek(v * size_)

        try:
            data = unpack('ff?', file.read(size_))
            print(data)

        except:
            print('File ends')
            break

        v += 1

Here size_ is a variable depends on the size you pack. Since you are getting

这里size_是一个变量,取决于您所包的大小。因为你越来越

'struct.error: unpack requires a bytes object of length 8'

size_ = 8.

size_ = 8。