[英]How to encode a long in Base64 in Python?
在Java中,我可以將BigInteger
編碼為:
java.math.BigInteger bi = new java.math.BigInteger("65537L");
String encoded = Base64.encodeBytes(bi.toByteArray(), Base64.ENCODE|Base64.DONT_GUNZIP);
// result: 65537L encodes as "AQAB" in Base64
byte[] decoded = Base64.decode(encoded, Base64.DECODE|Base64.DONT_GUNZIP);
java.math.BigInteger back = new java.math.BigInteger(decoded);
在C#中:
System.Numerics.BigInteger bi = new System.Numerics.BigInteger("65537L");
string encoded = Convert.ToBase64(bi);
byte[] decoded = Convert.FromBase64String(encoded);
System.Numerics.BigInteger back = new System.Numerics.BigInteger(decoded);
如何在Python中將長整數編碼為Base64編碼的字符串? 到目前為止我所嘗試的結果與其他語言的實現不同(到目前為止我已經嘗試過Java和C#),特別是它生成了更長的Base64編碼字符串。
import struct
encoded = struct.pack('I', (1<<16)+1).encode('base64')[:-1]
# produces a longer string, 'AQABAA==' instead of the expected 'AQAB'
當使用此Python代碼生成Base64編碼的字符串時,Java中生成的解碼整數(例如)生成16777472
而不是預期的65537
。 首先,我錯過了什么?
其次,我必須手工弄清楚struct.pack
使用的長度格式是什么; 如果我試圖編碼一個長數字(大於(1<<64)-1
),那么'Q'
格式規范太短而無法保存表示。 這是否意味着我必須手工完成表示,或者struct.pack
函數是否有未記錄的格式說明符? (我沒有被迫使用struct
,但乍一看似乎做了我需要的東西。)
有關將整數轉換為base64的信息,請查看此頁面。
import base64
import struct
def encode(n):
data = struct.pack('<Q', n).rstrip('\x00')
if len(data)==0:
data = '\x00'
s = base64.urlsafe_b64encode(data).rstrip('=')
return s
def decode(s):
data = base64.urlsafe_b64decode(s + '==')
n = struct.unpack('<Q', data + '\x00'* (8-len(data)) )
return n[0]
struct
模塊 :
...執行Python值和表示為Python字符串的C結構之間的轉換。
因為C沒有無限長度的整數,所以沒有包裝它們的功能。
但是寫自己很容易。 例如:
def pack_bigint(i):
b = bytearray()
while i:
b.append(i & 0xFF)
i >>= 8
return b
要么:
def pack_bigint(i):
bl = (i.bit_length() + 7) // 8
fmt = '<{}B'.format(bl)
# ...
等等。
當然,你會想要一個unpack
函數,比如評論中的jbatista:
def unpack_bigint(b):
b = bytearray(b) # in case you're passing in a bytes/str
return sum((1 << (bi*8)) * bb for (bi, bb) in enumerate(b))
這有點晚了,但我想我會戴上帽子:
def inttob64(n):
"""
Given an integer returns the base64 encoded version of it (no trailing ==)
"""
parts = []
while n:
parts.insert(0,n & limit)
n >>= 32
data = struct.pack('>' + 'L'*len(parts),*parts)
s = base64.urlsafe_b64encode(data).rstrip('=')
return s
def b64toint(s):
"""
Given a string with a base64 encoded value, return the integer representation
of it
"""
data = base64.urlsafe_b64decode(s + '==')
n = 0
while data:
n <<= 32
(toor,) = struct.unpack('>L',data[:4])
n |= toor & 0xffffffff
data = data[4:]
return n
這些函數將一個任意大小的長數字轉換為/從big-endian base64表示。
這可能會有所幫助。 我沒有使用struct.pack()
而是構建一個字節字符串進行編碼,然后在其上調用BASE64編碼。 我沒有編寫解碼,但顯然解碼可以恢復相同的字節串,循環可以恢復原始值。 我不知道你是否需要固定大小的整數(總是128位),我不知道你是否需要Big Endian,所以我為你留下了解碼器。
此外, encode64()
和decode64()
來自@ msc的答案,但修改為有效。
import base64
import struct
def encode64(n):
data = struct.pack('<Q', n).rstrip('\x00')
if len(data)==0:
data = '\x00'
s = base64.urlsafe_b64encode(data).rstrip('=')
return s
def decode64(s):
data = base64.urlsafe_b64decode(s + '==')
n = struct.unpack('<Q', data + '\x00'* (8-len(data)) )
return n[0]
def encode(n, big_endian=False):
lst = []
while True:
n, lsb = divmod(n, 0x100)
lst.append(chr(lsb))
if not n:
break
if big_endian:
# I have not tested Big Endian mode, and it may need to have
# some initial zero bytes prepended; like, if the integer is
# supposed to be a 128-bit integer, and you encode a 1, you
# would need this to have 15 leading zero bytes.
initial_zero_bytes = '\x00' * 2
data = initial_zero_bytes + ''.join(reversed(lst))
else:
data = ''.join(lst)
s = base64.urlsafe_b64encode(data).rstrip('=')
return s
print encode(1234567890098765432112345678900987654321)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.