【问题标题】:Unicode (UTF-8) reading and writing to files in PythonUnicode (UTF-8) 在 Python 中读取和写入文件
【发布时间】:2010-10-04 06:20:05
【问题描述】:

我在理解读取文本和将文本写入文件(Python 2.4)方面有些脑残。

# The string, which has an a-acute in it.
ss = u'Capit\xe1n'
ss8 = ss.encode('utf8')
repr(ss), repr(ss8)

("u'Capit\xe1n'", "'Capit\xc3\xa1n'")

print ss, ss8
print >> open('f1','w'), ss8

>>> file('f1').read()
'Capit\xc3\xa1n\n'

所以我在我最喜欢的编辑器中的文件 f2 中输入Capit\xc3\xa1n

然后:

>>> open('f1').read()
'Capit\xc3\xa1n\n'
>>> open('f2').read()
'Capit\\xc3\\xa1n\n'
>>> open('f1').read().decode('utf8')
u'Capit\xe1n\n'
>>> open('f2').read().decode('utf8')
u'Capit\\xc3\\xa1n\n'

我在这里不明白什么?显然,我缺少一些重要的魔法(或良好的感觉)。一种类型到文本文件中以获得正确的转换?

我在这里真正无法理解的是 UTF-8 表示的意义在于,如果您实际上无法让 Python 识别它,那么当它来自外部时。也许我应该只是 JSON 转储字符串,并改用它,因为它有一个 asciiable 表示!更重要的是,当从文件进入时,Python 会识别和解码这个 Unicode 对象的 ASCII 表示吗?如果有,如何获得?

>>> print simplejson.dumps(ss)
'"Capit\u00e1n"'
>>> print >> file('f3','w'), simplejson.dumps(ss)
>>> simplejson.load(open('f3'))
u'Capit\xe1n'

