【问题标题】:Python String Comparison--Problems With Special/Unicode CharactersPython 字符串比较——特殊/Unicode 字符的问题
【发布时间】:2011-03-08 06:00:00
【问题描述】:

我正在编写一个 Python 脚本来处理一些音乐数据。它应该通过比较它们的条目并匹配它们来合并两个独立的数据库。它几乎可以工作,但在比较包含特殊字符(即重音字母)的字符串时失败。我很确定这是 ASCII 与 Unicode 编码问题,因为我得到了错误:

“Unicode 相等比较未能将两个参数转换为 Unicode - 将它们解释为不相等”

我意识到我可以使用正则表达式来删除有问题的字符,但是我正在处理大量数据并且过度依赖正则表达式会使我的程序变得非常缓慢。有没有办法让 Python 正确比较这些字符串?这是怎么回事——有没有办法判断它是将我的字符串存储为 ASCII 还是 Unicode?​​p>

编辑 1:我使用的是 Python v2.6.6。检查类型后,我发现一个数据库向我吐出 Unicode 字符串,而一个数据库给出 ASCII。所以这可能就是问题所在。我正在尝试将第二个数据库中的 ASCII 字符串转换为 Unicode,其中包含类似

的行
line = unicode(f.readline().decode(latin_1).encode(utf_8))

但这会产生如下错误:

UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 41: ordinal not in range(128)

我不确定为什么 'ascii' 编解码器会抱怨,因为我正在尝试从 ASCII 解码 。有人可以帮忙吗?

【问题讨论】:

  • “使用正则表达式删除有问题的字符”:在你知道有问题的字符是什么以及它们为什么有问题之前是不可能的,并且删除不方便的数据通常不是一个好主意。该解决方案更有可能使用正确的编码解码您的 str 对象。

标签: python string


【解决方案1】:

Unicode 与字节

首先,一些术语。字符串有编码和解码两种:

  • 已编码。这是存储在磁盘上的内容。对于 Python,它是一堆 0 和 1,您可能会将其视为 ASCII,但它可以是任何东西——二进制数据、JPEG 图像等等。在 Python 2.x 中,这称为“字符串”变量。在 Python 3.x 中,它更准确地称为“字节”变量。
  • 已解码。这是一串实际字符。它们可以编码为 8 位 ASCII 字符串,也可以编码为 32 位汉字。但在转换为 编码 变量之前,它只是一个 Unicode 字符串。

这对你意味着什么

所以事情就是这样。你说你得到一个 ASCII 变量和一个 Unicode 变量。事实并非如此。

  • 您有一个变量,它是一串字节 - 1 和 0,大概以 8 个为一组。这是您错误地假设为 ASCII 的变量。
  • 您还有另一个变量是 Unicode 数据——数字、字母和符号。

在将字节字符串与 Unicode 字符串进行比较之前,您必须做出一些假设。在您的情况下,Python(和您)假设字节字符串是 ASCII 编码的。在你遇到一个 不是 ASCII 的字符之前,它工作得很好——一个带有重音符号的字符。

所以你需要找出那个字节串被编码成什么。它可能是 latin1。如果是,你想这样做:

if unicode_variable == string_variable.decode('latin1')

Latin1 基本上是 ASCII 加上一些扩展字符,如 Ç 和 Â。

如果您的数据是 Latin1,这就是您需要做的。但是如果你的字节串是用别的东西编码的,你需要弄清楚它是什么编码并将它传递给 decode()。

最重要的是,没有简单的答案,除非您知道(或做出一些假设)关于输入数据的编码

我会做什么

尝试在你的字节串上运行 var.decode('latin1')。这将为您提供一个 Unicode 变量。如果可行,并且数据看起来正确(即,带有重音符号的字符看起来像是属于它们的),那就顺其自然吧。

哦,如果 latin1 无法解析或看起来不正确,请尝试 utf8——另一种常见的编码。

【讨论】:

  • 谢谢,成功了!我正在从一个文本文件中读取数据,我可以验证它是用 utf-8 编码的,所以我只是用 utf_8 解码它,我的特殊字符问题就消失了。没想到读取测试文件这么复杂……
【解决方案2】:

您可能需要预处理数据库并将所有内容转换为 UTF-8。我的猜测是您在某些条目中使用了 Latin-1 重音字符。


至于你的问题,唯一确定的方法就是看。让你的脚本吐出那些不能比较的,然后查找字符代码。或者试试string.decode('latin1').encode('utf8') 看看会发生什么。

【讨论】:

  • OP 应该如何“吐出”字符串?什么字符代码?抬头看什么?是什么让您假设 OP 的文本以 latin1 编码,或者他的系统上的默认编码是 latin1? .encode('utf8') 有什么意义??? [如果解码成功,你会得到保证不会以 UTF-8 编码失败的 unicode]
  • 这是很多问题。 “吐出”我的意思是用 try/except 包围测试,并打印到 stderr 任何导致错误的东西。如果我看到类似'\xc3\xbcmlaut\n'的东西,我会有一些对诊断有用的东西。你关于 encode('utf8') 的观点很好,我认为这还不够。
  • 如果您将其重新编码为 UTF8,您将返回将 Unicode 变量与字节字符串进行比较。正确的做法是比较两个解码后的 unicode 变量。
  • 如果您对两个字符串都执行相同操作,则不会。但你是对的,重新编码不是正确的做法。正如我所说,“我认为这还不够”。我最近使用 Python 转换了我的 iPod Touch 音乐数据库,并且遇到了 latin1 字符的编码问题。
【解决方案3】:

将两者都转换为 unicode 应该会有所帮助:

if unicode(str1) == unicode(str2):
    print "same" 

【讨论】:

  • Ummm ... 似乎隐式转换为 unicode 失败;让它显式转换会有帮助吗?
【解决方案4】:

要了解您(不是它)是否将您的字符串存储为str 对象或unicode 对象,print type(your_string)

您可以使用print repr(your_string) 明确地向您(和我们)展示您的字符串中的内容。

顺便问一下,您使用的是什么版本的 Python,在什么操作系统上?如果是 Python 3.x,请使用 ascii() 而不是 repr()

【讨论】:

    猜你喜欢
    • 2019-08-03
    • 2015-11-06
    • 1970-01-01
    • 1970-01-01
    • 2015-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多