代码之家  ›  专栏  ›  技术社区  ›  Gregg Lind

Unicode(UTF-8)在Python中读写文件

  •  410
  • Gregg Lind  · 技术社区  · 16 年前

    我在理解阅读和向文件写入文本(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'
    

    所以我输入 Capit\xc3\xa1n 进入我最喜欢的编辑器,文件f2。

    然后:

    >>> 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'
    

    我在这里有什么不明白的?显然,我缺少了一些重要的魔法(或良好的判断力)。在文本文件中键入什么才能获得正确的转换?

    我在这里真正没能理解的是,如果你不能让Python识别它,那么当它来自外部时,UTF-8表示的意义是什么。也许我应该直接用JSON转储字符串,并使用它,因为它有一个可用的表示!更重要的是,当从文件中输入时,Python会识别和解码这个Unicode对象的ASCII表示吗?如果是这样,我怎么得到它?

    >>> print simplejson.dumps(ss)
    '"Capit\u00e1n"'
    >>> print >> file('f3','w'), simplejson.dumps(ss)
    >>> simplejson.load(open('f3'))
    u'Capit\xe1n'
    
    14 回复  |  直到 8 年前
        1
  •  797
  •   quantum    6 年前

    与其捣乱 .encode .decode ,在打开文件时指定编码。这个 io module ,在Python 2.6中添加,提供了一个 io.open 函数,允许指定文件的 encoding .

    假设文件以UTF-8编码,我们可以使用:

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

    然后 f.read 返回一个解码的Unicode对象:

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

    在3.x中 io.open 函数是内置函数的别名 open 功能,支持 把…编码 参数(2.x中没有)。

    我们也可以使用 open from the codecs standard library module :

    >>> import codecs
    >>> f = codecs.open("test", "r", "utf-8")
    >>> f.read()
    u'Capit\xe1l\n\n'
    

    然而,请注意,这 can cause problems when mixing read() and readline() .

        2
  •  115
  •   unbeknown unbeknown    16 年前

    在符号中 u'Capit\xe1n\n' (应该是公正的 'Capit\xe1n\n' 在3.x中,以及 必须 在3.0和3.1中) \xe1 只代表一个字符。 \x 是一个逃逸序列,表明 e1 以十六进制表示。

    Capit\xc3\xa1n 在文本编辑器中放入文件意味着它实际上包含 \xc3\xa1 这些是8个字节,代码会读取它们。我们可以通过显示结果来看到这一点:

    # Python 3.x - reading the file as bytes rather than text,
    # to ensure we see the raw data
    >>> open('f2', 'rb').read()
    b'Capit\\xc3\\xa1n\n'
    
    # Python 2.x
    >>> open('f2').read()
    'Capit\\xc3\\xa1n\n'
    

    相反,只需输入以下字符 á 在编辑器中,它应该处理到UTF-8的转换并保存。

    在2.x中,实际包含这些反斜杠转义序列的字符串可以使用 string_escape 编解码器:

    # Python 2.x
    >>> print 'Capit\\xc3\\xa1n\n'.decode('string_escape')
    Capitán
    

    结果是 str 它以UTF-8编码,其中重音字符由写入的两个字节表示 \\xc3\\xa1 在原始字符串中。要获得 unicode 结果,用UTF-8再次解码。

    在3.x中 string_escape 编解码器被替换为 unicode_escape ,并且严格执行,我们只能 encode 从a 潜水艇用热中子反应堆 bytes ,以及 decode 来自 字节 潜水艇用热中子反应堆 . unicode_escape 需要从a开始 字节 为了处理逃逸序列(反之,它 增加 他们);然后它将处理结果 \xc3 \xa1 性格 逃跑而不是 字节 逃跑。因此,我们必须做更多的工作:

    # Python 3.x
    >>> 'Capit\\xc3\\xa1n\n'.encode('ascii').decode('unicode_escape').encode('latin-1').decode('utf-8')
    'Capitán\n'
    
        3
  •  65
  •   Derlin    7 年前

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

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

    Python3添加了 把…编码 将参数设置为其打开函数。以下是关于开放功能的信息: 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)
    

    编码是用于解码或编码的编码名称 文件。这只能在文本模式下使用。默认编码为 依赖于平台(无论什么 locale.getpreferredencoding() 返回),但任何 text encoding 可以使用Python支持的。 请参阅 codecs 模块,用于显示支持的编码列表。

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

        4
  •  18
  •   Peter Mortensen icecrime    8 年前

    所以,我找到了一个解决我所寻找的问题的方法,那就是:

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

    这里有一些不寻常的编解码器很有用。这种特殊的读取允许从Python中获取UTF-8表示,将其复制到ASCII文件中,并将其读入Unicode。在“字符串转义”解码下,斜线不会加倍。

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

        5
  •  17
  •   Peter Mortensen icecrime    8 年前

    这适用于在Python 3.2中读取UTF-8编码的文件:

    import codecs
    f = codecs.open('file_name.txt', 'r', 'UTF-8')
    for line in f:
        print(line)
    
        6
  •  14
  •   Ricardo    13 年前
    # -*- 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
  •  7
  •   Ryan    8 年前

    除了 codecs.open() , io.open() 可以在2.x和3.x中用于读取和写入文本文件。例子:

    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
    
        8
  •  6
  •   praj    10 年前

    为了读取Unicode字符串并发送到HTML,我这样做了:

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

    适用于python驱动的http服务器。

        9
  •  6
  •   Community CDub    8 年前

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

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

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

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

    然后将此字符串粘贴到编辑器中,并确保它使用Latin-1存储它。假设剪贴板不会混淆字符串,则往返应该有效。

        10
  •  5
  •   Peter Mortensen icecrime    8 年前

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

    答案:你不能 除非 文件格式为此提供了支持。例如,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包含:hex:

    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 应该变为“”,但计算机无法知道,您必须通过指定写入文件时使用的编码来告诉它。

        11
  •  4
  •   Peter Mortensen icecrime    8 年前

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

    您实际如何输入UTF-8编码的非ASCII取决于您的操作系统和/或编辑器。 Here's how you do it in Windows .让OS X进入 带着浓重的口音,你简直可以打 选项 + E 那么 A. ,OS X中几乎所有的文本编辑器都支持UTF-8。

        12
  •  3
  •   Peter Mortensen icecrime    8 年前

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

    import codecs
    import functools
    open = functools.partial(codecs.open, encoding='utf-8')
    
        13
  •  1
  •   Peter Mortensen icecrime    8 年前

    我试图解析 iCal 使用Python 2.7.9:

    从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"))
    

    (现在它可以打印lik-bss。)

        14
  •  -1
  •   dr0i    5 年前

    我找到了最简单的方法,将整个脚本的默认编码更改为“UTF-8”:

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

    任何 open , print 或其他语句将仅使用 utf8 .

    至少适用于 Python 2.7.9 .

    Thx前往 https://markhneedham.com/blog/2015/05/21/python-unicodeencodeerror-ascii-codec-cant-encode-character-uxfc-in-position-11-ordinal-not-in-range128/ (看结尾)。

    推荐文章