2020/10/15更新:之前的代码错了:)
之前只对十进制列表算过编码,昨天试了下对字符串直接编码,然后发现结果很神奇。
我用一段伪随机生成的核酸序列举例吧(用random做的。搞生信,理解一下)
>>> from reedsolo import RSCodec
>>> rsc = RSCodec(6)
>>> ans = rsc.encode(b"CtttaAatUAGTTtUG")
>>> ans
bytearray(b'CtttaAatUAGTTtUG_$\xaf\xa5x>')
可以看到后面有奇怪的字符。这里解释一下,\x表示后面两位是十六进制。
同时,我也刚发现,原来对十进制进行编码时,也有问题,比如:
>>> from reedsolo import RSCodec
>>> rsd = RSCodec(6)
>>> rsd.encode([9,1,2,9]*4)
bytearray(b'\t\x01\x02\t\t\x01\x02\t\t\x01\x02\t\t\x01\x02\tQ\x7f_\xa3=\xef')
那么,当有9出现的时候,我下面的编码解码都是错的。也许还有别的数字也有这样的问题。
我当前的策略是按位解析。。。对单字符和双字符分别解析。
def _rsEncode(data, n):
'''
n 是实际编码长度,16 for example
dat = bytearray(b'CtttaAatUAGTTtUG_$\xaf\xa5x>')
:param data: bytearray
:param n:
:return:
'''
data = str(bytes(data))[2:-1]
tail = data[n:]
pos = tail.find("\\x")
i = 0
result = ''
while i < len(tail):
#print("start: " + str(i))
if i == pos:
# 取两位字符
result += "." + tail[i+2:i+4]
i += 4
pos = tail.find("\\x", i)
else:
...
i += 1
return result
需要注意的是,\和\t其实都是一个字符。
之前这篇 差错控制-reed-solomon编码 我鸽了,可能要 很久 才有空补了。当前业务上使用场景有限,现在只用一个 reedsolo 包足够了。
安装
直接pip 官方例子
编码
from reedsolo import RSCodec
def _rsEncode(data, n):
'''
Encode input data with Reed-Solomon codes. n bits for error correcting.
:param data: input data, like "b'@\\x16\\x10\\xec\\x11\\xec\\x11\\xda(\\xf3\\xd4BG\\x96'"
:param n: Length of result list.
:return: A list of error correcting values. Decimal.
'''
res = []
# remove b' and '
temp = str(bytes(data))[2:-1]
li = temp.split("\\x")
# only retain encoded values
for i in li[::-1]:
size = len(i)
if size == 1:
res.append(ord(i))
else:
j = i[2:]
# first 2 digits are hex
temp_li = [int('0' + i[:2], 16)]
while len(j) > 0: # single char
temp_li.append(ord(j[0]))
j = j[1:]
# reverse
res.extend(temp_li[::-1])
if len(res) >= n:
break
return res[:n][::-1]
rsc = RSCodec(7)
# 我这个data_list是一个十进制整数列表
encode_ = rsc.encode(data_list)
# _rsEncode帮我把【纠错码】,也就是尾部附加的那些数字转为十进制
rs_tail = _rsEncode(encode_, 7)
解码
下面这个函数将十进制整数的list(业务数据附加纠错码)转为字符串。
def _rsDecode(data, n, m):
'''
Reed-Solomon Decoding.
:param data: decimal list.
:param n: length of ecc. (bytes)
:param m: length of bussiness data. (bytes)
:return: utf-8 string
'''
rsd = RSCodec(n)
data = rsd.decode(data)[:m]
hex_ = ""
for i in data:
hex_ += "{:02X}".format(i)
# need to remove '@' at head
hex_ = hex_[3:]
while hex_[-1:] != "0":
hex_ = hex_[:-2]
hex_ = hex_[:-1]
return bytearray.fromhex(hex_).decode("utf-8")
注意
- RS的纠错能力是有限的,纠错码用几位要根据实际需求来。默认的是GF(2^8),也就是说,对于0~255的值是ok的。256以下就是8位表示嘛,所以如果超过8位,函数会自动切块,不保证速度。键盘上常用字符其实是够用的了。
>> rsc = RSCodec(12, nsize=4095) # always use a power of 2 minus 1
>> rsc = RSCodec(12, c_exp=12) # alternative way to set nsize=4095
- 纠错能力:2*e + v <= n
e:错误的位数
v:缺失的位数(需要提供位置)
n:纠错码长度