UTF-8与latin-1转换问题,Python Django
好的,我的问题是我有一个字符串 '\222\222\223\225',它在数据库中是以latin-1格式存储的。当我在Django中打印出来时,得到的字符串是 'ââââ¢',我猜这是它的UTF转换。现在我需要把这个字符串传递给一个函数来进行操作:
strdecryptedPassword + chr(ord(c) - 3 - intCounter - 30)
但是我遇到了这个错误:
chr() 的参数不在范围(256)内
如果我先尝试把这个字符串编码为latin-1格式,我会得到这个错误:
'latin-1' 编码器无法编码位置0-3的字符:序号不在范围(256)内
我读了很多关于字符编码的内容,但我还是有些搞不懂,感觉有些东西我没有掌握!
3 个回答
这是因为它使用了一种很糟糕的加密方式,这种方式只是通过某种请求来改变字符的编码值,所以从数据库中出来的字符串是被加密过的,而这个过程是用来解密的。你上面提供的内容似乎不太管用。在数据库里,它是用latin-1编码的,django会把它转换成unicode,但我不能直接把它作为unicode传给函数。当我尝试把它编码成latin-1时,就出现了错误。
正如Vinko所提到的,Latin-1或ISO 8859-1并没有你提到的八进制字符串的可打印字符。根据我对8859-1的记录,“C1控制字符(0x80 - 0x9F)来自ISO/IEC 6429:1992。它没有为80、81或99定义名称。”这些代码点的名称就是Vinko列出的:
\222 = 0x92 => PRIVATE USE TWO
\223 = 0x93 => SET TRANSMIT STATE
\225 = 0x95 => MESSAGE WAITING
这些字符的正确UTF-8编码是(Unicode,二进制,十六进制):
U+0092 = %11000010 %10010010 = 0xC2 0x92
U+0093 = %11000010 %10010011 = 0xC2 0x93
U+0095 = %11000010 %10010101 = 0xC2 0x95
带有抑扬符的小写字母a在ISO 8859-1中的代码是0xE2,因此在Unicode中是U+00E2;在UTF-8中,它是%11000011 %10100010,或者0xC3 0xA2。
分币符号在ISO 8859-1中的代码是0xA2,因此在Unicode中是U+00A2;在UTF-8中,它是%11000011 %10000010,或者0xC3 0x82。
所以,不管你看到什么,你似乎并没有看到ISO 8859-1的UTF-8编码。除此之外,你看到的只有5个字节,而你应该看到8个字节。
补充说明: 答案的前一部分讨论了“UTF-8编码”的说法,但忽略了问题的其他部分,问题中提到:
Now I need to pass the string into a function that does this operation:
strdecryptedPassword + chr(ord(c) - 3 - intCounter - 30)
I get this error: chr() arg not in range(256). If I try to encode the
string as Latin-1 first I get this error: 'latin-1' codec can't encode
characters in position 0-3: ordinal not in range(256).
你实际上没有展示intCounter是如何定义的,但如果它每个字符轻微递增,迟早会出现'ord(c) - 3 - intCounter - 30
'为负的情况(顺便问一下,为什么不把常量合并,使用'ord(c) - intCounter - 33
'呢?),到那时,chr()
可能会出错。如果值为负,你需要加256,或者使用取模操作来确保传给chr()
的值在0到255之间。由于我们看不到intCounter是如何递增的,所以无法判断它是从0到255循环,还是单调增加。如果是后者,那么你需要一个这样的表达式:
chr(mod(ord(c) - mod(intCounter, 255) + 479, 255))
其中256 - 33 = 223,479 = 256 + 223。这保证了传给chr()
的值是正数,并且对于任何输入字符c和任何intCounter的值都在0到255的范围内(而且,因为mod()
函数从不接受负数作为参数,所以无论mod()
在参数为负时的表现如何,它也能正常工作)。
你遇到的第一个错误“chr() arg not in range(256)”可能是因为你传入的值太小了,导致了下溢。因为chr这个函数不能处理负数。我不太清楚当输入的计数器加上33后超过了实际字符范围时,这个加密算法应该怎么处理,你需要查一下在这种情况下该怎么做。
关于第二个错误,你需要用decode()而不是encode()来处理普通字符串,这样才能得到正确的数据表示。encode()是用来把一个unicode对象(那些以u'开头的)转换成普通字符串,方便输出或者写入文件。而decode()则是把一个字符串对象转换成unicode对象,生成对应的代码点。如果你是从字符串对象生成unicode对象,可以使用unicode()这个调用,或者你也可以直接用a.decode('latin-1')来处理。
>>> a = '\222\222\223\225'
>>> u = unicode(a,'latin-1')
>>> u
u'\x92\x92\x93\x95'
>>> print u.encode('utf-8')
ÂÂÂÂ
>>> print u.encode('utf-16')
ÿþ
>>> print u.encode('latin-1')
>>> for c in u:
... print chr(ord(c) - 3 - 0 -30)
...
q
q
r
t
>>> for c in u:
... print chr(ord(c) - 3 -200 -30)
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ValueError: chr() arg not in range(256)