1. 程式人生 > 其它 >處理文字檔案

處理文字檔案

處理文字檔案

處理文字的最佳實踐是“Unicode三明治”。儘早把輸入的的位元組序列解碼成字串,然後對字串進行處理,在其他過程中一定不能編碼或解碼。對輸出來說,要儘量晚地把字串編碼成位元組序列

在Python3中能輕鬆的採納Unicode三明治的建議,因為內建的open函式會再讀取檔案時做必要的解碼,以文字模式寫入檔案時還會做必要的編碼,所以呼叫my_file.read()方法得到的以及傳給my_file.write(text)方法的都是字串物件

open('./cafe.txt', 'w', encoding='utf_8').write('café')
4
open('cafe.txt').read()
'caf茅'

寫入檔案時制定了UTF-8編碼,但是讀取檔案時沒那麼做,因此Python假定要使用系統預設的編碼(cp936),於是檔案的最後一個位元組解碼成了字元'茅',而不是'é'。

fp = open('cafe.txt', 'w', encoding='utf_8')
fp  # 預設情況下,open函式採用文字模式,返回一個TextIOWrapper物件
<_io.TextIOWrapper name='cafe.txt' mode='w' encoding='utf_8'>
fp.write('café')  # 在TextIOWrapper物件上呼叫write方法返回寫入的Unicode字元數
4
fp.close()
import os
os.stat('cafe.txt').st_size  # os.stat報告檔案中有5個位元組,UTF-8編碼的'é'佔兩個位元組,0xc3和0xa9
5
fp2 = open('cafe.txt')
fp2  # 開啟文字檔案時沒有顯式指定編碼,返回一個TextIOWrapper物件,編碼是區域設定中的預設值
<_io.TextIOWrapper name='cafe.txt' mode='r' encoding='cp936'>
fp2.encoding  # TextIOWrapper物件有個encoding屬性,可以檢視當前編碼是cp936
'cp936'
fp2.read()
'caf茅'
fp3 = open('cafe.txt', encoding='utf_8')  # 使用正確的編碼開啟文字檔案
fp3
<_io.TextIOWrapper name='cafe.txt' mode='r' encoding='utf_8'>
fp3.read() 結果符合預期,得到四個Unicode字元'café'
'café'
fp4 = open('cafe.txt', 'rb')  # 'rb'標誌指明在二進位制中讀取檔案
fp4
<_io.BufferedReader name='cafe.txt'>
fp4.read()  # 讀取返回的位元組序列,結果與預期相符
b'caf\xc3\xa9'

編碼預設值

import sys
import locale
expressions = '''
locale.getpreferredencoding()
type(my_file)
my_file.encoding
sys.stdout.isatty()
sys.stdout.encoding
sys.stdin.isatty()
sys.stdin.encoding
sys.stderr.isatty()
sys.stderr.encoding
sys.getdefaultencoding()
sys.getfilesystemencoding()'''
my_file = open('cafe.txt', 'w')
for expression in expressions.split():
    value = eval(expresion)
    print(expression.rjust(30), '->', repr(value))
 locale.getpreferredencoding() -> 'cp936'
                 type(my_file) -> 'cp936'
              my_file.encoding -> 'cp936'
           sys.stdout.isatty() -> 'cp936'
           sys.stdout.encoding -> 'cp936'
            sys.stdin.isatty() -> 'cp936'
            sys.stdin.encoding -> 'cp936'
           sys.stderr.isatty() -> 'cp936'
           sys.stderr.encoding -> 'cp936'
      sys.getdefaultencoding() -> 'cp936'
   sys.getfilesystemencoding() -> 'cp936'

在GNU/Linux和OS X中,這些編碼的預設值都是UTF-8,而且多年來都是如此,因此I/O能處理所有Unicode字元。

locale.getpreferreadencoding()返回的編碼是最重要的:這是開啟檔案的預設值,也是重定向到檔案sys.stdout/stdin/stderr的預設編碼。

關於編碼預設值的最佳建議是:別依賴預設值

如果遵從Unicode三明治的建議,而且始終在程式中顯式指定編碼,那將避免很多問題。

即使把位元組序列正確地轉換成字串,Unicode仍然有不如人意的地方