Python struct反向解包

huangapple go评论83阅读模式
英文:

Python struct reverse unpack

问题

尝试以以下方式解包结构以获得 '0123456789':

b"\x10\x32\x54\x76\x98"

也许还有其他方法,而不是编写自己的函数来实现这个目标?

谢谢。

英文:

I'm trying to unpack structure as shown below in this way to get '0123456789'

b"\x10\x32\x54\x76\x98"

Maybe there is other way then writing own function to do so ?

Thanks

答案1

得分: 3

I think it's easier to debug if you use your own function. However, if you want a one-line command to turn this into a string you can do like so:

X = b"\x10\x32\x54\x76\x98";

tmp = "".join([hex(i)[:1:-1].zfill(2) for i in X])

print(tmp)

>>> 0123456789

Basically it turns each byte to hexadecimal (which handles splitting methods), so we split them to read them backward without "0x".

Finally, we join them using an empty string separator.

EDIT

As pointed by Alain T. I updated the code and added the zfill() method to force the string length of each byte to 2 to avoid missing integers like in the example:
b"\x10\x32\x05\x76\x98".

英文:

I think it's easier to debug if you use your own function. However, if you want a one line command to turn this into a string you can do like so:

X = b"\x10\x32\x54\x76\x98"

tmp = "".join([hex(i)[:1:-1].zfill(2) for i in X])

print(tmp)

>>> 0123456789

Basically it turns each byte to hexadecimal (which handles splitting methods), so we split them to read them backward without "0x".

Finally we join them using an empty string separator.

EDIT

As pointed by Alain T. I updated the code and added the zfill() method to force the string length of each byte to 2 to avoid missing integers like in the example:
b"\x10\x32\x05\x76\x98".

答案2

得分: 0

以下是翻译好的代码部分:

使用reduce函数可以在数学上获取一个整数

from functools import reduce

X = b"\x10\x32\x45\x76\x98"

d = reduce(lambda r,b:r*100+b%16*10+b//16,X,0)

print(d)
# 123546789

或者不使用库使用sum()和推导式

d = sum( (d%16*10+d//16)*100**i for i,d in enumerate(X[::-1]))

print(d)
# 123546789

如果需要它是一个带有前导零的字符串可以使用format方法

d = ("{:02x}"*len(X)).format(*X[::-1])[::-1]

print(d)
0123546789

或者使用join函数和推导式

d = "".join(f"{d%16}{d//16}" for b in X)

print(d)
0123546789

对于字符串输出不带前导零可以在将它们转换为整数之前交换每个字节的nibbles然后使用该整数的十六进制表示这是最快的字符串转换):

d = hex(int.from_bytes((b%16*16+b//16 for b in X),"big"))[2:]

print(d)
123546789

性能比较

tests = {
"reduce    ": lambda : reduce(lambda r,b:r*100+b%16*10+b//16,X,0),
"from_bytes": lambda : hex(int.from_bytes((b%16*16+b//16 for b in X),"big"))[2:],
"format    ": lambda : ("{:02x}"*len(X)).format(*X[::-1])[::-1],
"join      ": lambda : "".join(f"{d%16}{d//16}" for d in X),
"sum       ": lambda : sum( (d%16*10+d//16)*100**i for i,d in enumerate(X[::-1])),
}

from timeit import timeit

for name,fn in tests.items():
    print(name,fn(),timeit(fn,number=1000000))

reduce    	123546789	1.1987413900000001
from_bytes	123546789	1.245261141
format    	0123546789	1.517380332
join      	0123546789	1.8026392529999997
sum       	123546789	2.1293131119999993

请注意,这是代码的翻译版本,不包括问题中的其他内容。

英文:

You could get an integer mathematically using reduce:

from functools import reduce
X = b"\x10\x32\x45\x76\x98"
d = reduce(lambda r,b:r*100+b%16*10+b//16,X,0)
print(d)
# 123546789

Or, without libraries using sum() and a comprehension:

d = sum( (d%16*10+d//16)*100**i for i,d in enumerate(X[::-1]))
print(d)
# 123546789

If you need it to be a string, with leading zero, you can use the format method

d = ("{:02x}"*len(X)).format(*X[::-1])[::-1]
print(d)
0123546789

or the join function and a comprehension:

d = "".join(f"{d%16}{d//16}" for b in X)
print(d)
0123546789

For a string output, without leading zeros, you can swap the nibles of each byte before converting them to an integer and then use the hex representation of that integer (this is the fastest string conversion):

d = hex(int.from_bytes((b%16*16+b//16 for b in X),"big"))[2:]
print(d)
123546789

Performance comparisons:

tests = {
"reduce    ": lambda : reduce(lambda r,b:r*100+b%16*10+b//16,X,0),
"from_bytes": lambda : hex(int.from_bytes((b%16*16+b//16 for b in X),"big"))[2:],
"format    ": lambda : ("{:02x}"*len(X)).format(*X[::-1])[::-1],
"join      ": lambda : "".join(f"{d%16}{d//16}" for d in X),
"sum       ": lambda : sum( (d%16*10+d//16)*100**i for i,d in enumerate(X[::-1])),
}
from timeit import timeit
for name,fn in tests.items():
print(name,fn(),timeit(fn,number=1000000))
reduce    	123546789	1.1987413900000001
from_bytes	123546789	1.245261141
format    	0123546789	1.517380332
join      	0123546789	1.8026392529999997
sum       	123546789	2.1293131119999993

答案3

得分: 0

这里还有另一种只使用 f-strings 和一点算术运算来完成的方法:

X = b"\x10\x32\x54\x76\x98"
for b in X:
print(f'{b%16}{b//16}', end='') 

输出:

0123456789

注意:

如果你需要一个单独的字符串,那么在循环内构建它,而不是打印。

英文:

Here's yet another way to do this just using f-strings and a little bit of arithmetic:

X = b"\x10\x32\x54\x76\x98"
for b in X:
print(f'{b%16}{b//16}', end='')

Output:

0123456789

Note:

If you need a single string then just build it within the loop instead of printing

huangapple
  • 本文由 发表于 2023年6月26日 20:09:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/76556544.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定