April 27, 2015

淺談電腦編碼與 Unicode (二) 基本應用篇

上一篇文章我們大致了解 電腦編碼與 Unicode,這篇則列出一些日常容易遇到的編碼問題。

大致可分為日常文書處理(一般使用者),與程式開發(工程師)兩部分。


目錄


(一) 日常文書處理與亂碼

如果文件用 A 編碼 encode,程式卻用 B 編碼 decode 該文件,就會產生亂碼。
如果程式用 B 編碼 encode,系統卻用 C 編碼開啟該程式,就會產生亂碼。

有了這個概念後,就可以解決八成的亂碼問題。

1. 開啟某程式時出現亂碼

A. Microsoft AppLocale

最常發生在較為老舊不支援 Unicode 程式,或是本身設計就含有地區限定概念的程式(e.g. 日本)。

當開啟該程式時,要不就是可正常執行但文字都是亂碼,要不就是跳出個警告視窗上面寫滿亂碼。

最簡單的解決方法是安裝微軟的 Microsoft AppLocale 公用程式 (或是 piaip大大 修正的 pAppLocale ),用 AppLocale 即可選擇要用什麼語言開啟什麼程式,解決亂碼問題。

B. 變更系統地區設定

AppLocale 的特色在於不必變更非 Unicode 應用程式的語言 (系統地區設定) 就可以執行舊版應用程式。

但有時候即使用了 AppLocale,程式依然會顯示亂碼,此時 Windows 下可透過

控制台 -> 時鐘語言和區域 -> 地區及語言 -> 系統管理 -> 變更系統地區設定

來改變非 Unicode 程式的預設語言,進而解決亂碼問題。

2. 開啟郵件、網頁時出現亂碼

網頁也算是檔案的一種,瀏覽器向 Web Server 要求資料後,Server 回傳動態或靜態資料,瀏覽器解析收到的 HTML、javascript、CSS 等,並將最後結果呈現在使用者面前。

若瀏覽器解碼的方式與網頁原先編碼的方式不同,呈現出來的網頁就會出現亂碼。

電子郵件雖然與網頁不完全相同,但概念差不多,也會因同樣的原因出現亂碼。

基本上網頁會在 HTML header 的 meta data 欄位註明其編碼資訊:

<head>
    <meta charset="utf-8">
</head>

而郵件開頭也會有一段字串註明這封郵件的編碼資訊:

Content-Type: text/plain; charset="UTF-8"

如果沒有這些資訊的話,瀏覽器會透過其他方式去猜測其編碼,如果猜錯了,呈現出來的就會是亂碼,使用者必須手動去調整。

解決郵件與網頁的亂碼很簡單,每個瀏覽器都可以選擇要用什麼編碼顯示網頁/郵件,只要選擇正確的編碼就能正常顯示惹。

各瀏覽器調整編碼的方式請參照:Gmail 說明 - 郵件文字顯示為亂碼

3. 開啟某個檔案時出現亂碼

與上述相同的道理,當我們使用如 Excel、Word 等文書軟體時,若開啟檔案時所用的編碼與檔案本身的編碼方式不相同時,使用者就會看見亂碼。

隨著 Unicode 的出現以及大家皆使用 Unicode 的共識下,文書處理已經越來越少遇到亂碼。

但是..

In general UTF-8 is more easily detected. Even so, Microsoft compilers and interpreters, and many pieces of software on Microsoft Windows such as Notepad will not correctly read UTF-8 text unless it has only ASCII characters or it starts with the BOM, and will add a BOM to the start when saving text as UTF-8. Google Docs will add a BOM when a Microsoft Word document is downloaded as a plain text file. – Wikipedia

微軟出品的文書軟體在上述兩個條件(ANSCII only, w/ BOM)以外,常常無法正確辨認出 UTF-8 編碼的檔案,偏偏微軟的文書軟體(Excel、Word等)又是大家最常用的工具..(だいたい microsoft のせい)。

用微軟的文書軟體開啟沒有 BOM 的 UTF-8 檔案,就會出現亂碼。

