本文目錄一覽:
- 1、php 編碼轉換原理
- 2、Java中UTF-8和BIG5怎麼互相轉換呢?
- 3、我PHP頁面用的是utf-8編碼,資料庫的編碼方式是big5。請問頁面中應該怎樣處理啊?
- 4、什麼是GBK、UTF-8、BIG5編碼?
php 編碼轉換原理
這個原理十有八九都不知道,轉載一篇別人的原理,希望能對你有所幫助。
更廣義的字符集轉換:iconv
在先前的章節中,我們已見到了兩組 mbs 與 wcs 互轉的函式,第一組對於有「狀態改變」的編碼系統 mbs 無法做到字元串的狀態控制,故不適合做該 mbs 的轉換工作;而第二組則可以直接做字元串的狀態控制,故使用範圍就更廣了。然而,這兩組字元串轉換函式在某些使用場合下都有很大的限制,廣義而言,它們都屬於「字符集轉換函式」,然而它們都直接與 I18N、locale 機制綁在一起,也就是說在使用它們之前,程序都必須設好正確的 locale 才行。故在以下的情況使用它們就會很不方便,甚至根本行不通:
• 如果程序中需要做 A 字集編碼與 B 字集編碼的轉換時,不幸的是這兩種字集 編碼都不是目前程序所處的 locale 所採用的。若使用前述的 wcs 與 mbs 轉換函式,唯一的辦法只有先呼叫 setlocale(),將程序的 LC_CTYPE locale 先切換到使用字集編碼 A 的 locale,把字元串 A 轉成 wcs 字元串後,再呼叫一次 setlocale(),將程序的 locale 切換到使用字集編碼 B 的 locale,最後才把 wcs 字元串轉成字元串 B。萬一 都找不到任何 locale 採用字集 A 或字集 B 時,則這一招就沒有用了。
• 如果程序中需要同時做多種字集編碼的轉換時,則這些 wcs 與 mbs 的轉換 函式就無法辦到了。原因是一旦 setlocale() 設定好程序的 locale 之後,其影響是遍及整個程序每個部分,我們不可能做到在轉換字集編碼 A 時設好 locale A,又 “同時” 設好 locale B 來轉字集編碼 B。
因此,我們需要更廣義的字集轉換系統,一個可以與 locale 完全無關的轉換系統,才能方便地達到上述的要求。故在 XPG2 標準中另外定義了一組全新的函式介面: iconv。事實上,在 glibc 中,表面上看來那些 wcs 與 mbs 轉換函式與 iconv 不太一樣,但它們底層的 wcs 與 mbs 轉換工作卻是由 iconv 來達成的。故 iconv 可以說是字集轉換系統中最基底的函式介面。
iconv 字集轉換系統只有三個函式,在很多系統中是宣告在 iconv.h 裡頭,使用上與一般在做檔案讀寫的概念一樣,先 “開啟”、之後 “操作”、完畢後要 “關閉”,這些函式包括:
• iconv_t iconv_open(const char *TOENC, const char *FROMENC)
• size_t iconv (iconv_t CD, const char **INBUF,
size_t *INBYTESLEFT, char **OUTBUF, size_t *OUTBYTESLEFT)
• int iconv_close(iconv_t CD)
首先 iconv_open() 函式就是做 “開啟” 動作,也就是當我們要將編碼系統 A 轉換到編碼系統 B 時,必須先呼叫此函式,將 FROMENC 設成編碼系統 A 的名字,同時將 TOENC 設成編碼系統 B 的名字,這時此函式就會做類似檔案開啟的動作,傳回一個代表此轉換管道的數據結構 iconv_t 供後續使用。事實上,在系統的實作中真的是將 iconv_open() 當作 “開啟檔案” 來處理,故它會受到目前系統或同一行程中可開啟檔案數所限,如果系統或程序的其它部分已開啟了太多的檔案以至於逼近系統上限,則有可能這邊的 iconv_open() 會失敗。
如果 iconv_open() 開啟失敗時,它會傳回 (iconv_t)-1 的值,同時設定 errno 全域變數,用以指出開啟失敗的原因。而開啟失敗的原因,不外乎就是已開啟的檔案數已超過上限、或系統內存不足、或系統本身無法做到編碼系統 A 與 B 之間的轉換。有興趣的讀者可以直接去閱讀 info libc, * Character Set Handling:: 一節了解各 errno 的值與其所代表的意義。
第二個 iconv() 函式就是用來做實際編碼系統轉換工作的,它必須先呼叫過 iconv_open() 並取得 iconv_t 結構後才能工作。只要 iconv_open() 可以開啟成功,則理論上它就可以進行轉換工作,而且不論其來源編碼與目標編碼是 mbs 字元串或 wcs 字元串、或二者的混合互轉,都沒有關係。但要注意的是,如果其中有 wcs 字元串時,就算用來存放 wcs 字元串是使用 wchar_t * 形別,在傳入此函式時仍然統一用 char * 來處理。
若要做字元串 A 轉換成字元串 B 時,字元串 A 是經由 *INBUF 傳入,而 *INBYTESLEFT 則傳入數組 A 的長度,一律以位元組數來計算。而轉換的結果則由 *OUTBUF 傳回,同樣的 *OUTBYTESLEFT 則為 *OUTBUF 的長度。如果轉換成功了,則 *INBUF 最後會設在存放字元串 A 的數組末尾,而 *INBYTESLEFT 會設為目前此數組還剩多少位元組可以用。而 *OUTBUF 與 *OUTBYTESLEFT 也是一樣。因此我們可以在相同的 A、B 數組中重複呼叫此函式。舉個例子來說,如果轉換過程中在 A 數組裡頭遇到了不合法的字元而無法進行轉換時,則 *INBUF 與 *OUTBUF 就會停在無法轉換的位置上,並將已轉換成功的結果傳回,則我們可以自行決定看是要跳過那個不合法的字元,繼續轉換剩餘的部分,或做其它的特殊處理 …等等。
在很多情況下有可能造成 iconv() 的轉換失敗,例如前面提到的在 A 數組中遇到不合法的字元,或者 A 中某個字元在 B 中找不到可對應的字。其中第二個情況是最有可能發生的,而目前 glibc-2.1.x 系統中遇到這種情況時就當做是轉換失敗,而不做進一步的處理。當然這並不是最好的處理方式,故未來可能會有所改變。而根據 info libc 的說明,如果轉換失敗了, iconv() 最後的傳回值是 (size_t)-1,並設定 errno 的值說明失敗的原因;如果轉換成功,則傳回值會是已成功轉換的字元個數。但我們實際的測試結果,在 glibc-2.1.3 的系統下,如果轉換成功後其傳回值卻永遠是 0,與 info libc 上的描述不一樣,這點有點奇怪,不曉得是不是目前 glibc 的 bug?
若遇到含「狀態改變」的編碼系統時,iconv() 也能正確工作,它能在轉換的過程中隨時記錄、更新字元串目前的狀態 (應該就是記錄在 iconv_t CD 結構中),故就算是采「分期付款」方式將同一字元串切成數分一段段來轉換,也不會出問題。但要注意的是,在第一次使用 iconv() 之前,必須先初始化一下 A、B 兩字元串的狀態,就好像我們在先前所提的 mbsrtowcs() 等函式一樣,在使用前也必須先做好狀能初始化,如此才能讓後續可以正常工作。而對於 iconv() 而言,初始化的方式就是呼叫它時,*INBUF 與 *OUTBUF 都設為 NULL 即可。
最後一個 iconv_close() 函式,就是當整個轉換結束後,用來做「關閉檔案」用的。
底下我們就寫了一個范常式序,用來說明 iconv 函式界面的使用方式:
#include stdio.h
#include string.h
#include iconv.h
int main(int argc, char **argv)
{
FILE *fin, *fout;
char *encFrom, *encTo;
char bufin[1024], bufout[1024], *sin, *sout;
int mode, lenin, lenout, ret, nline;
iconv_t c_pt;
if (argc != 5) {
printf(“Usage: a.out encFrom encTo fin fout\n”);
return 0;
}
encFrom = argv[1];
encTo = argv[2];
if ((fin = fopen(argv[3], “rt”)) == NULL) {
printf(“Cannot open file: %s\n”, argv[3]);
return -1;
}
if ((fout = fopen(argv[4], “wt”)) == NULL) {
printf(“Cannot open file: %s\n”, argv[4]);
return -1;
}
if ((c_pt = iconv_open(encTo, encFrom)) == (iconv_t)-1) {
printf(“iconv_open false: %s == %s\n”, encFrom, encTo);
return -1;
}
iconv(c_pt, NULL, NULL, NULL, NULL);
nline = 0;
while (fgets(bufin, 1024, fin) != NULL) {
nline ++;
lenin = strlen(bufin) + 1;
lenout = 1024;
sin = bufin;
sout = bufout;
ret = iconv(c_pt, sin, lenin, sout, lenout);
printf(“%s – %s: %d: ret=%d, len_in=%d, len_out=%d\n”,
encFrom, encTo, nline, ret, lenin, lenout);
if (ret == -1) {
printf(“stop at: %s\n”, sin);
break;
}
fprintf(fout, “%s”, bufout);
}
iconv_close(c_pt);
fclose(fin);
fclose(fout);
return 0;
}
這個程序可以從命令列輸入來源檔案與目的檔案的編碼系統名稱,將來源檔案的內容轉換成目的檔案。各位讀者可以注意到,在程序中我們完全沒有做 locale 設定等相關動作,原因正是不需要,iconv 函式介面是與 locale 完全無關的。同時,我們的程序只適合拿來做兩個 mbs 編碼系統間的轉換,不適合拿來做其中一個有 wcs 的轉換,原因是程序中我們沒有特別為 wcs 的情況使用 wchar_t * 數組,同時記得我們以前也提過,wcs 字元串是不能拿來做檔案輸出的,而我們的程序目前正是直接做檔案編碼轉換的工作。
這樣的轉碼程序倒底「耐不耐用」呢?有興趣的讀者可以跟著我們一起來測試。由於先前我們已介紹過了如何在您的 GNU/Linux 系統中安裝 zh_TW.Big5 與 zh_CN.GB2312 locale 環境,如果您都已安裝無誤的話,理論上您的系統的 Big5 與 GB2312 的 gconv 系統都有不成問題了 (在 glibc 中,gconv 系統可以說是 iconv 的心臟,我們會在下一小節詳細說明),故我們就直接以 Big5 與 GB2312 兩個編碼系統互轉為例子,測試一下我們的程序。首先,請先準備一個叫 f-big5 的檔案,內容用 Big5 編碼打入這樣一行的內容:
我是研究生
然後,將我們的范常式序編譯好,假設程序執行文件名就是 a.out,接著執行:
a.out BIG5 GB2312 f-big5 output
在 glibc-2.1.x 的系統下,應該可以正確轉換,這時您應該會見到如下的程序輸出:
BIG5 – GB2312: 1: ret=0, len_in=0, len_out=1012
其中 ret=0 就表示轉換成功,然後,您可以再開一個可以看 GB2312 編碼的 terminal 來看 output 檔的內容,您就會見到原來 f-big5 檔的內容已轉換成 GB2312 碼了。
看起來似乎很完美,我們再進一步測試。現在請在 f-big5 檔中再加一些內容,像這樣:
我是研究生
目前在正在做研究測試
按照上頭的指令再執行一次,我們得到如下的結果:
BIG5 – GB2312: 1: ret=0, len_in=0, len_out=1012
BIG5 – GB2312: 2: ret=-1, len_in=6, len_out=1008
stop at: 測試
第一行沒有問題,但第二行失敗了,程序指出第二行最後兩個字 “測試” 無法做轉換,為什麼會這樣呢?
要探討其原因,就要更深入 iconv 的編碼系統轉換原理了。我們在之前已多次提過,在做 mbs 與 wcs 之間轉換時,其實就是將 mbs 轉換成系統的基底字集,而 iconv 的轉換原理也是這般。事實上在 glibc 系統中,它在做編碼系統 A 與 B 之間的轉換時,有兩種途徑可選擇:一是如果 iconv 內部已內建了 A 與 B 的對應表格時,就採用此對應表格來轉換,而這也是最可信賴的轉換方式。萬一它找不到適當的對應表格時,它則會先將 A 轉換成基底字集,再由基底字集轉換成 B。也就是系統的基底字集在這裡扮演了中間媒介的角色。
要注意的是,在其它的 UNIX 系統中,它們可能只提供第一種途徑供轉換,而 glibc 提供第二種途徑的理由是,這樣可以確保任意兩個字集編碼都有機會進行轉換,只要這兩個字集編碼都同時被系統所支持。而且,由於 glibc 所選擇的「基底字集」理論上已包含了目前世界上所有正在使用的計算機字集,故使用基底字集做為中間媒介是合理而且可行的。
但各種字集編碼的問題是很複雜的玩意兒,有時候這一套也有出槌的時候。就像前面見到的 Big5 的 “測試” 無法轉成 GB2312 一樣,我們再做以下的實驗,就會知道原因了。因為 glibc 是以 UCS4 做為其系統的基底字集,而 UCS4 的 mbs 形式為 UTF8 編碼系統,故我們現在準備兩個檔案,一個是 f-big5,內含 Big5 碼的 “測試” 兩字,另一個是 f-gb,內含 GB2312 碼的 “測試” 兩字。現在我們用上頭那個范常式序將這兩個檔案都轉成 UTF8 編碼:
a.out BIG5 UTF8 f-big5 output1
a.out GB2312 UTF8 f-gb output2
這兩個都可以轉換成功。然後,我們用 diff 程序比較一下 output1 與 output2 的內容,發現它們的內容不一樣!
這就是無法轉換成功的原因!因為在中文裡頭意義一樣的兩個字,在 Big5 與 GB2312 裡頭分別被對應到不同的 UCS4 編碼裡頭去了。為什麼會如此?這牽涉到更深刻的 UCS4 編碼規則與其它編碼系統的對應問題,在這裡我們就先略過了,以後若有機會的話再回來討論。現在的問題是,在 glibc 中,如果 A 與 B 的某些字分別被對應到基底字集中不同的編碼中,就有可能會無法轉換成功。而這往往是很有可能發生的,特別是 glibc 直接採用 UCS4 做為其基底字集時。
而且,在回顧一下我們前面談的,在不同的 UNIX 系統下,其 iconv 底層的運作模式可能都不太一樣,這就造成了更嚴重的問題:就算系統同時支持 A 與 B 兩種編碼,我們仍不能保證可以將 A 轉成 B。或者,該系統中可以將 A 轉成 C,也可以將 C 轉成 B,但也不能保證可以將 A 直接 (或間接) 轉成 B。
這的確是另人氣餒的問題,現在也許讀者就會問了,那 iconv 有什麼用?我們的認為是,它在小字集與大字集編碼間的轉換是有用的。例如 Big5 與 GB2312 分別是 UTF8 的子字集,則我們可以很放心地使用 iconv 將 Big5 或 GB2312 轉成 UTF8,或反方向地轉換回來 (當然,前提是原來的 UTF8 字元串中沒有包含 Big5 或 GB2312 所沒有的字)。又或者 Big5 是 GBK 的一個子字集,我們一樣也可以利用它來做轉換。
但如果要直接用 iconv 來做 Big5 與 GB2312 的轉換的話,唯一個可靠方法就是在 iconv 系統中加入一個 Big5 與 GB2312 直接互轉的表格,並且希望此表格可以成系統標準的一部分 (至少要是 glibc 標準的一部分),如此我們的程序這樣寫才會有用 (至少在 glibc 的系統下)。因此,現在我們就進入下一小節,來看看 glibc 的 iconv 的核心部分,做為本章的一個結束。
Java中UTF-8和BIG5怎麼互相轉換呢?
Charset u8 = Charset.forName(“UTF-8”);
Charset b5 = Charset.forName(“BIG5”);
String s = u8.decode(u8ByteBuffer);
ByteBuffer b5ByteBuffer = b5.encode(s);
我PHP頁面用的是utf-8編碼,資料庫的編碼方式是big5。請問頁面中應該怎樣處理啊?
是亂碼問題嗎???
在php頂部加入一句: header(‘Content-type: text/html;charset=utf-8);
PHP寫入MySQL資料庫
建立conn時,query一下set names
什麼是GBK、UTF-8、BIG5編碼?
DZ6.1 後台導入不同編碼的論壇插件和轉換程序 (utf8/big5/gbk) #############################################
插件名稱:DZ6.1 後台導入不同編碼的論壇插件 (utf8/big5/gbk)
適用版本:Discuz!最新DZ 6.1(各版本,包括簡繁GBK和UTF-8)
6.1版作者:Albey
4.1以前作者:Eiffel
最後更新:2008年5月31日18時10分
數據結構:未修改資料庫
演示地址(UTF-8):
說明:1。提供的插件導入.rar是簡體UTF-8的,其他的版本只要自己手動加代碼就行了,sctc.inc.php不同版本有提供,因為utf-8的插件其實不多,所以才想到提供),這個是插件的導入文件的轉換
2。GB to UTF-8的轉換程序文件,是1.3版本,版權歸作者所有,這個是插件的頁面文件的轉換。
版本狀況:
2008.5 第1版,已測試
#############################################
安裝方法(UTF-8)
1。查找templates\default下,admincp.lang.php ‘plugins_import_ignore_version’ = ‘允許導入不同版本 Discuz! 的插件(易產生錯誤!!)’,複製代碼在下面加: ‘plugins_import_from_tc’ = ‘導入繁體 UTF8 編碼’,
‘plugins_import_from_sc’ = ‘導入簡體 UTF8 編碼’,
‘plugins_import_from_big5’ = ‘導入繁體 BIG5 編碼’,
‘plugins_import_from_gbk’ = ‘導入簡體 GBK 編碼’,複製代碼———————————————–
2。把sctc.inc.php,放到include下面,下載(include.rar ),然後找對應你自己BBS的版本
———————————————–
3。查找admin下,plugins.inc.php
查找: if(!defined(‘IN_DISCUZ’) || !defined(‘IN_ADMINCP’)) {
exit(‘Access Denied’);
}複製代碼在下面加: require_once DISCUZ_ROOT.’include/sctc.inc.php’;複製代碼———————————————–
查找: showtablerow(”, ”, ‘input type=”file” name=”importfile” size=”40″ class=”uploadbtn marginbot” /’);
showtablerow(”, ”, ‘input type=”checkbox” name=”ignoreversion” value=”1″ class=”checkbox” / ‘.lang(‘plugins_import_ignore_version’));複製代碼下面加: showtablerow(”, ”, ‘input class=”radio” type=”radio” name=”code” value=”utf_tc” ‘.lang(‘plugins_import_from_tc’));
showtablerow(”, ”, ‘input class=”radio” type=”radio” name=”code” value=”utf_sc” ‘.lang(‘plugins_import_from_sc’));
showtablerow(”, ”, ‘input class=”radio” type=”radio” name=”code” value=”big5″ ‘.lang(‘plugins_import_from_big5’));
showtablerow(”, ”, ‘input class=”radio” type=”radio” name=”code” value=”gbk” checked ‘.lang(‘plugins_import_from_gbk’));複製代碼———————————————–
查找: $db-query(“INSERT INTO {$tablepre}plugins ($sql1) VALUES ($sql2)”);複製代碼上面加: //utf8-gbk
switch($code) {
case “utf_tc”:
$sql2=tc2sc($sql2);
break;
case “big5”:
require_once DISCUZ_ROOT.’include/chinese.class.php’;
$chs = new Chinese(‘BIG5’, ‘UTF8’);
$sql2=$chs-Convert($sql2);
$sql2=tc2sc($sql2);
break;
case “gbk”:
require_once DISCUZ_ROOT.’include/chinese.class.php’;
$chs = new Chinese(‘GBK’, ‘UTF8’);
$sql2=$chs-Convert($sql2);
break;
case “utf_sc”:
break;
}
//utf8-gbk複製代碼———————————————–
查找: $db-query(“INSERT INTO {$tablepre}plugin$pluginconfig ($sql1) VALUES ($sql2)”);複製代碼上面加: //UTF8-GBK
switch($code) {
case “utf_tc”:
$sql2=tc2sc($sql2);
break;
case “big5”:
require_once DISCUZ_ROOT.’include/chinese.class.php’;
$chs = new Chinese(‘BIG5’, ‘UTF8’);
$sql2=$chs-Convert($sql2);
$sql2=tc2sc($sql2);
break;
case “gbk”:
require_once DISCUZ_ROOT.’include/chinese.class.php’;
$chs = new Chinese(‘GBK’, ‘UTF8’);
$sql2=$chs-Convert($sql2);
break;
case “utf_sc”:
break;
}
//UTF8-GBK複製代碼———————————————–
頁面文件最後?前,加 //utf-8-gbk
function tc2sc($str){
$outstr=””;
for($i=0; $i strlen($str); $i++) {
$ch=ord(substr($str,$i,1));
if($ch 127) {
$char=substr($str,$i,3);
$loc=strpos(strTC,$char);
if ($loc !== false) $char=substr(strSC,$loc,3);
$outstr.=$char;
$i+=2;
} else {
$outstr.=substr($str,$i,1);
}
}
return $outstr;
}
//utf-8-gbk複製代碼———————————————–
4。查找admin下,styles.inc.php
查找: if(!defined(‘IN_DISCUZ’) || !defined(‘IN_ADMINCP’)) {
exit(‘Access Denied’);
}複製代碼在下面加: require_once DISCUZ_ROOT.’include/sctc.inc.php’;複製代碼———————————————–
查找: showtablerow(”, ”, ‘input type=”file” name=”importfile” size=”40″ class=”uploadbtn marginbot” /’);
showtablerow(”, ”, ‘input class=”checkbox” type=”checkbox” name=”ignoreversion” id=”ignoreversion” value=”1″ /label for=”ignoreversion” ‘.lang(‘styles_import_ignore_version’).’/label’);複製代碼在下面加: showtablerow(”, ”, ‘input class=”radio” type=”radio” name=”code” value=”utf_tc” ‘.lang(‘plugins_import_from_tc’));
showtablerow(”, ”, ‘input class=”radio” type=”radio” name=”code” value=”utf_sc” ‘.lang(‘plugins_import_from_sc’));
showtablerow(”, ”, ‘input class=”radio” type=”radio” name=”code” value=”big5″ ‘.lang(‘plugins_import_from_big5’));
showtablerow(”, ”, ‘input class=”radio” type=”radio” name=”code” value=”gbk” checked ‘.lang(‘plugins_import_from_gbk’));複製代碼———————————————–
查找: $db-query(“INSERT INTO {$tablepre}templates (name, directory, copyright)複製代碼上面加: //utf8-gbk
switch($code) {
case “utf_tc”:
$stylearray[tplname]=tc2sc($stylearray[tplname]);
$stylearray[copyright]=tc2sc($stylearray[copyright]);
$stylearray[name]=tc2sc($stylearray[name]);
break;
case “big5”:
require_once DISCUZ_ROOT.’include/chinese.class.php’;
$chs = new Chinese(‘BIG5’, ‘UTF8’);
$stylearray[tplname]=$chs-Convert($stylearray[tplname]);
$stylearray[copyright]=$chs-Convert($stylearray[copyright]);
$stylearray[name]=$chs-Convert($stylearray[name]);
$stylearray[tplname]=tc2sc($stylearray[tplname]);
$stylearray[copyright]=tc2sc($stylearray[copyright]);
$stylearray[name]=tc2sc($stylearray[name]);
break;
case “gbk”:
require_once DISCUZ_ROOT.’include/chinese.class.php’;
$chs = new Chinese(‘GBK’, ‘UTF8’);
$stylearray[tplname]=$chs-Convert($stylearray[tplname]);
$stylearray[copyright]=$chs-Convert($stylearray[copyright]);
$stylearray[name]=$chs-Convert($stylearray[name]);
break;
case “utf_sc”:
break;
}
//utf8-gbk複製代碼———————————————–
此頁面最後?前,加 //utf8-gbk
function tc2sc($str){
$outstr=””;
for($i=0; $i strlen($str); $i++) {
$ch=ord(substr($str,$i,1));
if($ch 127) {
$char=substr($str,$i,3);
$loc=strpos(strTC,$char);
if ($loc !== false) $char=substr(strSC,$loc,3);
$outstr.=$char;
$i+=2;
} else {
$outstr.=substr($str,$i,1);
}
}
return $outstr;
}
//utf8-gbk複製代碼utf-8版本完成
[ 本帖最後由 Albey 於 2008-6-18 02:23 編輯 ]截圖.jpg (30.64 KB) 下載次數:832008-5-31 18:57插件導入.rar (90.42 KB) 下載次數:12502008-5-31 18:57簡體UTF-8GB2UTF8.rar (23.15 KB) 下載次數:2212008-5-31 18:57轉換文件的軟體include.rar (19.63 KB) 下載次數:2102008-5-31 18:57sctc.inc.php
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/251870.html