大量文字檔編碼轉換

一座古蹟應該要修繕到什麼程度,有時候還真是個好問題。

 

話說本站的前一個版本(v5)是由靜態的 HTML 網頁檔所組成,數了一下,總共有 249 支網頁檔位於舊網站的目錄中,而其中絕大多數是採用繁體中文的 Big5 編碼。然而日前 DR 看著看著,突然覺得這些編碼應該要統一成 UTF-8 才是,於是便開始構思應該要怎麼處理才好。

 

雖然一開始很自然而然就會想到 iconv 這支工具,但隨後很快就會意識到,這項需求實則需要更加精細的功能。它應該要能夠偵測檔案的編碼,而不是僅檢查 <meta> 標籤的 charset 屬性,然後根據偵測結果來決定是否需要執行編碼轉換。除此之外,它也要能夠在轉換後自動修改 charset 屬性。

 

於是 DR 撰寫了一支 Python 程式,並使用 chardet 模組來做字元編碼的偵測,在 Fedora 23 上可以利用以下指令來安裝該模組:

  • sudo dnf install python-chardet python3-chardet

 

以下程式碼(text_encoding_conversion.py)會讀取同目錄及子目錄下的所有 HTML 檔,若字元編碼為 Big5 就會轉換為 UTF-8,並變更 charset 屬性值:

#!/usr/bin/python
# -*- coding: utf-8 -*-
import os, sys, re
import codecs
import chardet

EXTENSIONS = (".htm", ".html")
FROM_ENCODING = "big5"
TO_ENCODING = "utf-8"

def get_encoding(fn):
    raw_file = open(fn, "rb")
    encoding = chardet.detect(raw_file.read())["encoding"]
    raw_file.close()
    
    return encoding
    
def do_conversion(fn):
    input_file = codecs.open(fn, "r", FROM_ENCODING)
    content = input_file.read()
    input_file.close()
    
    if "<meta " not in content:
        content = content.replace("<head>",
        "<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\" />" % TO_ENCODING)
    else:
        from_charset = re.compile(re.escape("charset=%s" % FROM_ENCODING), re.I)
        to_charset = "charset=%s" % TO_ENCODING
        content = re.sub(from_charset, to_charset, content)
    
    output_file = codecs.open(fn, "w", TO_ENCODING)
    output_file.write(content)
    output_file.close()

def text_encoding_conversion(path):
    total = 0
    converted = 0
    
    for dirname, dirnames, filenames in os.walk(path):
        for fn in filenames:
            if fn.lower().endswith(EXTENSIONS):
                total = total + 1
                full_path = os.path.join(dirname, fn)
                encoding = get_encoding(full_path)
                print("%s - %s" % (encoding, full_path))
                
                if encoding and encoding.lower() == FROM_ENCODING:
                    do_conversion(full_path)
                    converted = converted + 1

    print("%d files checked, %d converted." % (total, converted))

if __name__ == "__main__":
    appdir = os.path.abspath(os.path.dirname(sys.argv[0]))
    text_encoding_conversion(appdir)
    

 

分類: