我正在阅读一个包含井号(£)的文本文件:
f = open(file, 'r')
string = f.read()
f.close()
除了一些其他的正则表达式操作,我想删除这些井号,并将该字符串写入新文件。我最接近完成这项工作的是以下代码:
n = re.compile(unichr(163))
string = n.sub('', string)
这似乎正确地找到了英镑符号,但是£
不会替换为任何符号,而是转换为此符号:Â
任何人都知道发生了什么事?
答案 0 :(得分:3)
在utf8中,
£
映射到原始字节\xc2\xa3
。re
模块允许在unicode和字节编码的字符串之间进行字符串替换,这是一个错误。
我认为J.F. Sebastian的answer更加娴熟,但这是一个演练。
调用read()
返回字节串。
为了说明,我们创建以下文件durp
:
echo -n "£" > durp
下一个命令以十六进制格式获取文件的内容:
$ cat durp | xxd | cut -d " " -f 2
c2a3
注意:访问此url会在多个编码中显示£
。
这些是构成£
的原始字节。 python在读取时对文件做了什么?
$ python
> f = open("durp")
> f.read()
'\xc2\xa3'
它不知道编码是什么,所以它表示转义的十六进制形式的字节。
让我们导入您的代码:
> import re
> r = re.compile(u'£')
> u'£'
u'\xa3'
最后一行只是为了看看我们正在制作一个模式。这是错误的来源。
现在我们对文件内容执行替换:
> r.sub('', '\xc2\xa3')
'\xc2'
这是可以想象但错误的。我们在'\xa3'
中将''
替换为'\xc2\xa3'
并获得了'\xc2'
。这是re
中的错误,因为unicode字符串与bytestrings混合在一起。执行具有不同编码的字符的子集是没有意义的。这基本上是替换字节而不是字符。
J.F。 Sebastian的answer解释了您的终端如何将'\xc2'
解释为Â
。
答案 1 :(得分:2)
Python 2中re
模块中的一个错误是允许混合unicode模式和输入字节串:它使用latin-1
编码静默编码模式,导致错误的结果。 Python 3在这里正确地引发了TypeError
。
>>> u'\N{POUND SIGN}'.encode('latin-1')
'\xa3'
>>> u'\N{POUND SIGN}'.encode('utf-8')
'\xc2\xa3'
>>> import re
>>> re.sub(u'\N{POUND SIGN}', '', u'\N{POUND SIGN}'.encode('utf-8'))
'\xc2'
>>> print(re.sub(u'\N{POUND SIGN}', '', u'\N{POUND SIGN}'.encode('utf-8')).decode('cp1252'))
Â
>>> print(re.sub(u'\N{POUND SIGN}', '', u'x\N{POUND SIGN}y'))
xy
解决方案是对模式和输入字符串使用Unicode:
import io
with io.open('file.txt', encoding='utf-8') as file:
result = file.read().replace(u'\N{POUND SIGN}', '')
codecs
模块无法正确处理通用换行符,而是使用io
模块。 Python 3中的内置open()
函数是io.open()
。
答案 2 :(得分:0)
问题是你要混合8位字符串和完整的Unicode字符串。 @cdosborn已经很好地描述了这是如何导致部分替换字符的。
在Python中> 2.x,有两种保存文本的方法:字符串和Unicode字符串。字符串可以包含纯ASCII,ANSI,Windows-1252,UTF-8,UTF-16中的文本编码。问题是你必须知道如果你需要转换它,文本的编码是什么。手上的Unicode字符串完全是明确的,因为它们是使用已知编码从字符串显式转换的结果,使用Unicode转义码(u" \ u00A3"),或者像unichr这样的函数( )。
最佳做法是始终在输入代码时将字符串解码为Unicode。然后编码出路。这是Python 3.x和其他语言(如Java)的默认行为。
如果您正在处理文件,codecs
模块提供了一种在以下方式将文本转换为Unicode字符串的好方法:
my_file = codecs.open("filename.txt", "r", "utf-8")
my_unicode_string = my_file.read()
显然,如果您的文件采用其他编码方式,请更改编码名称utf-8
- 请参阅编解码器名称:https://docs.python.org/2/library/codecs.html#standard-encodings
如果您正在处理来自其他地方的字符串(stdin,webforms),请使用以下方法进行转换:
my_unicode_string = "my €uro sign in utf-8".decode("utf-8")
再次,相应地更改utf-8
参数
获得Unicode字符串后,您可以根据自己的意愿自由使用。要做一个简单的搜索并替换英镑符号,请执行以下操作:
my_unicode_string.replace(unichr(163), "")
为了使您的代码更易于阅读,您可以使用UTF-8对源代码进行编码并声明编码。这意味着您不必在转义序列或序数中隐藏Unicode字符。
完全放弃:
# -*- coding: utf-8 -*-
my_file = codecs.open("filename.txt", "r", "utf-8")
my_unicode_string = my_file.read()
replaced_unicode_string = my_unicode_string.replace("£", "")
现在,如果您想将replaced_unicode_string
写入另一个文件:
my_output_file = codecs.open("another_filename.txt", "w", "utf-8")
my_output_file.write(replaced_unicode_string)