【问题标题】:Wrong charset after re.sub()re.sub() 后字符集错误
【发布时间】:2013-04-14 09:14:10
【问题描述】:

我有这个代码

import chardet, re    

content = "Бланк свидетельства о допуске."
print content
print chardet.detect(content)
content = re.sub(u"(?i)[^-0-9a-zа-яё«»\&\;\/\<\>\.,\s\(\)\*:!\?]", "", content)
print content
print chardet.detect(content)

然后输出

Бланк свидетельства о допуске.
{'confidence': 0.99, 'encoding': 'utf-8'}
� �  .
{'confidence': 0.5, 'encoding': 'windows-1252'}

我做错了什么?如何在re.sub() 之后获取 uft-8 字符串? (Python 2.7,# coding: utf-8,UTF-8 文件,IDE Pycharm)。

谢谢。

【问题讨论】:

  • 您正在尝试对原始字节使用正则表达式?您在这里操作的是 bytes,而不是字符,每个输入字符由 2 个字节组成。
  • 我怎样才能获得 UTF-8?
  • 期望的输出是什么?
  • 查看此答案stackoverflow.com/a/16113322/497208 以获取指向“每个软件开发人员绝对、肯定必须了解 Unicode 和字符集的绝对最小值”

标签: python regex python-2.7


【解决方案1】:

这就是(我认为)您想要实现的目标(为了清楚起见,我简化了正则表达式):

#coding=utf8
import re    
content = u"Бланк XYZ свидетельства о ???допуске."
content = re.sub(u"(?iu)[^а-яё]", ".", content)
print content.encode('utf8') # Бланк.....свидетельства.о....допуске.

注意重点:

  • 主题是 unicode
  • 表达式是 unicode
  • 表达式使用 unicode 标志 (?u) 来进行大小写折叠。

另外,对于严肃的 unicode 工作,我推荐 regex 模块,它提供了出色且几乎完整的 unicode 支持。考虑:

# drop everything except Cyrillic and spaces 
import regex
content = regex.sub(u'[^\p{Cyrillic}\p{Zs}]', '', content) 

虽然documented re.UNICODE 只会改变\w 和朋友,但在我的测试中它也会影响大小写折叠 (re.IGNORECASE):

Python 2.7.2+ (default, Oct  4 2011, 20:06:09) 
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import re
>>> src = u'Σσ Φφ Γγ'
>>> src
u'\u03a3\u03c3 \u03a6\u03c6 \u0393\u03b3'
>>> re.sub(ur'(?i)[α-ώ]', '-', src)
u'\u03a3- \u03a6- \u0393-'
>>> re.sub(ur'(?iu)[α-ώ]', '-', src)
u'-- -- --'

所以这要么是未记录的功能,要么是文档问题。

【讨论】:

  • re.IGNORECASE 的文档声明它不受语言环境的影响。 re.UNICODE 标志被记录为仅更改字符类(\w\W\s\S 等。
  • 感谢regex 模块。
  • @thg435:很有趣,感谢您的确认!听起来文档需要更新。
【解决方案2】:

您的输入是 UTF-8:

>>> content
'\xd0\x91\xd0\xbb\xd0\xb0\xd0\xbd\xd0\xba \xd1\x81\xd0\xb2\xd0\xb8\xd0\xb4\xd0\xb5\xd1\x82\xd0\xb5\xd0\xbb\xd1\x8c\xd1\x81\xd1\x82\xd0\xb2\xd0\xb0 \xd0\xbe \xd0\xb4\xd0\xbe\xd0\xbf\xd1\x83\xd1\x81\xd0\xba\xd0\xb5.'

但是您使用的是 unicode 正则表达式。表达式直接匹配 UTF-8 输入的原始字节。

在所有这些字节中,只有空格、句号和\xbb 字节(作为» 字符)不会被删除。其余的单个字节被删除,因为它们不属于您的否定字符类[^...]

正确使用 Unicode(首先将 content 解码为 un​​icode)有效:

>>> re.sub(u"(?i)[^-0-9a-zа-яё«»\&\;\/\<\>\.,\s\(\)\*:!\?]", "", content.decode('utf8'))
u'\u043b\u0430\u043d\u043a \u0441\u0432\u0438\u0434\u0435\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u0430 \u043e \u0434\u043e\u043f\u0443\u0441\u043a\u0435.'
>>> print re.sub(u"(?i)[^-0-9a-zа-яё«»\&\;\/\<\>\.,\s\(\)\*:!\?]", "", content.decode('utf8'))
ланк свидетельства о допуске.

替代方法是对正则表达式使用原始字节字符串,并匹配字节组合。弄清楚哪些 UTF-8 字节和范围是有效的,非常非常很棘手。您需要完全了解UTF-8 encodes characters to multiple bytes 的原理,然后将您的否定字符类转换为一组允许通过相同字节组合的肯定 匹配项。这不适合胆小的人。

【讨论】:

  • 抱歉,我知道您的表达本身 unicode。你不应该混合这些。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-09-29
  • 2014-03-18
  • 2022-10-13
  • 1970-01-01
  • 1970-01-01
  • 2014-10-12
相关资源
最近更新 更多