85位编码的字符串在python中解码时未正确处理

时间:2018-08-25 08:58:56

标签: python-3.x character-encoding pyspark

我已经编码了十六进制(设备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())

我正在解码此版本,该版本已转义。

1 个答案:

答案 0 :(得分:2)

ASCII85编码ID的 actual 值应为:

T,irMU)?YQSB"#'3>>Cq

该值中不应包含\。错误的是您将a85encode()结果转换为字符串:

str(bs64.a85encode(bytearray.fromhex(id)))[2:-1]

a85encode()返回一个bytes对象,您需要解码,以ASCII形式获得具有相同代码点的字符串值:

bs64.a85encode(bytearray.fromhex(id)).decode('ASCII')

str(bytesobject)为您提供了易于调试的表示形式,可以安全地粘贴回Python代码中,因此任何'字符都可以通过\进行转义面前。您不想将此表示形式用于序列化。

请注意,您不需要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() class在表示形式之间进行转换:

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'