【问题标题】:How to create a temporary file with Unicode encoding?如何使用 Unicode 编码创建临时文件?
【发布时间】:2012-05-16 11:26:09
【问题描述】:

当我使用open() 打开文件时,我无法编写 unicode 字符串。我了解到我需要使用 codecs 并使用 Unicode 编码打开文件(请参阅 http://docs.python.org/howto/unicode.html#reading-and-writing-unicode-data)。

现在我需要创建一些临时文件。我尝试使用tempfile 库,但它没有任何编码选项。当我尝试使用 tempfile 在临时文件中写入任何 unicode 字符串时,它会失败:

#!/usr/bin/python2.6
# -*- coding: utf-8 -*-
import tempfile
with tempfile.TemporaryFile() as fh:
  fh.write(u"Hello World: ä")
  fh.seek(0)
  for line in fh:
    print line

如何在 Python 中使用 Unicode 编码创建临时文件?

编辑:

  1. 我使用的是 Linux,我收到的代码错误消息是:

    Traceback (most recent call last):
      File "tmp_file.py", line 5, in <module>
        fh.write(u"Hello World: ä")
    UnicodeEncodeError: 'ascii' codec can't encode character u'\xe4' in position 13: ordinal not in range(128)
    
  2. 这只是一个例子。在实践中,我正在尝试编写一些 API 返回的字符串。

【问题讨论】:

    标签: python unicode temporary-files


    【解决方案1】:

    其他人的答案都是正确的,我只是想澄清一下这是怎么回事:

    文字'foo'和文字u'foo'的区别在于前者是字节串,后者是Unicode对象。

    首先,了解 Unicode 是字符集。 UTF-8 是编码。 Unicode 对象与前者有关——它是一个 Unicode 字符串,不一定是 UTF-8 字符串。在您的情况下,字符串文字的编码将是 UTF-8,因为您在文件的第一行中指定了它。

    要从字节字符串中获取 Unicode 字符串,请调用 .encode() 方法:

    >>>> u"ひらがな".encode("utf-8") == "ひらがな"
    True
    

    同样,您可以在write 调用中调用您的string.encode,并获得与删除u 相同的效果。

    如果您没有在顶部指定编码,假设您正在从另一个文件中读取 Unicode 数据,您将指定它在到达 Python 字符串之前的编码。这将决定它如何以字节表示(即str 类型)。

    那么,您得到的错误仅仅是因为 tempfile 模块需要一个 str 对象。这 不 意味着它不能处理 unicode,只是它希望您传入一个字节字符串而不是 Unicode 对象——因为没有您指定编码,它不知道如何将其写入临时文件。

    【讨论】:

    • 是的。所以,没有必要用一些神奇的 unicode 选项打开临时文件,写一个显式编码的字符串就足够了:fh.write(u'föo bār'.encode('utf-8'))。如果您的大部分字符都是 CJK,请将 'utf-8' 替换为 'utf-16'。
    • @9000:如果您使用“utf-16”,请注意此方法。如果这样做,您将不得不一次写入整个文件,因为 encode('utf-16') 也会输出文件 BOM。如果您有多个字符串要写入同一个文件,则第一个字符串应使用 .encode('utf-16') ,随后的字符串应使用 .encode('utf-16-le') ,它们不会发送 BOM。使用一些神奇的 unicode 选项可以避免这个陷阱。
    • "abc" 是 Python 3 或 from __future__ import unicode_literals 中的 Unicode 字符串。
    【解决方案2】:

    tempfile.TemporaryFile 有encoding option in Python 3:

    #!/usr/bin/python3
    # -*- coding: utf-8 -*-
    import tempfile
    with tempfile.TemporaryFile(mode='w+', encoding='utf-8') as fh:
      fh.write("Hello World: ä")
      fh.seek(0)
      for line in fh:
        print(line)
    

    请注意,现在您需要指定 mode='w+' 而不是默认的二进制模式。另请注意,字符串文字在 Python 3 中是隐式 Unicode,没有 u 修饰符。

    如果您坚持使用Python 2.6, temporary files 始终是二进制的,您需要在将 Unicode 字符串写入文件之前对其进行编码:

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    import tempfile
    with tempfile.TemporaryFile() as fh:
      fh.write(u"Hello World: ä".encode('utf-8'))
      fh.seek(0)
      for line in fh:
        print line.decode('utf-8')
    

    Unicode 指定字符集,而不是编码,因此无论哪种情况,您都需要一种方法来指定如何对 Unicode 字符进行编码!

    【讨论】:

    • 在打印之前,最好也解码从文件中读取的 8 位字符串(在 Python 2 示例中),将其转换为 Unicode 字符串。 (修复它。)
    【解决方案3】:

    由于我正在开发一个包含 TemporaryFile 对象的 Python 程序,该对象应该在 Python 2 和 Python 3 中运行,因此我觉得像其他答案所建议的那样手动编码所有写为 UTF-8 的字符串并不令人满意。

    相反,我编写了以下小的 polyfill(因为我在六个中找不到类似的东西)来将二进制文件类对象包装成一个 UTF-8 文件类对象:

    from __future__ import unicode_literals
    import sys
    import codecs
    if sys.hexversion < 0x03000000:
        def uwriter(fp):
            return codecs.getwriter('utf-8')(fp)
    else:
        def uwriter(fp):
            return fp
    

    它的使用方式如下:

    # encoding: utf-8
    from tempfile import NamedTemporaryFile
    with uwriter(NamedTemporaryFile(suffix='.txt', mode='w')) as fp:
        fp.write('Hællo wörld!\n')
    

    【讨论】:

      【解决方案4】:

      我想出了一个解决方案:使用tempfile创建一个不会自动删除的临时文件,关闭它并使用codecs再次打开它:

      #!/usr/bin/python2.6
      # -*- coding: utf-8 -*-
      
      import codecs
      import os
      import tempfile
      
      f = tempfile.NamedTemporaryFile(delete=False)
      filename = f.name
      f.close()
      
      with codecs.open(filename, 'w+b', encoding='utf-8') as fh:
        fh.write(u"Hello World: ä")
        fh.seek(0)
        for line in fh:
          print line
      
      os.unlink(filename)
      

      【讨论】:

      • 很抱歉,这不是最理想的。请参阅@spinning_plate 的回答和我对它的评论;事情方式更简单。
      • @9000 我在这里没有看到spinning_plate 的答案。
      • @guettli:一定是某种错字;我一定是指dfb 的答案,目前已被接受。
      【解决方案5】:

      您正在尝试将 unicode 对象 (u"...") 写入应使用编码字符串 ("...") 的临时文件。您不必显式传递"encode=" 参数,因为您已经在第二行("# -*- coding: utf-8 -*-") 中说明了编码。只需使用fh.write("ä") 而不是fh.write(u"ä") 就可以了。

      【讨论】:

      • 是的,这可行,但我实际上是在尝试编写一些 API 返回的字符串,因此我的代码中没有 (u"...")。我已经用这些信息更新了我的问题。我尝试了一个包含两个文件的示例,fh.write(other_file.f()) 工作与否取决于另一个文件是否具有编码。在我的真实代码中,我无法控制创建字符串的代码。
      【解决方案6】:

      删除 u 使您的代码为我工作:

      fh.write("Hello World: ä")
      

      我猜是因为它已经是 unicode。

      【讨论】:

      • 是的,在没有 u 的 linux 机器上运行脚本会产生正确的输出 Hello World: ä
      • 是的,这行得通...实际上在我的真实程序中,我从一些 API 获取输入,但它失败了,所以这不是因为我的代码中的“u”。
      • @john:即使您在最终文件中获得了正确的 utf-8,删除 u 也可能不会按照您的想法进行。如果在键入“ä”时,您可能使用了一些 utf-8 编辑器,字符串中存储了两个字节。这很容易检查。如果是这样 len("Hello World: ä") 将是 15 并且 "Hello World: ä"[14] 将是 '\xa4'。
      【解决方案7】:

      将 sys 设置为默认编码为 UTF-8 将解决编码问题

      import sys
      reload(sys)
      sys.setdefaultencoding('utf-8') #set to utf-8 by default this will solve the errors
      
      import tempfile
      with tempfile.TemporaryFile() as fh:
        fh.write(u"Hello World: ä")
        fh.seek(0)
        for line in fh:
          print line
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-09-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-09-26
        • 1970-01-01
        • 2011-05-19
        相关资源
        最近更新 更多