Python cho người Việt

Python cho người Việt

Entries tagged “unicode”

In chuỗi unicode ra màn hình

written by Nguyễn Thành Nam, on Dec 16, 2009 9:33:00 PM.

Một ngày nào đấy, có lẽ bạn sẽ rất bực mình khi gặp phải một lỗi tương tự như sau:

c:\tmp>c:\Python26\python.exe helloworld.py
Traceback (most recent call last):
  File "helloworld.py", line 3, in <module>
    print u"Xin chào bạn, Python!"
  File "c:\Python26\lib\encodings\cp437.py", line 12, in encode
    return codecs.charmap_encode(input,errors,encoding_map)
UnicodeEncodeError: 'charmap' codec can't encode character u'\u1ea1' in position
 10: character maps to <undefined>

Mã nguồn của helloworld.py chỉ đơn giản như sau:

# -*- encoding: utf-8 -*-

print u"Xin chào bạn, Python!"

Lỗi này xảy ra vì chúng ta đang in một chuỗi unicode, với các ký tự nằm ngoài bảng mã ASCII (hoặc bảng mã mặc định của màn hình hiện tại).

Khi gặp phải các ký tự nằm ngoài bảng mã này, Python mặc định sẽ nâng một biệt lệ cho biết chính xác đó là ký tự nào, nằm ở vị trí nào. Như ví dụ trên, đó là ký tự có mã unicode là 1EA1 nằm tại vị trí thứ 10 (vị trí bắt đầu là 0) trong chuỗi Xin chào bạn, Python!.

Có một số cách để khắc phục lỗi này, từ việc thay đổi một số biến môi trường, cho đến việc sửa từng dòng lệnh. Tất cả đều nhằm một mục đích là chuyển chuỗi unicode cần in thành chuỗi byte theo bảng mã UTF-8.

  • Thay đổi biến môi trường: Biến môi trường PYTHONIOENCODING được dùng để định nghĩa bảng mã cho các bộ nhập chuẩn, bộ xuất chuẩn, và bộ lỗi chuẩn (stdin, stdout, stderr). Chúng ta có thể định nghĩa biến môi trường này thành utf-8 như sau:

    c:\tmp>set PYTHONIOENCODING=utf-8
    
    c:\tmp>c:\Python26\python.exe helloworld.py
    Xin chào bạn, Python!
  • Thay đổi bộ xuất chuẩn: Bộ xuất chuẩn (sys.stdout) có thể được "gói" trong bảng mã mới thông qua mô-đun codecs.

    # -*- encoding: utf-8 -*-
    import codecs
    import sys
    
    writer_factory = codecs.getwriter("utf-8")
    sys.stdout = writer_factory(sys.stdout)
    
    print u"Xin chào bạn, Python!"
    c:\tmp>c:\Python26\python helloworld.py
    Xin chào bạn, Python!
  • Thay đổi từng dòng lệnh print:

    # -*- encoding: utf-8 -*-
    print u"Xin chào bạn, Python!".encode("utf-8")
    c:\tmp>c:\Python26\python helloworld.py
    Xin chào bạn, Python!

Như chúng ta thấy, cách nào cũng dẫn đến cùng một kết quả mong muốn. Việc áp dụng cách nào sẽ phụ thuộc vào hoàn cảnh của từng chương trình.

Duyệt qua các file trong một thư mục

written by Phạm Thị Minh Hoài, on Jun 20, 2009 10:45:00 AM.

Bạn thường gặp bài toán liệt kê danh sách tất cả các file trong một thư mục cho trước. Dưới đây là một vài các cách khác nhau tùy theo hoàn cảnh.

1. Sử dụng os.listdir

Câu lệnh sau sẽ lấy danh mục các file trong thư mục Test nằm trong thư mục cá nhân của bạn.

>>> import os
>>> path = "~/Test"
>>> os.listdir(os.path.expanduser(path))
['file 3', 'file 1', 'file 2']

os.listdir chỉ cho bạn tên file hoặc thư mục nằm trong đường dẫn đó. Ví dụ:

>>> path = "/mnt/data/pictures/"
>>> os.listdir(path)
['Old', 'Icons', 'baby.jpg']

Để có đường dẫn đầy đủ bạn có thể viết:

>>> FJoin = os.path.join
>>> files = [FJoin(path, f) for f in os.listdir(path)]
>>> files
['/mnt/data/pictures/Old', '/mnt/data/pictures/Icons', '/mnt/data/pictures/baby.jpg']

