作者LPH66 (J∪$т М㎝iκä)
看板C_and_CPP
標題Re: [問題] UTF-8 Read / Write
時間Sat Mar 17 10:57:02 2018
首先一個用語解釋:
在 Windows 系統之下, 所有的 "Unicode" 都特指 UTF-16LE 編碼
這其實跟 Windows 內部的實作有關 (其實就是 Windows 的 wchar_t 字串啦)
跟我們平常其他討論裡的 Unicode 是指稱那個抽象編碼的用語不一樣
以下為分別兩者, Windows 的 "Unicode" 我會加 "" 表示
※ 引述《EdisonX (卡卡獸)》之銘言:
: 開發平台(Platform): (Ex: Win10, Linux, ...)
: Visual Studio 2017 , Console C/C++
: 額外使用到的函數庫(Library Used): (Ex: OpenGL, ...)
: 問題(Question):
: [Q1]
: 目前我收到的檔案,用記事本開、notepad++開,
: 一般 asci 是用 1 byte , 繁中、簡中(非常少數)是用 2 byte 存,
: 再用記事本去開,預設是用 utf-8 存 (非 asci),且無 bom 檔頭 ,
: 所以我是否可以假設這份檔案是以 utf-8 方式存檔?
UTF-8 的中文是 3-byte 喔
2-byte 的中文 Unicode 是 UTF-16
至於 Windows 預設的記事本, 只要不是存成 "ANSI" 選項就一定會加 BOM
當中的 "Unicode" 同樣是指 UTF-16
(記事本的 "Unicode" 是 UTF-16LE, "Unicode Big Endian" 是 UTF-16BE)
Notepad++ 我沒用過不太清楚 (我自己是用 Notepad2)
不過這種第三方的軟體才比較有可能設定成沒有 BOM 的存檔
: [Q2]
: 目前我嚐試過用 fopen / _wfopen 方式去開、讀檔 ,
: 也試過指定 ccs=UTF-8 方式去開 ,
: 再做簡單的 printf / wprintf , 不論怎麼改跑出來的一直都是亂碼 ,
: 最後嚐試用 char , 直接輸出到檔案去 , 神奇的事發生了
: console 輸出是亂碼 , 檔案全都解得出來
: 去細節後 code 摘要如下
: FILE * fin = fopen(sfilename.c_str(), "rb,ccs=UTF-8");
: char * pBuf = (char *)malloc(filesize + 32);
: fread((void*)pBuf, 1, filesize, fin);
: pBuf[filesize] = 0;
: FILE * fout = fopen("output.txt", "w");
: pFind = pBuf;
: while (pFind = strstr(pFind, pszDesc)) {
: pFindNext = strstr(pFind + iDescLen, pszScore);
: if (pFindNext == NULL) break;
: *(pFindNext - 1) = 0;
: fprintf(stdout, "%s\n", pFind); \\ 亂碼
: fprintf(fout, "%s\n", pFind); \\ 正常
: pFind = pFindNext + 1;
: }
: fclose(fout);
: free(pBuf);
: 請問是不是我誤會了什麼東西?
: 若要解析這種檔案, 請問我的方法正確嗎?
: 另若有版友建議直接加入 ATL CString 處理編碼的話也請告知
: (乍看只換 CString 問題應該不會改善)
如推文所說, console 是系統編碼, 在繁中系統就是 950
所以你把 Unicode 字串原封不動輸出是一定會變成亂碼的 (不論什麼編碼)
至於 ccs 選項, 它是你指定說這檔案是什麼編碼
系統來幫你轉成 "Unicode" 字串這樣
進來之後就已經是 "Unicode" ie. UTF-16LE 編碼的字串了
也就是你的 pBuf 已經是一個 UTF-16LE 編碼字串
你可以檢視一下你的 output.txt 的編碼, 會發現它是 UTF-16LE 無 BOM
: [Q3]
: 最後的問題是 , 這些截出來的字串會丟到簡易型 db,
: 之前碰過 sqlite , 但只用過 asci 編碼 , 查了下官網 ,
: sqlite 應是支援 utf-8 , 請問這方面是否有人有過經驗能給些意見?
: 或是直接丟掉 sqlite , 有其他較簡易但字元編碼較佳的 sql lib ?
: 最後謝謝各位細心回覆,感激不盡。
所有資料庫對字串欄位都必須指定編碼
那麼這裡問題來了: 你的字串如上面所說是一個 UTF-16LE 的字串
你不能就這樣貿貿然把它一股腦兒塞到指定為 UTF-8 的資料庫欄位當中
如果你要沿著這條路線下去的話, 你的資料庫欄位必須要指定為 UTF-16LE 才對
====
那如果你想保持輸入檔的 UTF-8 格式的話
還有一個方式是你叫 Windows 不要幫你轉, 也就是拿掉 ccs 選項
這樣你讀進來的字串就會跟輸入檔的編碼一模一樣了
--
'Oh, Harry, don't you
see?' Hermione breathed. 'If she could have done
one thing to make
absolutely sure that every single person in this school
will read your interview, it was
banning it!'
---'Harry Potter and the order of the phoenix', P513
--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 123.195.9.46
※ 文章網址: https://www.ptt.cc/bbs/C_and_CPP/M.1521255424.A.D7E.html
※ 編輯: LPH66 (123.195.9.46), 03/17/2018 11:01:01
推 ilikekotomi: 感謝分享 沒想到windows的是這樣 03/17 11:12
推 EdisonX: 太感謝了!我先實作 , 有問題再請教,謝謝! 03/17 12:28
→ EdisonX: 再進一步請教 , 所以在監看式裡中文顯示亂碼也正常 ? 03/17 12:38
→ LPH66: 好久沒用 VS 的監看式, 剛剛測了一下 03/17 12:48
→ LPH66: char 陣列會用系統編碼顯示, 所以會有一樣的問題 03/17 12:48
推 EdisonX: 原來如此 , 那我放心全用 char* 去處理了, 謝謝. 03/17 12:56
推 Domos: utf-8是1~4byte,中文不一定都是3byte。utf-16則是2或4byt 03/17 13:01
→ Domos: e。 03/17 13:01
推 EdisonX: 那拿到一份文件有比較客觀的方法知道是用什麼編碼嗎 ? 03/17 13:02
→ EdisonX: 剛看了一下, 我的中文字確實有3bytes,應該是 utf8 了 03/17 13:38
→ LPH66: UTF-8 的中文確實不都是 3 byte, 但 4 byte 的中文是罕用字 03/17 13:41
→ LPH66: 所以我平常是都會直接只說 3 byte 這樣... 03/17 13:41
→ LPH66: 100% 判斷編碼的方法應該是沒有, 不過可以猜 03/17 13:42
→ LPH66: UTF-8 的位元組組合有個特定模式不容易在其他編碼出現 03/17 13:43
→ LPH66: 這也就是 Joel 在講的「根本就沒有純文字這種東西。」 03/17 13:45
推 cutekid: 推 L 大附的補充連結。 03/17 13:48