2012年4月14日 星期六

C++ 程式編譯之字串

當使用 C 的 printf() 或 C++ 的 cout 秀出文字運作原來相當簡單,就是直接把執行程式內存放的字串字碼丟給系統處理就是了,只要字碼正確就會看到正確的字形在 Console 視窗秀出來。

但這個正確的字碼是怎麼產生的?看看以下這個測試程式,請用記事本編寫比較方便,隨後會做不同的編碼存檔。

#include <iostream>
using namespace std;

int main()
{
  const char *ansi_str1 = "中文";

  cout << "ansi_str1 = " << ansi_str1 << endl;
  return 0;
}

將之存成 ANSI 編碼格式,檔名為 test.cpp,我用 gcc 4.6.2 進行
編譯:


但若改用 utf-8 編碼存檔,結果如下:


變成亂碼了吧。

所有的原始程式檔都必須存放到文字檔中,但文字檔的編碼格式有好多種,所以 "中文" 這個字串在不同編碼的文字檔中字碼也不一樣,而編譯程式內定的做法,只是把原始檔中的字串複製到程式執行檔中的資料儲存區而已,所以要做轉換才能讓執行檔得到正確的字碼,原始檔所用的字碼稱為 source character set,而要放到執行檔中的字碼稱為 execution character set,在 gcc 中分別可用 -finput-charset=CHARSET 和 -fexec-charset=CHARSET 兩佪編譯參數來指定,所以可改成如下編譯:


為什麼不用指定 -finput-charset 呢,只要多測試幾次就會發現,若全不指定就採取直接複製的方式,若只指定一邊,另一邊就內定為 utf-8。接下來看看以下這個程式範例:

#include <iostream>
using namespace std; 

int main()
{ 
  const char *ansi_str1 = "中文"; 
  const wchar_t *unicode_str1 = L"123中文"; 

  cout << "ansi_str1 = " << ansi_str1 << endl; 
  wcout << "unicode_str1 = " << unicode_str1 << endl; 

  return 0;
}

先儲存為 ANSI 編碼,編譯結果:


這個編譯 ansi_str1 採用複製的方式,但 unicode_str1 須要被轉換為 UNICODE 才能被存放到執行檔的資料儲存區,可是卻不知來源檔的 L"123中文" 字串是何種編碼,所以改如下編譯:


unicode_str1 沒顯示 "中文" 二字,以上是在 WinXp 做測試,WinXp 雖然有支援 UNICODE,但 gcc 做法是先把 wchar_t 字串轉換成 char 字串,再呼叫 char 字串相關的功能,問題就在由 wchar_t 轉成  char  並未設定 setlocale(),就以內定的 "C" 轉換,以至於中文部份沒能轉換,所以程式碼要改一下

#include <iostream>
#include <locale>
using namespace std; 

int main()
{ 
  const char *ansi_str1 = "中文"; 
  const wchar_t *unicode_str1 = L"123中文"; 

  cout << "ansi_str1 = " << ansi_str1 << endl;

  setlocale(LC_CTYPE, "");  // 指定使用系統現正使用中的地區語言做轉換 
  wcout << "unicode_str1 = " << unicode_str1 << endl; 

  return 0;
}

這樣就沒問題了。

反觀 vc 為什麼不用有這些考量,主要的原因是 vc 不像 gcc 是跨平台,gcc 在 source character set 和 execution character set 好像只能接受 utf-8 和 ansi(BIG5、GB…都是 ansi)兩種編碼,vc 在 execution character set 只有 ansi 一種,而 source character set 可接受多種編碼,而且採用自動辨識。

沒有留言:

張貼留言