Để đảm bảo vấn đề tương thích mã khi chuyển qua lại giữa các hệ điều hành bạn luôn luôn nên dùng: os.path.join để nối các đường dẫn, hoặc dùng os.path.sep để cộng các đường dẫn.

Không nên:

path = path1 + "\path2"

Nên viết:

path = os.path.join(path1, "path2")

hoặc

path = path1 + os.path.sep + path2

Trên Windows bạn có thể viết: path = "~/Test" hoặc "~/test" đều được. Linux phân biệt chữ hoa chữ thường nên phải viết chính xác Test. Ký tự phân tách đường dẫn trên linux là "/", trên Windows bạn có thể viết "/" hoặc tôi hay dùng "\\" (thay vì `"\").

2. Sử dụng walk

os.listdir chỉ liệt kê các file và thư mục trong một thư mục. Để lấy được cả danh sách đệ quy các file trong thư mục bạn có thể dùng os.walk hoặc thư viện ngoài glob.

Hàm os.walk là một generator functionos.walk(path) là một generator object. Nghĩa là hàm trạng thái trả về các kết quả kế tiếp nhau theo yêu cầu. Mỗi một item của walk có ba thành phần:

  1. Thư mục hiện tại

  2. Các thư mục con

  3. Các file bên trong

Dưới đây là hàm sử dụng walk để liệt kê tất cả các file và thư mục con trong một thư mục cho trước, kết quả trả về chứa đường dẫn đầy đủ:

import os

FJoin = os.path.join

def GetFiles(path):
    """Output: file_list là danh sách tất cả các file trong path và trong tất cả các
       thư mục con bên trong nó. dir_list là danh sách tất cả các thư mục con
       của nó. Các output đều chứa đường dẫn đầy đủ."""

    file_list, dir_list = [], []
    for dir, subdirs, files in os.walk(path):
        file_list.extend([FJoin(dir, f) for f in files])
        dir_list.extend([FJoin(dir, d) for d in subdirs])
    return file_list, dir_list

if __name__ == "__main__":
    files, dirs = GetFiles(os.path.expanduser("~/Music"))
    for file in files:
        print file
    for dir in dirs:
        print dir

Hàm này chạy tốt trên Windows, trên Linux nó lấy cả các link. Nói chung các link sẽ không có ý nghĩa trong đa số các trường hợp của bạn. Để loại bỏ các link bạn viêt lại hàm GetFiles như sau:

def GetFiles(path):
    file_list, dir_list = [], []
    for dir, subdirs, files in os.walk(path):
        file_list.extend([FJoin(dir, f) for f in files])
        dir_list.extend([FJoin(dir, d) for d in subdirs])
    file_list = filter(lambda x: not os.path.islink(x), file_list)
    dir_list = filter(lambda x: not os.path.islink(x), dir_list)
    return file_list, dir_list

Chú ý rằng một object vừa có thể là file vừa có thể là link. Vì vậy bạn không thể dùng hàm os.path.isfile để lọc các link được:

>>> os.path.islink('/home/hoaiptm/Music/mylink')
True
>>> os.path.isfile('/home/hoaiptm/Music/mylink')
True

Tương tự như vậy với hàm os.path.isdir, nó cũng trả về True nếu object là link liên kết đến một thư mục khác.

3. Sử dụng glob

glob là cách liệt kê file và thư mục theo pattern, các pattern có thể chứa các ký tự đại diện như *, ?, [], nó cho phép bạn tìm kiếm chỉ các file có tên thỏa mãn quy tắc pattern cho trước. Ví dụ:

# liệt kê các file và thư mục trong thư mục path
glob.glob(os.path.join(path, "*"))
# liệt kê các file và thư mục cấp 2 bên trong thư mục path.
# chẳng hạn path chứa thư mục A, B, C thì lệnh trên sẽ
# liệt kê hết các file và thư mục bên trong A, B, C.
glob.glob(os.path.join(path, "*", "*"))
# liệt kê các file và thư mục trong thư mục path bắt đầu với chữ cái a.
glob.glob(os.path.join(path, "[a]*"))
# liệt kê các file và thư mục trong thư mục path
# có tên kết thúc bằng chữ cái p và có đúng 3 ký tự.
glob.glob(os.path.join(path, "??p"))

Chú ý rằng không giống như listdir hoặc walk, glob lấy đường dẫn đầy đủ.

Hàm glob không thực hiện đệ quy. Hàm sau đây sử dụng glob để tìm kiếm đệ quy tất cả các file và thư mục bên trong thư mục cho trước:

import os
from os import path
import glob

def dirwalk(dir, bag, wildcards):
    bag.extend(glob.glob(path.join(dir, wildcards)))
    for f in os.listdir(dir):
        fullpath = os.path.join(dir, f)
        if os.path.isdir(fullpath) and not os.path.islink(fullpath):
            dirwalk(fullpath, bag, wildcards)

files = []

# Lấy tất cả các file và thư mục con trong thư mục path:
dirwalk(path, files,  "*")

# Lấy tất cả các file trong thư mục path (có thể có lẫn thư mục):
dirwalk(path, files, "*.*")

# Lấy tất cả các file python trong thư mục path:
dirwalk(path, files,  "*.py")

# Lấy tất cả các file hoặc thư mục bắt đầu bằng "py" trong thư mục path:
dirwalk(path, files,  "py*.*")

Hàm dirwalk ở triển khai này làm được nhiều việc hơn hàm GetFiles triển khai ở mục 2. Nó tổng quát hơn.

Một cách triển khai khác có thể viết như thế này:

def dirwalk2(dir, bag, wildcards):
    """ bag là một list chứa các file thỏa mãn quy tắc wildcards """

    if glob.glob(path.join(dir, "*")):
        bag.extend(glob.glob(path.join(dir, wildcards)))
        dirwalk2(path.join(dir, "*"), bag, wildcards)

dirwalk2 tuy ngắn gọn nhưng khó hiểu hơn dirwalk. Nó hoàn toàn tương tự ngoại trừ việc không kiểm tra một object có thể là link (chỉ có ý nghĩa trên linux).

4. Thư mục có chứa tên file tiếng Việt

Các ví dụ phía trên giả thiết rằng bạn có các tên file và thư mục là tiếng anh thông thường hoặc tiếng việt không dấu. Thư mục có thể chứa các file có tên tiếng việt hoặc tiếng Tàu, tiếng Hàn... Bạn phải truyền đối số unicode cho các hàm listdir, walk, glob... Quy tắc này áp dụng cho tất cả các hàm và thủ tục khác thao tác với file. Quan sát ví dụ sau (trên Windows):

>>> import os
>>> FJoin = os.path.join
>>> FExists = os.path.exists
>>> path = "D:/abc"
>>> os.listdir(path)
['V? mi?n t\xe2y']
>>> [FJoin(path, f) for f in os.listdir(path)]
['D:/abc\\V? mi?n t\xe2y']
>>> [FExists(FJoin(path, f)) for f in os.listdir(path)]
[False]

Thư mục abc có chứa duy nhất file Về miền tây, tên file là tiếng Việt có dấu, kết quả kiểm tra sự tồn tại cho thấy file không tồn tại. Nguyên nhân là từ hàm listdir. Nêu đối số của hàm listdir là unicode các tên file lấy về cũng là unicode, nếu là ascii, các tên file trả về cũng là dạng ascii. Vì vậy tên file trả về của listdir trong trường hợp này không đúng và bạn không thể thao tác với file đó được. Để lấy đúng tên file trong trường hợp này bạn phải truyền đối số unicode cho hàm listdir:

>>> path = u"D:/abc"
>>> [FJoin(path, f) for f in os.listdir(path)]
[u'D:/abc\\V\u1ec1 mi\u1ec1n t\xe2y']
>>> [FExists(FJoin(path, f)) for f in os.listdir(path)]
[True]

Tuy nhiên trên Linux, bạn không gặp vấn đề này.

5. Bài tập ví dụ:

Viết chương trình đổi tên tất cả các file trong thư mục c:\data và các thư mục con của nó. Đổi tên các file có phần mở rộng là *.htm thành *.docx.

Dưới đây là một triển khai của bài tập này:

# -*- coding: utf-8 -*-
#!/usr/bin/env python

from os import path
import os
import glob

def dirwalk(dir, bag, wildcards):
    """ bag là một list chứa các file thỏa mãn quy tắc wildcards """

    if glob.glob(path.join(dir, "*")):
        bag.extend(glob.glob(path.join(dir, wildcards)))
        dirwalk(path.join(dir, "*"), bag, wildcards)

def rename(oldName, newExt):
    """ Thay thế phần mở rộng cũ thành mở rộng mới. Giả thiết oldName luôn có
     phần mở rộng. """

    newName = oldName[0:oldName.rfind(".") + 1] + newExt
    os.rename(oldName, newName)
    return newName

def WalkAndRename(dir, oldExt, newExt):
    files = []
    dirwalk(dir, files, u"*" + oldExt)

    for f in files:
        print f, "-->", rename(f, newExt)

if __name__ == "__main__":
    WalkAndRename('c:\\data', "htm", 'docx')

Xử lý tiếng Việt trong Python

written by Phạm Thị Minh Hoài, on Jun 14, 2009 2:23:00 AM.

Ví dụ sau minh họa 3 codecs bạn hay gặp nhất trong Python:

>>> s = "sky down no enemy"
>>> print type(s), len(s)
<type 'str'> 17
>>> s = "thiên hạ vô địch"
>>> s
'thi\xc3\xaan h\xe1\xba\xa1 v\xc3\xb4 \xc4\x91\xe1\xbb\x8bch'
>>> print type(s), len(s)
<type 'str'> 23
>>> s = unicode("thiên hạ vô địch", 'utf8')
>>> s
u'thi\xean h\u1ea1 v\xf4 \u0111\u1ecbch'
>>> print type(s), len(s)
<type 'unicode'> 16

Nghiên cứu ví dụ trên. len(s) trong hai ví dụ đầu thực sự là số byte cần để lưu trữ s. len(s) trong ví dụ 3 là độ dài ký tự, dung lượng nhớ thực sự để lưu trữ s là 16 * 2 = 32 bytes.

Tại bất cứ một thời điểm nào trong chương trình Python bạn cũng phải xác định được bạn đang thao tác với chuỗi kiểu gì: ascii, unicode, hay utf8... Dùng lệnh sau để biết một chuỗi là unicode hay không:

>>> s
u'thi\xean h\u1ea1 v\xf4 \u0111\u1ecbch'
>>> type(s) == type(u"")
True
>>> ss = "sky down no enemy"
>>> type(ss) == type(u"")
False
>>> s = "thiên hạ vô địch"
>>> type(ss) == type(u"")
False

Cộng một chuỗi không phải unicode với một chuỗi unicode thì chuỗi không phải unicode sẽ được tự động convert sang unicode trước khi cộng:

>>> ss = "sky down no enemy"
>>> ss + u""
u'sky down no enemy'
>>> ss = "thiên hạ vô địch"
>>> ss + u""
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 3: ordinal not in range(128)
>>> ss = unicode("thiên hạ vô địch", 'utf8')
>>> ss + u""
u'thi\xean h\u1ea1 v\xf4 \u0111\u1ecbch'

Trong tình huống thứ 2 bạn cố gắng cộng một chuỗi utf8 vào chuỗi unicode. Python tự động chuyển chuỗi utf8 thành chuỗi unicode bằng cách decode với codec='ascii', vì vậy gặp lỗi. Tình huống này tương đương với:

ss + u"" = ss.decode('ascii') + u""
>>> ss = "thiên hạ vô địch"
>>> ss.decode('ascii') + u""
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 3: ordinal not in range(128)

Hai cách để cộng đúng là:

ss.decode('utf8') + u""

hoặc

unicode(ss, 'utf8') + u""

Các thao tác với chuỗi khác như join, split, find... cũng tương tự. Nghĩa là nếu có một tham số unicode thì các tham số còn lại được tự động chuyển sang unicode trước khi thực hiện thao tác. Ví dụ:

>>> ss = "sky down no enemy"
>>> ss.split(u" ")
[u'sky', u'down', u'no', u'enemy']
>>> l = ["sky", "down", "no", "enemy"]
>>> unichr(32).join(l)
u'sky down no enemy'

Tương tự thao tác sau sẽ gây lỗi:

>>> s = "thiên hạ vô địch"
>>> s.split(u" ")
Traceback (most recent call last):
  File "<string>", line 1, in <string>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 3: ordinal not in range(128)

Để chuyển một chuỗi tiếng Việt utf sang dạng viết hoa, bạn luôn luôn phải chuyển nó về unicode trước. Nghiên cứu ví dụ sau:

>>> s = "thiên hạ vô địch"
>>> s.upper()
'THI\xc3\xaaN H\xe1\xba\xa1 V\xc3\xb4 \xc4\x91\xe1\xbb\x8bCH'
>>> s
'thi\xc3\xaan h\xe1\xba\xa1 v\xc3\xb4 \xc4\x91\xe1\xbb\x8bch'
<</sourcecode>>

So sánh với s ta thấy s.upper() không upper case được các ký tự ê, , ô, đ, ...

Để upper một chuỗi utf bạn phải chuyển nó về unicode trước, upper xong thì chuyển ngược lại. Hàm Upper sau nhận đầu vào là chuỗi utf, upper nó và trả về utf khác đã được upper, hàm này thích hợp cho mục đích upper tiếng Việt:

# -*- coding: utf-8 -*-

def Upper(s):
    if type(s) == type(u""):
        return s.upper()
    return unicode(s, "utf8").upper().encode("utf8")

if __name__ == "__main__":
    s = "thiên hạ vô địch"
    us = s.decode('utf8')

    print s.upper()
    print Upper(s)
    print Upper(us)

Kết quả:

THIêN Hạ Vô địCH
THIÊN HẠ VÔ ĐỊCH
THIÊN HẠ VÔ ĐỊCH

Đầu vào thích hợp của Upper là ascii, utf8, unicode, các dạng khác có thể sai: latin1, tcvn... Tương tự như vậy cho hàm lower, và capwords. Xem xét ví sau với capwords:

# -*- coding: utf-8 -*-

import string

s = "thiên hạ vô địch"

print string.capwords(s)
print string.capwords(s.decode('utf8'))

Kết quả:

Thiên Hạ Vô địch
Thiên Hạ Vô Địch

Các tên file hoặc tên thư mục là tiếng Việt có dấu cũng đòi hỏi cách xử lý đặc biệt. Giả sử bạn có thư mục abc với một file duy nhất tên là: tiếng việt.txt. Thư mục abc đặt trong thư mục cá nhân của bạn. Nghiên cứu đoạn chương trình sau:

# -*- coding: utf-8 -*-

import os

path = os.path.join(os.path.expanduser("~"), "abc")
files = [os.path.join(path, basename) for basename in os.listdir(path)]

print map(os.path.exists, files)

Chú ý:

  • Trên Windows path = "C:\Documents and Settings\YourAccountName\abc"

  • Trên Linux path = "/home/YourAccountName/abc"

Vì chỉ có duy nhất một file tiếng việt.txt trong thư mục abc nên chúng ta mong đợi kết quả in ra là: [True]. Tuy nhiên chỉ Linux cho câu trả lời này. Windows thì không. Không tin bạn thử xem. Chương trình trên cho dù đã được viết với việc tận dụng thư viện sẵn có os nhằm nâng cao tính khả chuyển nhưng vẫn không khả chuyển, khi tên file là tiếng Việt có dấu.

Giải quyết vấn đề này rất đơn giản bạn chỉ cần thay "abc" thành u"abc" để tất cả các đường dẫn tên file bị ép chuyển sang unicode là OK. Bạn làm như sau:

path = os.path.join(os.path.expanduser("~"), u"abc")

hoặc

path = os.path.join(os.path.expanduser(u"~"), "abc")

Các vấn đề tương tự cũng áp dụng cho hàm glob hoặc walk, Các hàm này cần các đường dẫn là chuỗi unicode để có thể lấy chính xác các file có tên tiếng Việt.

Loại bỏ dấu tiếng Việt. Trong nhiều trường hợp bạn cần loại bỏ dấu của một chuỗi tiếng Việt có dấu. Chẳng hạn chuyển tiếng việt thành tieng viet. Đây là một cách thức đơn giản giải quyết vấn đề này:

# -*- coding: utf-8 -*-

import string
import re

INTAB = "ạảãàáâậầấẩẫăắằặẳẵóòọõỏôộổỗồốơờớợởỡéèẻẹẽêếềệểễúùụủũưựữửừứíìịỉĩýỳỷỵỹđ"
INTAB = [ch.encode('utf8') for ch in unicode(INTAB, 'utf8')]

OUTTAB = "a"*17 + "o"*17 + "e"*11 + "u"*11 + "i"*5 + "y"*5 + "d"

r = re.compile("|".join(INTAB))
replaces_dict = dict(zip(INTAB, OUTTAB))

def khongdau(utf8_str):
    return r.sub(lambda m: replaces_dict[m.group(0)], utf8_str)

print khongdau("thiên hạ vô địch")
print khongdau("sky down no enemy")
print khongdau("THIÊN HẠ VÔ ĐỊCH")

Kết quả:

thien ha vo dich
sky down no enemy
THIÊN HẠ VÔ ĐỊCH

Test thứ ba cho thấy nó chưa làm việc với chữ hoa. Một chút cải tiến nhỏ để nó làm việc với chữ hoa các bạn có thể thêm vào dễ dàng. Chương trình này chưa được test kỹ về sự chính xác và về performance. Các bạn tự test và công bố kết quả nhé.

Slice với chuỗi tiếng việt dạng utf8 có thể gặp các vấn đề. Thao tác tương tự khi bạn đọc các khối dữ liệu utf8 với kích thước quy định trước từ file utf8. Xem xét các ví dụ sau:

>>> s = 'thiên hạ vô địch'
>>> s
'thi\xc3\xaan h\xe1\xba\xa1 v\xc3\xb4 \xc4\x91\xe1\xbb\x8bch'
>>> s[7:17]
'h\xe1\xba\xa1 v\xc3\xb4 \xc4'

chuỗi s[7:17] là chuỗi què. byte cuối cùng của chuỗi này \xc4 mới là một nửa của chữ cái đ ('\xc4\x91'), vì vậy mọi thao tác của bạn trên chuỗi này có tiềm năng gặp lỗi. chẳng hạn:

>>> unicode(s[7:17], 'utf8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.6/encodings/utf_8.py", line 16, in decode
    return codecs.utf_8_decode(input, errors, True)
UnicodeDecodeError: 'utf8' codec can't decode byte 0xc4 in position 9: unexpected end of data
>>> unicode(s[7:16], 'utf8')
u'h\u1ea1 v\xf4 '

Unicode áp dụng cho s[7:16] thì vô tư vì nó không bị què.

Việc đọc các file text utf8 cũng gặp tình huống tương tự. Chẳng hạn thao tác sau tiềm năng gặp lỗi:

f = open("file name", "r")
s = f.read(1000)
...
s.close()

Trong tình huống này bạn cố gắng đọc 1000 byte đầu tiên của file, s có thể là chuỗi tiếng Việt bị què như tình huống ở trên.

Đọc ghi file dữ liệu tiếng Việt. File chứa dữ liệu dạng văn bản tiếng Việt thường được ghi dưới dạng unicode hoặc utf8. Đoạn chương trình sau đọc nội dung utf8:

ff = open("anyfile", 'r')
content = ff.read()
ff.close()

Dữ liệu tiếng Việt lưu dưới dạng utf8 thường có BOM_UTF8 (= "\xef\xbb\xbf") ở đầu file. Bạn phải loại bỏ cái này trước khi có thể làm cái gì đó. Đoạn chương trình sau làm việc này:

import codecs

content = open("anyfile", 'r').read()
if content.startswith(codecs.BOM_UTF8):
    content = content[3:]

Chú ý rằng đoạn chương trình trên không thích hợp cho việc xử lý các file lớn (hơn 200MB). Với các file lớn bạn cần chia nhỏ thành các file nhỏ hơn.

codecs là thư viện chứa rất nhiều BOM.

>>> dir(codecs)
['BOM', 'BOM32_BE', 'BOM32_LE', 'BOM64_BE', 'BOM64_LE', 'BOM_BE', 'BOM_LE', 'BOM_UTF16', 'BOM_UTF16_BE', 'BOM_UTF16_LE', 'BOM_UTF32',
'BOM_UTF32_BE', 'BOM_UTF32_LE', 'BOM_UTF8', ...]

Dùng thư viện codecs bạn có thể nhanh chóng đọc nội dung file mà không mất nhiều công biến đổi encoding. Ví dụ:

# đọc toàn bộ nội dung của file vào content,
# nội dung của file được biết trước như là utf8,
# content sẽ là nội dung unicode.
content = codecs.open('your file name', 'r', 'utf8').read()
# mặc định đọc toàn bộ nội dung của file vào content
# (dạng mặc định là utf8)
content = codecs.open('your file name', 'r').read()

Ghi dữ liệu tiếng Việt ra file:

ff = open("filename", 'w').write(content)

Ở đây content là chuỗi utf8, nếu bạn đưa vào content là dạng unicode, nó sẽ được tự động chuyển về dạng utf8 trước khi được ghi ra file.