解決方式:轉換該檔案的編碼(下面會說明)。

4. 作業系統/軟體僅支援 Unicode

EX.1 OS X

看到這邊您可能會覺得微軟真差勁,怎麼對 Unicode 的支援程度那麼差呢!於是興沖沖的去買了潮到出水的 MAC Pro,用蘋果 OS X 內建的 Keynote, Pages, Numbers 製作精美的簡報與投影片,生活開始變得很美好。

直到有一天,你同事丟給你一份 Excel 轉出的 csv 檔案,要求你修改裡面的一些東西,你很有自信的覺得「OSX 也可以開啟 csv 檔案,沒問題的啦」,安心的點開該檔案後,發現裡面全是亂碼。

GG WP /ff report colleague

// How to reproduce:
Windows 開啟 Excel,在 A1 輸入 '安安',在 B1 輸入 '你好'
另存新檔,選擇 csv 檔案
將該 csv 檔案傳到 OSX 電腦
點兩下用 Numbers 開啟該檔,亂碼 GG

原因在於 OSX 預設是以 Unicode 開啟某些檔案,而 Excel 預設是以 ANSI 儲存 csv 檔案,這一來一往之下就造成了亂碼。

EX.2 Sublime Text

另一個經典的例子就是軟體工程師的神器 Sublime Text

據說每十個工程師就有五個娶了 Sublime Text,三個娶了初音,剩下兩個成為了魔法師。

Sublime Text 預設不支援 CJK 編碼,若我們用 Windows 內建的記事本將某文字檔存成 ANSI 編碼,則用 Sublime Text 開啟該檔案後會看到亂碼。

解決方式:將該檔案的編碼轉換為 Unicode 系列。


(二) 觀看/轉換文件的編碼

上述兩個 case 的解法都需要轉換檔案的編碼。

如果作業系統是 Windows 的話,建議下載 Notepad++,Notepad ++ 本身是個相當優質的文字編輯器,其轉換編碼的功能雖陽春,也足夠滿足大部分的使用需求了。

Notepad ++ 右下角可查看檔案編碼:

Notepad ++ 上方選單可轉換檔案編碼:

如果作業系統不是 Windows 的話,可參考這篇討論:Best way to convert text files between character sets?


(三) 程式開發與編碼

編碼與程式開發息息相關,有經驗的工程師或多或少都會遇過編碼產生的問題,以下列出幾個新手容易忽略的觀念。

1. 開發環境的編碼(檔案以什麼樣的編碼儲存)

從開發環境開始就與編碼息息相關,包含 文字編輯器 與 Integrated development environment (IDE)

文字編輯器例如上述提到的 Sublime Text。

IDE 最有名的例子應該就是 Eclipse 了,在早期版本的 Android Development Tools (ADT) 中,Eclipse 預設編碼為系統編碼。若系統編碼是繁體中文(台灣),Eclipse 預設編碼就會是 Big5,所有的程式碼與註解都會用 Big5 儲存,同一個檔案拿到 Unicode 編輯器的環境後,程式碼部分或許可以正確顯示,但是中文字(e.g.註解部分)就會全變成亂碼。

更改 Eclipse 預設編碼的方式如下:

Windows -> Preferences -> General -> Workspace -> Text file encoding -> UTF-8

2. 程式語言本身支援程度

有些程式語言在設計的時候並沒有那麼詳盡的考慮到 Unicode 的問題,故程式設計師在開發上必須多花點心力在編碼上面,Python 2.X 系列就是個經典的例子。

更多資料可參考: Python 2.x Unicode HOWTO

我在另一篇文章「在 Python 2.x 處理 Unicode 字串」 進一步討論 Python 編碼,有興趣的可以看看。

3. 檔案的讀寫

在寫程式時常常會有檔案讀寫的需求,而讀取與寫入的動作都跟編碼息息相關,用不正確的編碼讀取或寫入檔案會產生非預期的結果。

大部分的程式語言都會提供一個專門處理檔案(FILE I/O) 的物件(Object)供工程師操作,這些物件提供的方法(Method)大多有一個參數(Argument)讓工程師指定編碼,例如

