使用python解决文本文件中的字符串编码问题

2024-06-16 16:27:42 发布

您现在位置:Python中文网/ 问答频道 /正文

我有很多外部文件的XML文档和文件名,它们有各种形式的文本损坏或Mojibake,在导入过程中会导致数据质量问题。我读过很多关于StackOverflow的文章,关于如何纠正字符串,但是他们没有真正概述如何以系统的方式清理文本,python的decodeencode似乎也帮不上忙。如何使用python2.7恢复包含拉丁语-1(ISO-8859-1)范围内字符但通常具有混合编码的XML文件和文件名?在


Tags: 文件数据字符串文档文本文件名过程系统
1条回答
网友
1楼 · 发布于 2024-06-16 16:27:42

你必须做出假设

如果你不能对你将要遇到的信件做出假设,你可能就有麻烦了。因此,在我们的文档中,我们可以合理地假设挪威字母A-Å。没有什么神奇的工具可以自动更正您遇到的每个文档。在

因此,在这个域中,我们知道一个文件可能包含å,其UTF-8 2字节表示0xc3 0xa5或{a1},Latin-1和{a3}将其表示为0xe5。一般来说,这个character lookup非常好,如果你发现自己在研究一个字符,它可能会成为一个很好的书签。在

示例

  • 挪威人
  • 损坏的版本Ã¥

您可以在这个方便的debugging chart中找到这类问题的长列表。在

基本Python编码、解码

如果你知道到底是什么地方出了问题,这是最简单的方法把绳子打回原形。在

our_broken_string = 'Ã¥'
broken_unicode = our_broken_string.decode('UTF-8')
print broken_unicode # u'\xc3\xa5' yikes -> two different unicode characters
down_converted_string = broken_unicode.encode('LATIN-1')
print down_converted_string # '\xc3\xa5' those are the right bytes
correct_unicode = down_converted_string.decode('UTF-8')
print correct_unicode # u'\xe5' correct unicode value

文件

在处理文档时,可以做出一些相对较好的假设。单词、空格和行。即使文档是XML,您仍然可以将其视为单词,而不必太担心标记,或者如果单词确实是单词,则只需要能找到的最小单元。我们还可以假设,如果文件有文本编码问题,它可能也有行尾问题,这取决于有多少不同的操作系统损坏了该文件。我将在行尾中断rstrip,并使用print将数组重新组合到StringIO文件句柄。在

在保留空白时,通过一个漂亮的print函数运行XML文档可能很诱人,但是您不应该这样做,我们只想在不改变任何其他内容的情况下更正小文本单元的编码。一个好的起点是看看是否可以逐行逐字地阅读文档,而不是在任意的字节块中,而忽略您正在处理的是XML。在

在这里,我利用这样一个事实:如果文本超出UTF-8的范围,那么您将得到unicodedecoderrors,然后尝试使用拉丁语-1。这在这个文件中起作用了。在

^{pr2}$

处理Mojibake

如果你的问题是真的Mojibake,比如一个错误的文件名。您可以使用FTFY尝试试探性地更正您的问题。同样,我会采取逐字逐句的方法来获得最佳效果。在

import os
import sys
import ftfy
import unicodedata


if __name__ == '__main__':
    path = sys.argv[1]
    file_system_encoding = sys.getfilesystemencoding()
    unicode_path = path.decode(file_system_encoding)

    for root, dirs, files in os.walk(unicode_path):
        for f in files:
            comparable_original_filename = unicodedata.normalize('NFC', f)
            comparable_new_filename = ftfy.fix_text(f, normalization='NFC')

            if comparable_original_filename != comparable_new_filename:
                original_path = os.path.join(root, f)
                new_path = os.path.join(root, comparable_new_filename)
                print "Renaming:" + original_path + " to:" + new_path
                os.rename(original_path, new_path)

这通过目录更正了很多更丑陋的错误,其中å被拼成了{}。这是什么?大写字母A+COMBINING LETTER TILDE0xcc 0x83是使用unicode equivalence表示Ã的几种方法之一。这确实是FTFY的一项工作,因为它实际上将执行一个启发式的任务,并解决这些类型的问题。在

用于比较和文件系统的Unicode规范化

另一种方法是使用unicode的规范化来获得正确的字节。在

import unicodedata

a_combining_tilde = 'A\xcc\x83'
# Assume: Expecting UTF-8 
unicode_version = a_combining_tilde.decode('UTF-8') # u'A\u0303' and this cannot be converted to LATIN-1 and get Ã
normalized = unicodedata.normalize('NFC', unicode_version) # u'\c3'
broken_but_better = normalized.encode('UTF-8') # '\xc3\x83` correct UTF-8 bytes for Ã.

总之,如果将其作为UTF-8编码的字符串A\xcc\x83\xc2\xa5,对其进行规范化,然后向下转换为拉丁语-1字符串,然后再返回到UTF-8,您将获得正确的unicode。在

你需要注意操作系统是如何编码文件名的。您可以通过以下方式检索该信息:

file_system_encoding = sys.getfilesystemencoding()

假设file_system_encoding是{},对吗?然后比较两个看起来完全相同的unicode字符串,结果它们不相等!FTFY,默认规格化为NFC,HFS规范化为NFD的旧版本。因此,仅仅知道编码是相同的是不够的,你必须用同样的方式规范化比较才能有效。在

  • Windows NTFS存储unicode而不进行规范化。在
  • Linux存储unicode而不进行规范化。在
  • machfs使用专有HFD规范化存储UTF-8。在

在节点.js有关于dealing with different filesystems的好指南。总之,规范化是为了比较,不要任意地重新规范化文件名。在

最后说明

谎言、该死的谎言和XML声明

在XML d中文档您将得到这样的东西,它应该通知XML解析器有关文本编码的信息。在

<?xml version="1.0" encoding="ISO-8859-1"?>

如果你看到了这一点,在被证明是真的之前,它应该被视为谎言。在将此文档交给XML解析器之前,您需要验证和处理编码问题,并且需要更正声明。在

谎言,该死的谎言和BOM标记

字节顺序标记听起来是个不错的主意,但就像它们的XML声明一样,它们是文件编码情况的完全不可靠的指示器。Within UTF-8, BOMs are NOT recommended并且对于字节顺序没有意义。它们唯一的值是指示某些东西是用UTF-8编码的。但是,考虑到文本编码的问题,默认值是而且应该是预期的UTF-8。在

相关问题 更多 >