文件中的Unicode编码转换问题,如何将一个'\uXXXX'当做u'\uXXXX'处理?

0xfff 发布于 2016/06/12 15:00
阅读 1K+
收藏 0

这个问题不太好描述,查几个关键词都不是我要找的结果,所以麻烦各位大牛直接看例子。

对一串Unicode编码的字符,在python3中是可以直接输出的


>>> s = "\u4e94\u6761\u4e0b\u4f4d&\u4ef2\u6751\u82bd\u8863\u5b50 - fragment of tears (Moromi Original 1) [Challenge]"
>>> s
'五条下位&仲村芽衣子 - fragment of tears (Moromi Original 1) [Challenge]'
>>> print(s)
五条下位&仲村芽衣子 - fragment of tears (Moromi Original 1) [Challenge]


不需要转码,甚至不需要python2中u'XXXX'这样的表示方式。

但是如果一个文件,比如“1.txt”中有这样一个字符串,然后文件读入,则会出现这样的问题:

>>> file_i = open('1.txt')
>>> s = file_i.read()
>>> s
'\\u4e94\\u6761\\u4e0b\\u4f4d&\\u4ef2\\u6751\\u82bd\\u8863\\u5b50 - fragment of tears (Moromi Original 1) [Challenge]'
>>> print(s)
\u4e94\u6761\u4e0b\u4f4d&\u4ef2\u6751\u82bd\u8863\u5b50 - fragment of tears (Moromi Original 1) [Challenge]


因为是以Unicode编码读入,所以 encode decode之类的方法得出的结果依然是\u格式的东西,我基本都试过了,不会解码成中文。我也查到了,可以用

file_i = codecs.open( "1.txt", "r", "unicode-escape" )
来读入,可以正常解码,但是上面的问题其实是简化的,因为要处理的文档不仅是\u编码的问题,我要先读入文件,然后变成\u,再进行解码。比如有的地方可能是#UXXXX,我需要先转换为\uXXXX。

或者换个说法就是,如何将一个'\uXXXX'当做u'\uXXXX'处理?

以下是问题补充:

@0xfff:我的意思是探讨这种直接转换的可行性,或者有没有类似的函数和方法,如果说直接先处理好字符串,再重新保存回文件,然后再打开,再保存,那就偏离了问题的意义了。 因为python貌似无法对一个文件读入并重写,直接'w'方式打开会直接清空文件,'w+'又不能清除前面不要的东西。只能是'r’打开文件,处理字符串,然后'w'再次打开,写入文件,这就是两次文件操作。如果是按上面的办法,必须对一个文件进行四次读写操作,简直是愚蠢,我觉得应该有更好的解决方案。 (2016/06/12 15:20)
@0xfff:以及另一个跟文件名相关的问题,如果文件名含有\u格式的中文,虽然文件名不允许出现斜杠,但是有的会命名为#u$u%u之类的,这样的话就不能直接用读文件的方式来解决。那么只能:1读文件名并处理为\u;2字符串写入一个临时文件;3读入这个临时文件,此时为中文;4文件重命名。感觉也是自找麻烦,明明是不需要文件操作的,硬是要加上一个作为中转。 (2016/06/12 15:37)
加载中
3
Feng_Yu
Feng_Yu

我解释一下。文件保存通常是不能按照"unicode"这种编码方式进行保存的,必须以某种编码后形式保存,如UTF8, GBK, ASCII等。实测在linux环境下,python3会将unicode编码字符串自动转换成UTF8编码保存文件:

$ ipython3
Python 3.4.3 (default, Oct 14 2015, 20:28:29) 
Type "copyright", "credits" or "license" for more information.

IPython 1.2.1 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: s = "\u4e94\u6761\u4e0b\u4f4d&\u4ef2\u6751\u82bd\u8863\u5b50 - fragment of tears (Moromi Original 1) [Challenge]"

In [2]: s
Out[2]: '五条下位&仲村芽衣子 - fragment of tears (Moromi Original 1) [Challenge]'

In [3]: with open("1.txt", "w") as f:                                                                                   
    f.write(s)
   ...:

python3强制使用unicode编码,这个s实际存储的确实是unicode字符串。但是执行f.write()的时候,会将s.encode()的结果进行保存,于是文件编码会变成UTF-8。

$ file 1.txt 
1.txt: UTF-8 Unicode text, with no line terminators



再读取出来的时候,会自动转换为unicode:

In [26]: with open("1.txt", "r") as fr:
   sr = fr.readline()
   ....:    

In [27]: sr
Out[27]: '五条下位&仲村芽衣子 - fragment of tears (Moromi Original 1) [Challenge]'

In [28]: sr.en
sr.encode    sr.endswith  

In [28]: sr.encode()
Out[28]: b'\xe4\xba\x94\xe6\x9d\xa1\xe4\xb8\x8b\xe4\xbd\x8d&\xe4\xbb\xb2\xe6\x9d\x91\xe8\x8a\xbd\xe8\xa1\xa3\xe5\xad\x90 - fragment of tears (Moromi Original 1) [Challenge]'



0
Feng_Yu
Feng_Yu
改用python3,从此免除unicode烦恼
Feng_Yu
Feng_Yu
回复 @0xfff : 这样的话其实你可以先做一次正则替换,把\\替换成\应该就行了
Feng_Yu
Feng_Yu
回复 @0xfff : 等等,我怀疑你是不是直接把raw字符串没编码(encode)直接存入文件了?也就是说这个文件压根就是个ASCII文件,存储的内容就是\u4e94\u6761这样的?如果是的话就解释的通了,因为你读取出来自动把\给转义了,成了\\
Feng_Yu
Feng_Yu
回复 @0xfff : 文件是不可能用unicode编码的,因此你肯定需要转为UTF8编码。
0xfff
0xfff
大牛我用的就是py3啊,但是从文件读的话也是一样的,后面那个u'\uXXXX'只是为了便于理解。或者说你直接在文件中写入上面那个字串,然后读读看,感觉处理好蛋疼。
0
0xfff
0xfff

