关于python:将Unicode文本写入文本文件?

Writing Unicode text to a text file?

我正在从谷歌文档中提取数据,并对其进行处理,然后将其写入一个文件(最终我将粘贴到WordPress页面中)。

它有一些非ASCII符号。如何将这些安全地转换为可在HTML源代码中使用的符号?

目前,我正在将所有内容转换为unicode,将其连接到一个python字符串中,然后执行以下操作:

1
2
3
import codecs
f = codecs.open('out.txt', mode="w", encoding="iso-8859-1")
f.write(all_html.encode("iso-8859-1","replace"))

最后一行出现编码错误:

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

部分溶液:

此python运行时没有出错:

1
2
3
4
row = [unicode(x.strip()) if x is not None else u'' for x in row]
all_html = row[0] +"<br/>" + row[1]
f = open('out.txt', 'w')
f.write(all_html.encode("utf-8")

但是如果我打开实际的文本文件,我会看到很多符号,比如:

1
Qur???an

也许我需要写一些文本文件以外的东西?


当您第一次得到Unicode对象时,通过将其解码为Unicode对象,并在退出时根据需要对其进行编码,尽可能地专门处理Unicode对象。

如果字符串实际上是Unicode对象,则在将其写入文件之前,需要将其转换为Unicode编码的字符串对象:

1
2
3
4
foo = u'Δ, Й, ?, ? ?, ?, あ, 叶, 葉, and ?.'
f = open('test', 'w')
f.write(foo.encode('utf8'))
f.close()

当您再次读取该文件时,您将得到一个Unicode编码的字符串,您可以将其解码为Unicode对象:

1
2
f = file('test', 'r')
print f.read().decode('utf8')


在python 2.6+中,可以使用io.open(),这是python 3上的默认值(内置open()):

1
2
3
4
import io

with io.open(filename, 'w', encoding=character_encoding) as file:
    file.write(unicode_text)

如果需要增量地编写文本(不需要多次调用unicode_text.encode(character_encoding)),可能会更方便。与codecs模块不同,io模块具有适当的通用换行支持。


Unicode字符串处理在Python3中是标准化的。

  • 字符以Unicode存储
  • 您只需要以UTF-8格式打开文件

    1
    2
    3
    4
    out1 ="(嘉南大圳 ㄐㄧㄚ ㄋㄢˊ ㄉㄚˋ ㄗㄨㄣˋ )"
    fobj = open("t1.txt","w", encoding="utf-8")
    fobj.write(out1)
    fobj.close()

  • codecs.open打开的文件是一个接收unicode数据、用iso-8859-1编码并写入文件的文件。然而,你试图写的不是unicode;你拿unicode自己用iso-8859-1编码。这就是unicode.encode方法所做的,编码unicode字符串的结果是一个字节字符串(str类型)。

    您应该使用普通的open()并自己对unicode进行编码,或者(通常更好)使用codecs.open(),而不是自己对数据进行编码。


    前言:你的观众会工作吗?

    确保您的查看器/编辑器/终端(但是您正在与UTF-8编码文件交互)可以读取该文件。这通常是Windows上的一个问题,例如记事本。

    Writing Unicode text to a text file?

    在python 2中,使用来自io模块的open(这与python 3中内置的open相同):

    1
    import io

    一般来说,最佳实践是使用UTF-8来写入文件(我们甚至不必担心UTF-8的字节顺序)。

    1
    encoding = 'utf-8'

    UTF-8是最现代和最通用的编码方式——它适用于所有Web浏览器、大多数文本编辑器(如果有问题,请参阅您的设置)和大多数终端/外壳。

    在Windows上,如果您仅限于在记事本(或其他有限的查看器)中查看输出,则可以尝试使用utf-16le

    1
    encoding = 'utf-16le' # sorry, Windows users... :(

    只需使用上下文管理器打开它并写出您的Unicode字符:

    1
    2
    with io.open(filename, 'w', encoding=encoding) as f:
        f.write(unicode_object)

    使用许多Unicode字符的示例

    下面是一个示例,它尝试将每个可能的字符映射到3位宽(4是最大值,但这可能会有点远),从数字表示(整数)到编码的可打印输出,如果可能的话,还包括其名称(将其放入名为uni.py的文件中):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    from __future__ import print_function
    import io
    from unicodedata import name, category
    from curses.ascii import controlnames
    from collections import Counter

    try: # use these if Python 2
        unicode_chr, range = unichr, xrange
    except NameError: # Python 3
        unicode_chr = chr

    exclude_categories = set(('Co', 'Cn'))
    counts = Counter()
    control_names = dict(enumerate(controlnames))
    with io.open('unidata', 'w', encoding='utf-8') as f:
        for x in range((2**8)**3):
            try:
                char = unicode_chr(x)
            except ValueError:
                continue # can't map to unicode, try next x
            cat = category(char)
            counts.update((cat,))
            if cat in exclude_categories:
                continue # get rid of noise & greatly shorten result file
            try:
                uname = name(char)
            except ValueError: # probably control character, don't use actual
                uname = control_names.get(x, '')
                f.write(u'{0:>6x} {1}    {2}
    '
    .format(x, cat, uname))
            else:
                f.write(u'{0:>6x} {1}  {2}  {3}
    '
    .format(x, cat, char, uname))
    # may as well describe the types we logged.
    for cat, count in counts.items():
        print('{0} chars of category, {1}'.format(count, cat))

    这应该以大约一分钟的顺序运行,您可以查看数据文件,如果文件查看器可以显示Unicode,您将看到它。有关类别的信息可在此处找到。基于计数,我们可能可以通过排除没有相关符号的cn和co类别来改进结果。

    1
    $ python uni.py

    它将显示十六进制映射、类别、符号(除非无法获得名称,所以可能是控制字符)和符号的名称。例如

    我建议在Unix或Cygwin上使用less(不要将整个文件打印/分类到输出中):

    1
    $ less unidata

    例如,将显示类似于我使用python 2(unicode 5.2)从中采样的以下行:

    1
    2
    3
    4
    5
    6
    7
    8
    9
         0 Cc NUL
        20 Zs     SPACE
        21 Po  !  EXCLAMATION MARK
        b6 So  ?  PILCROW SIGN
        d0 Lu  D  LATIN CAPITAL LETTER ETH
       e59 Nd  ?  THAI DIGIT NINE
      2887 So  ?  BRAILLE PATTERN DOTS-1238
      bc13 Lo  ?  HANGUL SYLLABLE MIH
      ffeb Sm  ?  HALFWIDTH RIGHTWARDS ARROW

    我的Python3.5有Unicode8.0,我想大多数3的都有。


    如何将Unicode字符打印到文件中:

    将此保存到文件:foo.py:

    1
    2
    3
    4
    5
    6
    7
    #!/usr/bin/python -tt
    # -*- coding: utf-8 -*-
    import codecs
    import sys
    UTF8Writer = codecs.getwriter('utf8')
    sys.stdout = UTF8Writer(sys.stdout)
    print(u'e with obfuscation: é')

    运行它并将输出管道传输到文件:

    1
    python foo.py > tmp.txt

    打开tmp.txt查看内部,您会看到:

    1
    2
    el@apollo:~$ cat tmp.txt
    e with obfuscation: é

    因此,您已经将带有模糊标记的Unicode E保存到一个文件中。


    当您试图对一个非Unicode字符串进行编码时,就会出现这个错误:它试图对其进行解码,假设它是纯ASCII格式的。有两种可能性:

  • 您正在将其编码为bytestring,但是因为您使用了codecs.open,所以write方法需要一个unicode对象。所以你对它进行编码,然后它试图再次对它进行解码。试试:用f.write(all_html)代替。
  • 实际上,所有的HTML都不是Unicode对象。当你使用.encode(...)时,它首先尝试对其进行解码。

  • 如果写信给python3

    1
    2
    3
    4
    5
    6
    7
    8
    9
    >>> a = u'bats\u00E0'
    >>> print a
    batsà
    >>> f = open("/tmp/test","w")
    >>> f.write(a)
    >>> f.close()
    >>> data = open("/tmp/test").read()
    >>> data
    'batsà'

    如果写信给python2:

    1
    2
    3
    4
    5
    6
    7
    >>> a = u'bats\u00E0'
    >>> f = open("/tmp/test","w")
    >>> f.write(a)

    Traceback (most recent call last):
      File"<stdin>", line 1, in <module>
    UnicodeEncodeError: 'ascii' codec can't encode character u'\xe0' in position 4: ordinal not in range(128)

    要避免此错误,您必须使用编码解码器"utf-8"将其编码为字节,如下所示:

    1
    2
    >>> f.write(a.encode("utf-8"))
    >>> f.close()

    并在读取时使用编解码器"utf-8"对数据进行解码:

    1
    2
    3
    >>> data = open("/tmp/test").read()
    >>> data.decode("utf-8")
    u'bats\xe0'

    而且,如果您尝试在这个字符串上执行print,它将自动使用类似这样的"utf-8"编解码器进行解码。

    1
    2
    >>> print a
    batsà