5.16 å¢žåŠ æˆ–æ”¹å�˜å·²æ‰“开文件的编ç �¶
问题¶
ä½ æƒ³åœ¨ä¸�å…³é—一个已打开的文件å‰�æ��ä¸‹å¢žåŠ æˆ–æ”¹å�˜å®ƒçš„Unicodeç¼–ç �。
解决方案¶
å¦‚æžœä½ æƒ³ç»™ä¸€ä¸ªä»¥äºŒè¿›åˆ¶æ¨¡å¼�æ‰“å¼€çš„æ–‡ä»¶æ·»åŠ Unicodeç¼–ç �/è§£ç �æ–¹å¼�,
�以使用 io.TextIOWrapper() 对象包装它。比如:
import urllib.request
import io
u = urllib.request.urlopen('http://www.python.org')
f = io.TextIOWrapper(u, encoding='utf-8')
text = f.read()
å¦‚æžœä½ æƒ³ä¿®æ”¹ä¸€ä¸ªå·²ç»�打开的文本模å¼�的文件的编ç �æ–¹å¼�,å�¯ä»¥å…ˆä½¿ç”¨ detach() 方法移除掉已å˜åœ¨çš„æ–‡æœ¬ç¼–ç �层,
并使用新的编ç �æ–¹å¼�代替。下é�¢æ˜¯ä¸€ä¸ªåœ¨ sys.stdout 上修改编ç �æ–¹å¼�的例å�:
>>> import sys
>>> sys.stdout.encoding
'UTF-8'
>>> sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding='latin-1')
>>> sys.stdout.encoding
'latin-1'
>>>
è¿™æ ·å�šå�¯èƒ½ä¼šä¸æ–ä½ çš„ç»ˆç«¯ï¼Œè¿™é‡Œä»…ä»…æ˜¯ä¸ºäº†æ¼”ç¤ºè€Œå·²ã€‚
讨论¶
I/O系统由一系列的层次构建而æˆ�ã€‚ä½ å�¯ä»¥è¯•ç�€è¿�行下é�¢è¿™ä¸ªæ“�作一个文本文件的例å�æ�¥æŸ¥çœ‹è¿™ç§�层次:
>>> f = open('sample.txt','w')
>>> f
<_io.TextIOWrapper name='sample.txt' mode='w' encoding='UTF-8'>
>>> f.buffer
<_io.BufferedWriter name='sample.txt'>
>>> f.buffer.raw
<_io.FileIO name='sample.txt' mode='wb'>
>>>
在这个例å�ä¸ï¼Œio.TextIOWrapper 是一个编ç �和解ç �Unicode的文本处ç�†å±‚,
io.BufferedWriter 是一个处�二进制数�的带缓冲的I/O层,
io.FileIO 是一个表示�作系统底层文件�述符的原始文件。
å¢žåŠ æˆ–æ”¹å�˜æ–‡æœ¬ç¼–ç �会涉å�Šå¢žåŠ æˆ–æ”¹å�˜æœ€ä¸Šé�¢çš„ io.TextIOWrapper 层。
一般æ�¥è®²ï¼Œåƒ�上é�¢ä¾‹å�è¿™æ ·é€šè¿‡è®¿é—®å±žæ€§å€¼æ�¥ç›´æŽ¥æ“�作ä¸�å�Œçš„层是很ä¸�安全的。 ä¾‹å¦‚ï¼Œå¦‚æžœä½ è¯•ç�€ä½¿ç”¨ä¸‹é�¢è¿™æ ·çš„æŠ€æœ¯æ”¹å�˜ç¼–ç �看看会å�‘生什么:
>>> f
<_io.TextIOWrapper name='sample.txt' mode='w' encoding='UTF-8'>
>>> f = io.TextIOWrapper(f.buffer, encoding='latin-1')
>>> f
<_io.TextIOWrapper name='sample.txt' encoding='latin-1'>
>>> f.write('Hello')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: I/O operation on closed file.
>>>
ç»“æžœå‡ºé”™äº†ï¼Œå› ä¸ºf的原始值已ç»�è¢«ç ´å��了并关é—了底层的文件。
detach() 方法会æ–开文件的最顶层并返回第二层,之å�Žæœ€é¡¶å±‚就没什么用了。例如:
>>> f = open('sample.txt', 'w')
>>> f
<_io.TextIOWrapper name='sample.txt' mode='w' encoding='UTF-8'>
>>> b = f.detach()
>>> b
<_io.BufferedWriter name='sample.txt'>
>>> f.write('hello')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: underlying buffer has been detached
>>>
一旦æ–开最顶层å�Žï¼Œä½ å°±å�¯ä»¥ç»™è¿”å›žç»“æžœæ·»åŠ ä¸€ä¸ªæ–°çš„æœ€é¡¶å±‚ã€‚æ¯”å¦‚ï¼š
>>> f = io.TextIOWrapper(b, encoding='latin-1')
>>> f
<_io.TextIOWrapper name='sample.txt' encoding='latin-1'>
>>>
尽管已ç»�å�‘ä½ æ¼”ç¤ºäº†æ”¹å�˜ç¼–ç �的方法, ä½†æ˜¯ä½ è¿˜å�¯ä»¥åˆ©ç”¨è¿™ç§�技术æ�¥æ”¹å�˜æ–‡ä»¶è¡Œå¤„ç�†ã€�错误机制以å�Šæ–‡ä»¶å¤„ç�†çš„å…¶ä»–æ–¹é�¢ã€‚例如:
>>> sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding='ascii',
... errors='xmlcharrefreplace')
>>> print('Jalape\u00f1o')
Jalapeño
>>>
注æ„�下最å�Žè¾“出ä¸çš„é�žASCIIå—符 ñ 是如何被 ñ å�–代的。