Python中从文件读取字符

120
在文本文件中,有一个字符串"I don't like this"。
然而,当我将它读取到一个字符串中时,它变成了"I don‘t like this"。我理解"\u2018"是"'"的Unicode表示。
f1 = open (file1, "r")
text = f1.read()

执行读取操作的命令。

现在,有没有可能以这样的方式读取字符串,即当它被读入字符串时,它是"I don't like this",而不是"I don\xe2\x80\x98t like this like this"?

第二次编辑:我看到一些人使用映射来解决这个问题,但实际上,是否有内置的转换可以进行这种ANSI到Unicode(反之亦然)的转换?


一些评论:我看到有些人使用映射来解决这个问题,但是真的没有内置的转换可以进行这种 ANSI 到 Unicode(反之亦然)的转换吗?谢谢! - Graviton
没有这样的东西,因为有成千上万的Unicode代码点。你怎么决定哪些应该映射到哪些ASCII字符呢? - John Millikin
2
顺便提一下,你的文本文件有问题!U+2018是“左单引号”,而不是常见的撇号(U+0027)。 - user3850
John,你的评论是错误的,至少就一般情况而言是这样。iconv库可以用于将Unicode字符(甚至是与地区相关的)转换为ASCII字符。 $ python -c 'print u"\u2018".encode("utf-8")' | iconv -t 'ascii//translit' | xxd 0000000: 270a - user3850
问题是,你需要将UNICODE转换为ASCII(而不是相反)。 - hasen
9个回答

193

参考:http://docs.python.org/howto/unicode

因此,从文件中读取Unicode很简单:

import codecs
with codecs.open('unicode.rst', encoding='utf-8') as f:
    for line in f:
        print repr(line)

还可以以更新模式打开文件,允许读取和写入:

with codecs.open('test', encoding='utf-8', mode='w+') as f:
    f.write(u'\u4500 blah blah blah\n')
    f.seek(0)
    print repr(f.readline()[:1])

编辑: 我假设您的目标只是想在Python中正确地将文件读入字符串。如果您试图将Unicode转换为ASCII,则实际上没有直接的方法可以做到这一点,因为Unicode字符不一定存在于ASCII中。

如果您要转换为ASCII字符串,请尝试以下方法之一:

  1. 如果您只想处理一些特殊情况(如此特定的示例),请用ASCII相应字符替换特定的Unicode字符

  2. 使用unicodedata模块的normalize()方法和string.encode()方法,尽可能地转换为最接近的ASCII等效项(参考https://web.archive.org/web/20090228203858/http://techxplorer.com/2006/07/18/converting-unicode-to-ascii-using-python):

    >>> teststr
    u'I don\xe2\x80\x98t like this'
    >>> unicodedata.normalize('NFKD', teststr).encode('ascii', 'ignore')
    'I donat like this'
    

4
codecs 模块在处理通用换行模式时存在问题。在 Python 2.7+ 中应使用 io.open() 替代(在 Python 3 中内置于 open() 函数中)。 - jfs

23

使用Python 3的read方法也可以读取编码文本文件:

f = open (file.txt, 'r', encoding='utf-8')
text = f.read()
f.close()

使用这种变化,无需导入任何其他库。


15

有几个要考虑的点。

在Python中,'字符'只能作为Unicode字符串表示的一部分出现,例如,如果你写:

>>> text = u'‘'
>>> print repr(text)
u'\u2018'

现在,如果您只想漂亮地打印Unicode字符串,请使用Unicode的encode方法:
>>> text = u'I don\u2018t like this'
>>> print text.encode('utf-8')
I don‘t like this

为了确保从任何文件中读取的每一行都能被视为Unicode,请使用codecs.open函数而不是仅仅使用open,这使您可以指定文件的编码:
>>> import codecs
>>> f1 = codecs.open(file1, "r", "utf-8")
>>> text = f1.read()
>>> print type(text)
<type 'unicode'>
>>> print text.encode('utf-8')
I don‘t like this

6

但实际上是“I don‘t like this”,而不是“I don't like this”。字符u'\u2018'是一个完全不同的字符,而不是“'”(在视觉上应该对应于“`”)。

如果你想将编码的Unicode转换为普通ASCII,你可以保留一个Unicode标点符号映射到ASCII的映射表。

punctuation = {
  u'\u2018': "'",
  u'\u2019': "'",
}
for src, dest in punctuation.iteritems():
  text = text.replace(src, dest)

Unicode中有非常多的标点符号字符, 但我想你只能指望其中很少一部分被阅读文档所使用的应用程序实际使用。


1
实际上,如果你创建一个将Unicode序数映射到Unicode序数的字典({0x2018: 0x27, 0x2019: 0x27}),你可以将整个字典传递给text.translate()函数,以一次性完成所有替换。 - Thomas Wouters

3

有可能您有一个带有Unicode转义字符的非Unicode字符串,例如:

>>> print repr(text)
'I don\\u2018t like this'

这种情况我之前也遇到过。你可以使用unicode_escape编解码器将字符串解码为Unicode,然后再编码为任何你想要的格式:

>>> uni = text.decode('unicode_escape')
>>> print type(uni)
<type 'unicode'>
>>> print uni.encode('utf-8')
I don‘t like this

3
放下文本文件已经损坏的事实(U+2018是左引号,而不是撇号):iconv可以用于将Unicode字符转换为ASCII。
您需要搜索“iconvcodec”,因为该模块似乎不再受支持,我找不到它的官方网站。
>>> import iconvcodec
>>> from locale import setlocale, LC_ALL
>>> setlocale(LC_ALL, '')
>>> u'\u2018'.encode('ascii//translit')
"'"

另外,您可以使用iconv命令行实用程序来清理您的文件:

$ xxd foo
0000000: e280 980a                                ....
$ iconv -t 'ascii//translit' foo | xxd
0000000: 270a                                     '.

1
实际上,U+2018是特殊字符‘的Unicode表示。如果需要的话,可以使用以下代码将该字符的实例转换为U+0027:
text = text.replace (u"\u2018", "'")

另外,你用什么来写这个文件?f1.read()应该返回一个看起来像这样的字符串:
'I don\xe2\x80\x98t like this'

如果返回的是这个字符串,那么文件被错误地写入了:
'I don\u2018t like this'

抱歉!正如你所说,它返回了“我不喜欢这个”。 - Graviton
你看到的那个“I don't like this”是Python中称为str的内容。它似乎是u'I don't like this'的utf-8编码,这在Python中是一个unicode实例。尝试在前者上调用.decode('utf-8')或在后者上调用.encode('utf-8')。 - Logan
@hop:哎呀,我忘记了ord()返回的是十进制而不是十六进制。感谢你指出来。 - John Millikin

1
这是Python用来展示Unicode编码字符串的方式。但我认为您应该能够在屏幕上打印该字符串或将其写入新文件中,而不会遇到任何问题。
>>> test = u"I don\u2018t like this"
>>> test
u'I don\u2018t like this'
>>> print test
I don‘t like this

1

我不确定 (errors="ignore") 选项的作用,但它似乎适用于具有奇怪Unicode字符的文件。

with open(fName, "rb") as fData:
    lines = fData.read().splitlines()
    lines = [line.decode("utf-8", errors="ignore") for line in lines]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接