python-3.x - 在 python 中解码时,85 位编码的字符串没有被正确处理
问题描述
我已经编码了一个十六进制(设备 ID):
9F1D8E8BA2194CD29CC744083914535A
转化为 85 位编码数字(等效转换):
T,irMU)?YQSB"#\'3>>Cq
这些都存储在 spark 数据框中。
问题是当我尝试将其转换回来(将列传递给 UDF)时,它没有返回设备 ID,在调试时我发现它给出的输出实际上是:
T,irMU)?YQSB"#\\\'3>>Cq
这意味着实际的字符串会自动添加转义字符,然后对其进行解码。
这是 udf,我为转换而写的:
def convert_id(id):
id = id.replace("-", "")
return str(bs64.a85encode(bytearray.fromhex(id)))[2:-1]
udf_convert_id = udf(convert_id, StringType())
这是为了解码它:
def convert_docid2idfa(docId):
try:
docId = docId.replace('\\\\','\\')
id_str = bs64.a85decode(docId).hex()
idfa = id_str[:8]+"-"+id_str[8:12]+"-"+id_str[12:16]+"-"+id_str[16:20]+"-"+id_str[20:]
return idfa
except:
return docId
convert_docid2idfa_udf = udf(convert_docid2idfa, StringType())
我正在解码这个版本,它有逃逸。
解决方案
ASCII85 编码的 ID的实际值应该是:
T,irMU)?YQSB"#'3>>Cq
值中不应有 a \
。错误是您将a85encode()
结果转换为字符串:
str(bs64.a85encode(bytearray.fromhex(id)))[2:-1]
a85encode()
返回一个bytes
对象,您需要将其解码为 ASCII,以获得具有相同代码点的字符串值:
bs64.a85encode(bytearray.fromhex(id)).decode('ASCII')
str(bytesobject)
为您提供了一个调试友好的表示形式,可以安全地粘贴回 Python 代码中,因此任何字符都在前面'
加上 a 进行转义。\
您不想使用此表示进行序列化。
请注意,您不需要bytearray
一个常规的不可变bytes
对象就足以将十六进制 ID 解码为二进制字符串:
bs64.a85encode(bytes.fromhex(id)).decode('ASCII')
演示:
>>> import base64 as b64
>>> id = '9F1D8E8BA2194CD29CC744083914535A'
>>> encoded = bs64.a85encode(bytes.fromhex(id)).decode('ASCII')
>>> print(encoded)
T,irMU)?YQSB"#'3>>Cq
>>> b64.a85decode(encoded).hex()
'9f1d8e8ba2194cd29cc744083914535a'
如果您无法修复编码,您仍然可以使用unicode_escape
编解码器修复损坏的值;首先将字符串编码为 ASCII。您应该能够通过测试长度来检测这种损坏的值,一个 20 字节的 IDFA 应该总是产生一个 20 个字符的 ASCII85 字符串,任何更长的都需要修复:
if len(docId) > 20:
docId = docId.encode('ascii').decode('unicode_escape')
decoded = b64.a85decode(docId).hex()
以上修复了通过调用str()
字节对象引入的转义:
>>> encoded
'T,irMU)?YQSB"#\'3>>Cq'
>>> botched = str(encoded.encode('ascii'))[2:-1]
>>> botched
'T,irMU)?YQSB"#\\\'3>>Cq'
>>> botched.encode('ascii').decode('unicode_escape')
'T,irMU)?YQSB"#\'3>>Cq'
>>> bs64.a85decode(botched.encode('ascii').decode('unicode_escape')).hex()
'9f1d8e8ba2194cd29cc744083914535a'
请注意,如果您正在使用 IDFA 值,则可以使用uuid.UUID()
该类在表示之间进行转换:
from uuid import UUID
bs64.a85encode(UUID(hex=id).bytes).decode('ASCII')
编码,和
str(UUID(bytes=bs64.a85decode(docId)))
回到带有破折号的 8-4-4-4-12 十六进制字符串:
>>> from uuid import UUID
>>> id = '9F1D8E8B-A219-4CD2-9CC7-44083914535A'
>>> encoded = bs64.a85encode(UUID(hex=id).bytes).decode('ASCII')
>>> encoded
'T,irMU)?YQSB"#\'3>>Cq'
>>> str(UUID(bytes=bs64.a85decode(encoded)))
'9f1d8e8b-a219-4cd2-9cc7-44083914535a'
推荐阅读
- algorithm - 选择排序算法的改进?
- python - 使用 sphinx 记录 django rest 框架
- sql - SQL Server ROUND 不工作
- geospatial - CQL 过滤多个类型名以检索指定几何中的特征
- c++ - std::string::find 为取消引用的迭代器和等效的字符串文字返回不同的值
- c++ - 用字符串中的更多字符替换一个字符,而不删除 C++ 中的其他字母
- c# - 从 C# 中的对象中删除特定属性
- list - 如何在使用 lambda、Java 的列表迭代结束时返回值
- ios - 如果答案不正确,防止警报操作解除 UIAlertController - Swift 4
- phpunit - 测试状态码需要 500 而不是 200