引用来自“Feng_Yu”的评论

改用python3,从此免除unicode烦恼

回复 @Feng_Yu : 抱歉这几天没怎么上线。确实是一个ASCII的文件,但是不管是什么编码,我的意思是文件内容就是\uXXXX甚至#uXXXX这种。或者类似的例子,如果你在记事本保存一个'1'字符,他读的时候不会读成一个1bit的1,而是'\49'这一个字符。


至于正则替换,我也试过,后来想了下,其实文件中只有一个斜杠,但是输出的时候为了表述的需要显示成了双斜杠,如果本来是双斜杠,恐怕会显示4个吧?总之替换是无效的。。。。。。
Feng_Yu
Feng_Yu
正则处理其实不是特别好的方案,因为很可能把并不想处理的\给处理了。可以参考这篇问答: http://stackoverflow.com/questions/147741/character-reading-from-file-in-python
0
0xfff
0xfff

引用来自“Feng_Yu”的评论

我解释一下。文件保存通常是不能按照"unicode"这种编码方式进行保存的,必须以某种编码后形式保存,如UTF8, GBK, ASCII等。实测在linux环境下,python3会将unicode编码字符串自动转换成UTF8编码保存文件:

$ ipython3
Python 3.4.3 (default, Oct 14 2015, 20:28:29) 
Type "copyright", "credits" or "license" for more information.

IPython 1.2.1 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: s = "\u4e94\u6761\u4e0b\u4f4d&\u4ef2\u6751\u82bd\u8863\u5b50 - fragment of tears (Moromi Original 1) [Challenge]"

In [2]: s
Out[2]: '五条下位&仲村芽衣子 - fragment of tears (Moromi Original 1) [Challenge]'

In [3]: with open("1.txt", "w") as f:                                                                                   
    f.write(s)
   ...:

python3强制使用unicode编码,这个s实际存储的确实是unicode字符串。但是执行f.write()的时候,会将s.encode()的结果进行保存,于是文件编码会变成UTF-8。

$ file 1.txt 
1.txt: UTF-8 Unicode text, with no line terminators



再读取出来的时候,会自动转换为unicode:

In [26]: with open("1.txt", "r") as fr:
   sr = fr.readline()
   ....:    

In [27]: sr
Out[27]: '五条下位&仲村芽衣子 - fragment of tears (Moromi Original 1) [Challenge]'

In [28]: sr.en
sr.encode    sr.endswith  

In [28]: sr.encode()
Out[28]: b'\xe4\xba\x94\xe6\x9d\xa1\xe4\xb8\x8b\xe4\xbd\x8d&\xe4\xbb\xb2\xe6\x9d\x91\xe8\x8a\xbd\xe8\xa1\xa3\xe5\xad\x90 - fragment of tears (Moromi Original 1) [Challenge]'



抱歉几天没上线,感谢回复,貌似理解了一点。但是如果想在读取文件后进行转换该怎么处理?功利点说用啥函数能解决?因为decode之类的函数也试过,貌似是不起作用的。

笨办法也是有的,碰到“\\u”就提取后面四个字符,然后转化为16进制的值,chr()输出此Unicode值对应的中文字符,然后拼接起来,但是感觉好蛋疼啊。或者换个例子,如果我在文件中存了一个字符串'0101',在执行正常的读入后,应该如何将其转换成一个4bit的十进制5的二进制表示,而不是'\48\49\48\49'?如果当成一个字符数组然后一个个减48然后移位自然是可以的。    

Feng_Yu
Feng_Yu
参考上面的回复。stackoverflow这个问答比较有意思,读取文件的时候实际可以强行指定文件的处理方式。
0
0xfff
0xfff

引用来自“Feng_Yu”的评论

改用python3,从此免除unicode烦恼
回复 @Feng_Yu : 秒回的大牛。我是用的s = s.replace('a', 'b')这样类似的替换,不知道算不算正则,正则替换只用过re.sub。stackoverflow那边去的不多,这个大致看了下,有说一个个摘出来单个处理的,直接赋值为s = u'XXXX'的,encode的这就跟我从文件读取的要求有点违背了,不然我直接从文件复制字串出来赋值给s,就一点问题都没有了。。。。。。还有的用codecs.open(),这个我后来也补充过了,只能用于文件里是已经处理好格式为\uXXXX的文件。比如文件里是#uXXXX,所以我先r模式读入,#替换为\,这时就只能w模式写入,然后r模式重新打开,最后再w模式写回,这就有点蛋疼了。好吧我承认,这是没有技术还净想偷懒。。。。。。
0
一碗粥
一碗粥
In [10]: with open('./luanma.txt', 'r') as f:
   ....:     lines = f.readlines()
   ....:     print '1. ', lines[0]
   ....:     print '-' * 150
   ....:     print '2. ', eval(repr(unicode(zzz)).replace(r'\\u', r'\u'))
1.  \u4e94\u6761\u4e0b\u4f4d&\u4ef2\u6751\u82bd\u8863\u5b50 - fragment of tears (Moromi Original 1) [Challenge]

------------------------------------------------------------------------------------------------------------------------------------------------------
2.  五条下位&仲村芽衣子 - fragment of tears (Moromi Original 1) [Challenge]



返回顶部
顶部