【问题讨论】:

    标签: python unicode utf-8 io


    【解决方案1】:

    我发现在打开文件时指定编码更容易,而不是混淆编码和解码方法。 io module(在 Python 2.6 中添加)提供了一个 io.open 函数,它有一个编码参数。

    使用 io 模块中的 open 方法。

    >>>import io
    >>>f = io.open("test", mode="r", encoding="utf-8")
    

    然后调用 f 的 read() 函数后,返回一个编码的 Unicode 对象。

    >>>f.read()
    u'Capit\xe1l\n\n'
    

    请注意,在 Python 3 中,io.open 函数是内置 open 函数的别名。内置的 open 函数只支持 Python 3 中的 encoding 参数,不支持 Python 2。

    编辑:以前这个答案推荐codecs 模块。 codecs module can cause problems when mixing read() and readline(),所以这个答案现在推荐 io 模块。

    使用编解码器模块中的 open 方法。

    >>>import codecs
    >>>f = codecs.open("test", "r", "utf-8")
    

    然后调用 f 的 read() 函数后,返回一个编码的 Unicode 对象。

    >>>f.read()
    u'Capit\xe1l\n\n'
    

    如果您知道文件的编码,那么使用 codecs 包就不会那么混乱了。

    http://docs.python.org/library/codecs.html#codecs.open

    【讨论】:

    • 也非常适合写入文件,而不是 open(file,'w') do codecs.open(file,'w','utf-8')solved
    • codecs.open(...) 方法是否也完全符合with open(...): 样式,其中with 关心完成后关闭文件?它似乎仍然有效。
    • @try-catch-finally 是的。我一直使用with codecs.open(...) as f:
    • 我希望我能投票一百次。在为大量混合数据引起的编码问题苦恼了几天,并且对编码进行了斗鸡眼的阅读之后,这个答案就像沙漠中的水一样。希望我早点看到它。
    【解决方案2】:

    在符号中

    u'Capit\xe1n\n'
    

    “\xe1”只代表一个字节。 "\x" 告诉你 "e1" 是十六进制的。 当你写

    Capit\xc3\xa1n
    

    在你的文件中你有“\xc3”。这些是 4 个字节,在您的代码中您可以全部阅读它们。显示它们时可以看到:

    >>> open('f2').read()
    'Capit\\xc3\\xa1n\n'
    

    你可以看到反斜杠被反斜杠转义了。所以你的字符串中有四个字节:“\”、“x”、“c”和“3”。

    编辑:

    正如其他人在他们的回答中指出的那样,您应该只在编辑器中输入字符,然后您的编辑器应该处理转换为 UTF-8 并保存它。

    如果您确实有这种格式的字符串,您可以使用string_escape 编解码器将其解码为普通字符串:

    In [15]: print 'Capit\\xc3\\xa1n\n'.decode('string_escape')
    Capitán
    

    结果是一个以 UTF-8 编码的字符串,其中重音字符由在原始字符串中写入 \\xc3\\xa1 的两个字节表示。如果你想要一个 unicode 字符串,你必须用 UTF-8 再次解码。

    您的编辑:您的文件中没有 UTF-8。实际看看它的样子:

    s = u'Capit\xe1n\n'
    sutf8 = s.encode('UTF-8')
    open('utf-8.out', 'w').write(sutf8)
    

    将文件 utf-8.out 的内容与您使用编辑器保存的文件的内容进行比较。

    【讨论】:

    • 那么,如果 python 可以使用 utf-8 编码格式读取文件,那么它的意义何在?换句话说,python 是否会在 \xc3 中以 1 个字节读取任何 ascii 表示形式?
    • “那么,有什么意义……”问题的答案是“Mu”。 (因为 Python 可以读取以 UTF-8 编码的文件)。对于您的第二个问题: \xc3 不是 ASCII 集的一部分。也许您的意思是“8位编码”。您对 Unicode 和编码感到困惑;没关系,很多都是。
    • 尝试将此作为入门读物:joelonsoftware.com/articles/Unicode.html
    • 注意:u'\xe1' 是一个 Unicode 代码点 U+00e1,可以使用 1 个或 更多 个字节表示,具体取决于字符编码(在 utf-8 中为 2 个字节) . b'\xe1' 是一个字节(一个数字 225),它可以表示什么字母取决于用于解码它的字符编码,例如,在 cp1251 中是 б (U+0431),在 cp866 中是 с (U+0441),等等。
    • 令人惊讶的是,有多少英国程序员说“只使用 ascii”,然后却没有意识到英镑符号不是它。大多数人都不知道 ascii!=local 代码页(即 latin1)。
    【解决方案3】:

    现在你在 Python3 中只需要open(Filename, 'r', encoding='utf-8')

    [于 2016 年 2 月 10 日编辑以要求澄清]

    Python3 在其 open 函数中添加了 encoding 参数。以下关于 open 函数的信息是从这里收集的:https://docs.python.org/3/library/functions.html#open

    open(file, mode='r', buffering=-1, 
          encoding=None, errors=None, newline=None, 
          closefd=True, opener=None)
    

    Encoding 是用于解码或编码的编码名称 文件。这应该只在文本模式下使用。默认编码是 平台相关(无论locale.getpreferredencoding() 返回),但可以使用 Python 支持的任何text encoding。 有关支持的编码列表,请参阅codecs 模块。

    因此通过将encoding='utf-8'作为参数添加到open函数中,文件的读取和写入都以utf8形式完成(这也是现在Python中所做的一切的默认编码。)

    【讨论】:

    • 能否请您详细说明您的答案,添加更多关于您提供的解决方案的描述?
    • 它看起来可以在 python 2 中使用编解码器模块 - codecs.open('somefile', encoding='utf-8') stackoverflow.com/a/147756/149428
    【解决方案4】:

    实际上,这对我在 Python 3.2 中读取具有 UTF-8 编码的文件很有用:

    import codecs
    f = codecs.open('file_name.txt', 'r', 'UTF-8')
    for line in f:
        print(line)
    

    【讨论】:

      【解决方案5】:

      所以,我找到了我正在寻找的解决方案,即:

      print open('f2').read().decode('string-escape').decode("utf-8")
      

      这里有一些不寻常的编解码器很有用。这种特殊的读取允许人们从 Python 中获取 UTF-8 表示,将它们复制到 ASCII 文件中,然后将它们读入 Unicode。在“string-escape”解码下,斜线不会加倍。

      这允许我想象中的那种往返。

      【讨论】:

      • 很好的回应,我已经测试了(codecs.open(file,"r","utf-8") 和简单的open(file,"r").read().decode("utf-8") 两种解决方案,并且都运行良好。
      • 我收到“TypeError:预期的 str、bytes 或 os.PathLike 对象,而不是 _io.TextIOWrapper”,知道为什么吗?
      • 我认为,考虑到赞成票的数量,接受第二个答案是个好主意:)
      【解决方案6】:
      # -*- encoding: utf-8 -*-
      
      # converting a unknown formatting file in utf-8
      
      import codecs
      import commands
      
      file_location = "jumper.sub"
      file_encoding = commands.getoutput('file -b --mime-encoding %s' % file_location)
      
      file_stream = codecs.open(file_location, 'r', file_encoding)
      file_output = codecs.open(file_location+"b", 'w', 'utf-8')
      
      for l in file_stream:
          file_output.write(l)
      
      file_stream.close()
      file_output.close()
      

      【讨论】:

        【解决方案7】:

        除了codecs.open(),可以使用io.open()配合Python2或Python3读写unicode文件

        示例

        import io
        
        text = u'á'
        encoding = 'utf8'
        
        with io.open('data.txt', 'w', encoding=encoding, newline='\n') as fout:
            fout.write(text)
        
        with io.open('data.txt', 'r', encoding=encoding, newline='\n') as fin:
            text2 = fin.read()
        
        assert text == text2
        

        【讨论】:

        • 是的,使用io更好;但是我写了这样with io.open('data.txt', 'w', 'utf-8') as file:的with语句并得到了一个错误:TypeError: an integer is required。在我更改为with io.open('data.txt', 'w', encoding='utf-8') as file: 之后,它起作用了。
        【解决方案8】:

        为了读入一个 Unicode 字符串然后发送到 HTML,我这样做了:

        fileline.decode("utf-8").encode('ascii', 'xmlcharrefreplace')
        

        对 python 驱动的 http 服务器很有用。

        【讨论】:

          【解决方案9】:

          好吧,您最喜欢的文本编辑器没有意识到 \xc3\xa1 应该是字符文字,但它会将它们解释为文本。这就是为什么最后一行出现双反斜杠的原因——它现在是一个真正的反斜杠 + xc3,等等。

          如果你想在 Python 中读写编码文件,最好使用codecs 模块。

          在终端和应用程序之间粘贴文本很困难,因为您不知道哪个程序会使用哪种编码来解释您的文本。您可以尝试以下方法:

          >>> s = file("f1").read()
          >>> print unicode(s, "Latin-1")
          Capitán
          

          然后将此字符串粘贴到您的编辑器中,并确保它使用 Latin-1 存储它。在剪贴板没有乱码字符串的假设下,往返应该可以工作。

          【讨论】:

            【解决方案10】:

            您偶然发现了编码的一般问题:如何判断文件的编码方式?

            答案:您不能除非文件格式为此提供。例如,XML 以:

            开头
            <?xml encoding="utf-8"?>
            

            这个标头是经过精心挑选的,因此无论编码如何都可以读取。在您的情况下,没有这样的提示,因此您的编辑器和 Python 都不知道发生了什么。因此,您必须使用codecs 模块并使用codecs.open(path,mode,encoding),它提供了Python 中缺少的位。

            至于你的编辑器,你必须检查它是否提供了一些设置文件编码的方法。

            UTF-8 的重点是能够将 21 位字符 (Unicode) 编码为 8 位数据流(因为这是世界上所有计算机都可以处理的唯一内容)。但由于大多数操作系统早于 Unicode 时代,它们没有合适的工具将编码信息附加到硬盘上的文件中。

            下一个问题是 Python 中的表示。这在comment by heikogerlach 中得到了完美的解释。您必须了解您的控制台只能显示 ASCII。为了显示 Unicode 或任何 >= charcode 128,它必须使用某种转义方法。在您的编辑器中,您不能输入转义的显示字符串,而是输入字符串的含义(在这种情况下,您必须输入变音符号并保存文件)。

            也就是说,您可以使用 Python 函数 eval() 将转义字符串转换为字符串:

            >>> x = eval("'Capit\\xc3\\xa1n\\n'")
            >>> x
            'Capit\xc3\xa1n\n'
            >>> x[5]
            '\xc3'
            >>> len(x[5])
            1
            

            如您所见,字符串“\xc3”已变成单个字符。现在这是一个 8 位字符串,采用 UTF-8 编码。获取 Unicode:

            >>> x.decode('utf-8')
            u'Capit\xe1n\n'
            

            Gregg Lind问:我认为这里缺少一些部分:文件f2包含:十六进制:

            0000000: 4361 7069 745c 7863 335c 7861 316e  Capit\xc3\xa1n
            

            codecs.open('f2','rb', 'utf-8'),例如,以单独的字符读取它们(预期)有什么方法可以用 ASCII 写入文件吗?

            回答:这取决于你的意思。 ASCII 不能表示大于 127 的字符。所以你需要某种方式来表示“接下来的几个字符意味着一些特殊的东西”,这就是序列“\x”所做的。它说:接下来的两个字符是单个字符的代码。 "\u" 使用四个字符将 Unicode 编码为 0xFFFF (65535)。

            因此您不能直接将 Unicode 写入 ASCII(因为 ASCII 根本不包含相同的字符)。您可以将其编写为字符串转义符(如 f2 中);在这种情况下,文件可以表示为 ASCII。或者您可以将其编写为 UTF-8,在这种情况下,您需要一个 8 位安全流。

            您使用decode('string-escape') 的解决方案确实有效,但您必须知道您使用了多少内存:是使用codecs.open() 的三倍。

            请记住,文件只是一个 8 位的字节序列。位和字节都没有意义。是你说“65 意味着 'A'”。由于\xc3\xa1应该变成“à”,但计算机无法知道,你必须通过指定写入文件时使用的编码来告诉它。

            【讨论】:

            • 我认为这里缺少一些部分:文件 f2 包含:十六进制:0000000:4361 7069 745c 7863 335c 7861 316e 0a Capit\xc3\xa1n。例如,codecs.open('f2','rb', 'utf-8') 以单独的字符读取它们(预期)有什么方法可以在 ascii 中写入文件吗?
            【解决方案11】:

            \x.. 序列是 Python 特有的。它不是通用的字节转义序列。

            您实际输入 UTF-8 编码的非 ASCII 的方式取决于您的操作系统和/或您的编辑器。 Here's how you do it in Windows。要让 OS X 输入带有重音的 a,您只需点击 option + E,然后点击 A,然后OS X 中几乎所有的文本编辑器都支持 UTF-8。

            【讨论】:

              【解决方案12】:

              您还可以改进原来的 open() 函数以处理 Unicode 文件,方法是使用 partial 函数替换它。这个解决方案的美妙之处在于您不需要更改任何旧代码。它是透明的。

              import codecs
              import functools
              open = functools.partial(codecs.open, encoding='utf-8')
              

              【讨论】:

                【解决方案13】:

                我试图使用 Python 2.7.9 解析 iCal

                从 icalendar 导入日历

                但我得到了:

                 Traceback (most recent call last):
                 File "ical.py", line 92, in parse
                    print "{}".format(e[attr])
                UnicodeEncodeError: 'ascii' codec can't encode character u'\xe1' in position 7: ordinal not in range(128)
                

                它被修复了:

                print "{}".format(e[attr].encode("utf-8"))
                

                (现在它可以像老板一样打印了。)

                【讨论】:

                  【解决方案14】:

                  我发现最简单的方法是将整个脚本的默认编码更改为“UTF-8”:

                  import sys
                  reload(sys)
                  sys.setdefaultencoding('utf8')
                  

                  任何openprint 或其他语句都将只使用utf8

                  至少对Python 2.7.9有效。

                  谢谢https://markhneedham.com/blog/2015/05/21/python-unicodeencodeerror-ascii-codec-cant-encode-character-uxfc-in-position-11-ordinal-not-in-range128/(看看最后)。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2018-06-13
                    • 2014-09-02
                    相关资源
                    最近更新 更多