// JAVA
BufferedReader in = new BufferedReader(
       new InputStreamReader(
                  new FileInputStream(fileDir), "UTF8"));
# python 2.x
import codecs
with codecs.open(file name,'r',encoding='utf8') as f:
    text = f.read()
// javascript
var fileReader = new FileReader();
reader.readAsText(file, ‘big5’);

在每次讀取與寫入前,最好能夠先知道檔案的編碼格式,並明確的在程式碼中指定。

我在另一篇文章「用 javascript 讀入、輸出 ANSI 與 UTF-8 編碼的檔案」 簡單示範了如何在 javascript 輸入與輸出 ANSI/UTF-8 編碼的檔案。

4. 螢幕輸出

寫程式時有時會需要將資訊顯示(print)在螢幕上,若編碼不正確就會顯示出亂碼。

通常螢幕輸出亂碼都是因為使用了錯誤的編碼讀取檔案,故解決方式與上述類似。

比起螢幕輸出亂碼,程式設計的新手更常遇到螢幕輸出排版走樣,輸出的結果雖正確但卻不好看。

此時應該善用內建函數的排版功能,如 C/C++ 的 printf() 或 Python 的 format() 等來調整版面

另一個要注意的點是 CJK 的全形與半形符號。半形空格 U+0020 與全形空格 U+3000 雖然都是空格,但所佔的空間不一樣,在排版時就會產生不同的效果,甚至會影響某些 auto grader 的輸出結果。

有興趣的可以參考這篇討論:Aligning Japanese characters in python

5. 給程式設計新手的結論

A. 程式會自己判斷編碼

在寫程式的時候,如果沒有特別指定要用哪種編碼開啟檔案,則程式可能會:

  • 用該程式語言預設的編碼方式開啟檔案
  • 用目前作業系統預設的編碼開啟檔案

現在大部分的程式語言都會自動去 “猜” 編碼,即使程式語言本身不支援這個功能,通常也可以找到開源的第三方 library 提供類似的功能。

但如果設計師自己可以預先確定檔案編碼的話,最好還是手動指定以避免錯誤。

B. 常搞混的編碼名稱

如同 淺談電腦編碼與 Unicode (一) 基礎概念篇 提到的,有些編碼在歷史共業之下被取了錯誤的名稱,但久而久之大家也漸漸習慣那樣的叫法。

像 ANSI 與 ASCII 就是這樣的例子:Differences between ansi and ascii?

The word “ANSI” is still often incorrectly used when talking about code page 1252 and also about other similar code page introduced by Microsoft for central European languages, Turkish, Greek, Hebrew, Arabic, Baltic languages, Vietnamese, and Thai. It is better not use for this.

Essentially “ANSI”, when used in opposition to “ASCII”, means the Windows extended ASCII character set, usually code page 1252 but perhaps one of the other 256-character Windows character sets, or even all of them together.

最簡單的記法是分成兩大類,一類是 ASCII 系,一類是 Unicode 系

  • ASCII
    • ASCII
    • ANSI
    • EASCII
    • ISO 8859-1
    • cp1252
    • Big5 (台灣繁體中文)
    • (GBK, Shift_JIS…)
  • Unicode
    • UTF-8
    • UTF-16

這記法有點過度簡化,但較方便新手記憶。

C. Make your apps Unicode-aware

回顧編碼的由來,亂碼其實也算是一種歷史共業,我們無法限制使用者輸入的檔案一定要使用某種編碼,但是我們可以漸漸改變習慣,盡可能使用 Unicode,減低大家的麻煩。

根據 w3techs 於 2015 年 3 月的 調查資料 顯示,全世界的網頁有 83.9% 使用 UTF-8 編碼,這個結果代表著未來 Unicode 趨勢將銳不可擋,工程師統一使用某種編碼,對使用者而言也是件好事。

Make your apps Unicode-aware, for the good of mankind.


(四) 延伸閱讀

VoiceTube 看影片學英文