簡體   English   中英

Unicode (UTF-8) 在 Python 中讀取和寫入文件

[英]Unicode (UTF-8) reading and writing to files in Python

我在理解對文件(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'

我發現在打開文件時指定編碼更容易,而不是弄亂編碼和解碼方法。 io模塊(在 Python 2.6 中添加)提供了一個io.open函數,該函數具有一個 encoding 參數。

使用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模塊。 編解碼器模塊在混合read()readline()時可能會導致問題,所以這個答案現在推薦使用io模塊。

使用 codecs 模塊中的 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

在符號中

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的內容與您使用編輯器保存的文件的內容進行比較。

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

[在 2016-02-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)

編碼是用於解碼或編碼文件的編碼名稱。 這應該只在文本模式下使用。 默認編碼取決於平台(無論locale.getpreferredencoding()返回什么),但可以使用 Python 支持的任何文本編碼 有關支持的編碼列表,請參閱codecs模塊。

所以通過將encoding='utf-8'作為參數添加到 open 函數中,文件的讀取和寫入都是以 utf8 完成的(這也是現在 Python 中所做的一切的默認編碼。)

實際上,這對我在 Python 3.2 中讀取 UTF-8 編碼的文件很有用:

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

所以,我找到了我正在尋找的解決方案,即:

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

有一些不尋常的編解碼器在這里很有用。 這種特殊的讀取允許人們從 Python 中獲取 UTF-8 表示,將它們復制到 ASCII 文件中,然后將它們讀入 Unicode。 在“字符串轉義”解碼下,斜線不會加倍。

這允許我想象的那種往返。

# -*- 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()

除了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

要讀取 Unicode 字符串然后發送到 HTML,我這樣做了:

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

對 python 驅動的 http 服務器很有用。

您偶然發現了編碼的一般問題:如何判斷文件采用哪種編碼?

答:除非文件格式為此提供,否則您不能這樣做。 例如,XML 以以下開頭:

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

該標頭經過精心選擇,因此無論編碼如何都可以讀取。 在您的情況下,沒有這樣的提示,因此您的編輯器和 Python 都不知道發生了什么。 因此,您必須使用codecs模塊並使用codecs.open(path,mode,encoding)來提供 Python 中的缺失位。

至於你的編輯器,你必須檢查它是否提供了某種方式來設置文件的編碼。

UTF-8 的重點是能夠將 21 位字符 (Unicode) 編碼為 8 位數據流(因為這是世界上所有計算機都可以處理的唯一內容)。 但是由於大多數操作系統早於 Unicode 時代,它們沒有合適的工具將編碼信息附加到硬盤上的文件。

下一個問題是 Python 中的表示。 這在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\u0026quot; 使用四個字符將 Unicode 編碼為 0xFFFF (65535)。

所以你不能直接將 Unicode 寫入 ASCII(因為 ASCII 根本不包含相同的字符)。 您可以將其寫為字符串轉義(如 f2); 在這種情況下,文件可以表示為 ASCII。 或者您可以將其編寫為 UTF-8,在這種情況下,您需要一個 8 位安全流。

您使用decode('string-escape')解決方案確實有效,但您必須知道您使用了多少內存:使用codecs.open()的數量的codecs.open()

請記住,文件只是具有 8 位的字節序列。 位和字節都沒有意義。 是你說“65 意味着‘A’”。 由於\\xc3\\xa1應該變成“à”但計算機無法知道,因此您必須通過指定寫入文件時使用的編碼來告訴它。

好吧,您最喜歡的文本編輯器沒有意識到\\xc3\\xa1應該是字符文字,但它會將它們解釋為文本。 這就是為什么你在最后一行得到雙反斜杠的原因——它現在在你的文件中是一個真正的反斜杠 + xc3等。

如果你想在 Python 中讀寫編碼文件,最好使用codecs模塊。

在終端和應用程序之間粘貼文本很困難,因為您不知道哪個程序將使用哪種編碼來解釋您的文本。 您可以嘗試以下操作:

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

然后將此字符串粘貼到您的編輯器中,並確保它使用 Latin-1 存儲它。 在剪貼板沒有亂碼的假設下,往返應該可以工作。

\\x.. 序列是 Python 特有的。 它不是通用字節轉義序列。

您實際輸入 UTF-8 編碼的非 ASCII 的方式取決於您的操作系統和/或編輯器。 以下是您在 Windows 中的操作方法 要讓 OS X 輸入帶有重音符號的a ,您只需點擊option + E ,然后點擊A ,並且幾乎所有 OS X 中的文本編輯器都支持 UTF-8。

您還可以改進原始open()函數以通過使用partial函數替換它來處理 Unicode 文件。 此解決方案的美妙之處在於您無需更改任何舊代碼。 它是透明的。

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

我試圖使用 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"))

(現在它可以像 á böss 一樣打印。)

通過將整個腳本的默認編碼更改為“UTF-8”,我找到了最簡單的方法:

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

任何openprint或其他語句將只使用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/ (看最后)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM