1. 程式人生 > >控制檯程式的中文輸出亂碼問題,printf,wprintf與setlocale

控制檯程式的中文輸出亂碼問題,printf,wprintf與setlocale

#include <stdio.h>
#include <wchar.h>
int main(void) {
    char str[] = "中文";
    wchar_t wstr[] = L"中文";
    printf("1:%s\n", str);
    wprintf(L"2:%s\n", wstr);
    return 0;
}

Windows平臺下VS2008輸出:

Windows平臺下MinGW輸出:

當加上setlocale函式設定後,

#include <stdio.h><span style="color: rgb(255, 0, 0); ">
</span>#include <locale.h>
#include <wchar.h>
int main(void) {
    setlocale(LC_CTYPE, "");
    char str[] = "中文";
    wchar_t wstr[] = L"中文";
    printf("1:%s\n", str);
    wprintf(L"2:%s\n", wstr);
    return 0;
}

輸出分別為:

   

 

為解其中各種紛亂的糾結,又讓我一個美好的下午就此悲劇=  =.

=============================================================分割線

這檔子事還得從字元編碼說起.關於字符集和編碼的基礎知識,請看咱昨天寫的 

這裡涉及到一個字元在原始碼(文字)中,編譯好的二進位制檔案中,以及最後控制檯輸出編碼形式的區別.

首先,要明確一點:C(語言/程式)並不理解ANSI,UTF-8以及任何其他編碼.它只知道處理你給它的字元二進位制表示.

在簡體中文Windows下,預設的文字儲存編碼是ANSI(即GBK);Linux下根據系統locale設定,一般應該是(zh_CN.UTF-8).(以下基於簡體中文Windows)

1)對於原始檔中儲存的"中文"這個字串,VS2008看到的就是"0xd6d0"和"0xcec4"的形式(預設ANSI編碼得到).但編譯器才不管是不是GBK神馬的,它就管那串數字.

區別,MinGW看到的是"0xe4b8ad"和"0xe69687"(gcc預設UTF-8).注意,用MinGW編譯的原始檔中有中文寬字元必須儲存為UTF-8編碼.

2)然後,在二進位制檔案中的儲存形式,對傳統的字串(char str[] = "中文";),編譯器什麼都不做,直接把那串數字(如"0xd6d0","0xcec4")搬過去塞進二進位制檔案.

但對於寬字串(wchar_t wstr[] = L"中文";),編譯器會將其做轉換,轉換成Unicode編碼格式(在Windows是UTF-16,而Linux下是UTF-32).如"中文"的16位Unicode是"0x4e2d"和"0x6587",然後把這串轉換後的數字("0x4e2d","0x6587")塞進二進位制檔案中.(這裡VS和MinGW做的沒有區別)

這裡有點需要注意,編譯器必須知道你的原始檔儲存的編碼!如VS預設是ANSI編碼,如果你用UTF-8儲存.c原始檔去用VS開啟看一定是亂碼.同理如果你用mingw編譯ANSI編碼儲存的原始檔,也會出錯!(但可以修改編譯選項解決,見文章末尾) 在本文這裡這個原因其實很好理解,因為編譯器需要知道,如果它要將一個儲存在檔案中的字元轉成寬字元時,是從什麼編碼轉到Unicode.(可見上述VS是GBK->Unicode,而MinGW是UTF-8->Unicode)

來小結下"中""文"的3種編碼:

ANSI(GBK): 0xd6d0  0xcec4

UTF-8: 0xe4b8ad 0xe69687

Unicode: 0x4e2d 0x6587

到這裡,一切都還正常~ 

3)控制檯的輸出是問題關鍵!在簡體中文Windows下的控制檯顯示環境是ANSI編碼(內碼表936, GBK),先明確這點.

對於傳統字串輸出printf("%s\n", str);程式執行時,直接將二進位制檔案中儲存的那串數字丟進輸出流.到這裡,你該發現了吧:str儲存在檔案中是GBK,儲存在二進位制檔案中是GBK,到控制檯的輸出環境也是GBK!三者一致,自然輸出正常.(當然,如果你修改三者中任一的一個編碼,輸出結果都會不一樣)

但對於寬字串呢,wprintf(L"%s\n", wstr);會怎麼做?wprintf會先二進位制檔案的Unicode編碼那串東西轉成本地區域編碼,然後丟進輸出流.哦!這本地區域編碼程式是怎麼得到就成關鍵中的關鍵了.這時咱們來看看setlocale這個函式吧.()

setlocale是用來程式執行時,設定當前的區域資訊. 函式引數格式這裡就不介紹了,請看上面連結或Google.

值得注意是: 在所有C程式啟動前,locale的預設設定setlocale(LC_ALL,"C");會被執行.

那"C"是什麼環境呢?

The "C" locale is the minimal locale. It is a rather neutral locale which has the same settings across all systems and compilers, and therefore the exact results of a program using this locale are predictable. This is the locale used by default on all C programs.

其實這麼看咱也沒弄懂"C"具體是個啥區域環境,暫且鑑定為是指那個只認128字元的編碼環境吧.(反正它不認中文=  =)

所以,輸出時Unicode編碼預設轉成這個C環境編碼,然後丟進輸出流.而控制檯的顯示環境預設是GBK啊,這不就亂了嗎!所以亂碼啦~

解決辦法就是在程式中加上setlocale(LC_CTYPE, "");

LC_CTYPE表示C字串相關的處理.而雙引號中是對應的locale字串,如果什麼都不寫就從當前系統獲得預設的環境編碼.當然你也可以手動寫成setlocale(LC_CTYPE, "chs"); 一樣的.

這時候,程式輸出時將Unicode編碼的字串轉成系統的預設編碼(Windows下是ANSI),而Windows系統預設編碼一般都與控制檯環境編碼一致,OK~正常輸出了.

等等!在加了setlocale函式後的VS2008兩個"中文"都輸出正確了,而MinGW怎麼第一個卻還是亂碼"涓枃"?! 這是當然啦,忘了嗎?MinGW的原始檔儲存的編碼格式是UTF-8啊.並且程式將文字儲存的UTF-8編碼(0xe4b8ad和0xe69687)塞進二進位制檔案中,輸出時也沒做轉換,又直接將那串UTF-8編碼丟進輸出流,在GBK環境的控制檯輸出,實際過程就相當於UTF-8==>GBK,要知道UTF-8與GBK可不相容啊,這樣輸出顯示的結果註定是亂碼啊!

一切都清晰了是不是~

=============================================================又見分割線 

    wchar_t wstr[] = L"中文";    
    setlocale(LC_ALL, "zh_CN.UTF-8");        
    wprintf(L"%s/n",wstr);

這樣依然存在輸出亂碼問題.

這個問題的原因在於wprintf的格式化引數%s.應該呢,在標準C中,格式化引數%s表示普通字串(char*),%ls表示寬字串(wchar_t*)(貌似%S也可以表示寬字串,但在C標準中已被拋棄了,迴避之)

所以Linux下應該用wprintf(L"%ls/n",wstr);才可以正常輸出.

而wprintf(L"%s\n", wstr)的%s是將wstr當作多位元組字串,通過呼叫mbrtowc()函式轉換成Unicode編碼,再交給wprintf輸出. 可wstr本來就是Unicode編碼字串,被當成MBCS編碼再轉換成Unicode,這多的一步處理使字串的內容全亂了.

文章中也說明了Linux下輸出寬字串,未必非要是wprintf,用printf("%ls\n", wstr)也可以. %ls將wstr當作寬字元,通過呼叫wcrtomb()函式轉換成多位元組編碼(這裡是UTF-8),然後交給printf輸出.所以最後依然顯示正確.

而Windows下用%s和%ls都可以正確輸出.其區別我猜想應該是Windows下C執行庫(CRT)與Linux下的The GUN C Library(glibc)實現不同所致.

CRT太特立獨行,Linux下glibc的實現我覺得應該更符合C標準吧.

=============================================================再見分割線 

最後附加:

之前提到過Windows下MinGW編譯的原始檔有中文寬字元時,必須是UTF-8儲存的(沒有中文的話就隨意啦).否則若原始碼中有中文的寬字元變數時編譯會出錯: "converting to execution character set: Illegal byte sequence".(原因之前也說過了,是因為要讓編譯器知道是從什麼編碼轉到Unicode的)

當然如果你執意要儲存ANSI編碼,那麼可以指定gcc的輸入檔案的編碼引數-finput-charset. (如-finput-charset=GBK)

同樣也能指定gcc的輸出編碼引數-fexec-charset. (如-fexec-charset=GBK  這樣之前那個"中文"顯示""涓枃"就也能得到正常輸出啦)

原文連結

相關推薦

控制檯程式中文輸出亂碼問題,printf,wprintfsetlocale

#include <stdio.h> #include <wchar.h> int main(void) { char str[] = "中文"; wchar_t wstr[] = L"中文"; printf("1:%s\n"

控制檯程式中文輸出亂碼問題(export LC_CTYPE=zh_CN.GBK,或者修改/etc/sysconfig/i18n為zh_CN.GBK。使用setlocale(LC_CTYPE, "");會使用預設辦法。編譯器會將原始碼做轉換成Unicode格式,或者指定gcc的輸

今天發現用securecrt登陸時,gcc編譯出錯時會出現亂碼,但直接在主機的視窗介面下用Shell編譯卻沒有亂碼。查看了一下當時的錯誤描述,發現它的引號是中文引號,導致在SecureCRT中顯示出錯:  before numeric constant 在網上查了一下,可以

IntelliJ IDEA亂碼(專案檔案亂碼控制檯輸出亂碼)說明調整

IntelliJ 產品 IDEA等亂碼(專案檔案亂碼、控制檯輸出亂碼)說明與調整 InterlliJ 產品的編碼配置都有以下幾個通用步驟: 屬性配置檔案(idea64.exe.vmoptions 以及 idea.exe.vmoptions ,如果不清楚該改哪一個

C++ 控制檯程式輸出UTF8字元亂碼問題解決方法

1. 使用程式碼糾正 // 控制檯顯示亂碼糾正 system("chcp 65001"); //設定字符集 (使用SetConsoleCP(65001)設定無效,原因未知)

Eclipse控制檯中的中文輸出亂碼問題

1.run -- run configuration 2.apache tomcat -- arguments -- vm arguments 3.common -- encoding -- other UTF-8

【IDEA工具設定】解決控制檯中文輸出亂碼問題

---------------------------------------------------------------------------------親測有效,完美的解決了我的問題。之前遇到兩種錯誤。一、控制檯沒有亂碼,但編輯區的中文無法識別是什麼編碼,也是醉了。

Java控制檯中輸入中文輸出亂碼的解決辦法

在學習Java IO的輸入輸出流的時候遇到了一個問題,就是無論用BufferedReader還是Scanner輸入中文,在控制檯輸出的時候都會出現亂碼的情況,而且不管在Window-Preferences中怎麼改變編碼方式都無效(包括UTF-8和GBK)。 import

Jenkins控制臺中文輸出亂碼解決方法

http 在服務器 管理 中文 服務器環境 電腦 image tomcat pps 1、 設置jenkins所在服務器環境變量,右鍵我的電腦→屬性→高級系統設置→環境變量,添加JAVA_TOOL_OPTIONS 2、修改Tomcat配置,進入apache_tomcat

qtcreator_process_stub中文輸出亂碼

中文 修改 -c anti col size comm 輸出 控制 使用qt運行程序輸出中文,全都變成了□,讓人很頭疼,百度了很久,找了一些解決方案都是: 用vim打開x11-common,在控制臺輸入 vim /etc/X11/Xresources/x11-common

python程式設計中中文輸出亂碼UnicodeEncodeError: 'ascii' codec can't encode character解決方案

問題是這樣的 我用的jupyter,下圖是我的原始碼我知道由於未把ASCII轉為utf8,但是我按照網上的程式碼修改後直接沒有output了 我加上 import sys reload(sys) sys.setdefaultencoding('utf-8')

log4j 控制檯和檔案輸出亂碼問題解決

一個小問題,卻讓我感覺到,現在真正動腦的人很少。。我來說說吧。 今天遇到一個小問題,log4j輸出到檔案亂碼,控制檯正常。顯然是編碼問題導致。Google一搜,幾乎一水的說: 專案中log4j在英文版linux下輸出中文日誌為亂碼。由於

關於CString的一點小收穫,CString在控制檯程式輸出到螢幕

CString是一個非常好用的類,這就不用多說了,但是由於只能在MFC中使用,使得他的使用變得非常有限。筆者通過上網研究和親自實踐發現在控制檯程式中也可以輕鬆使用CString。 首先,要在控制檯中使用CString必須要加入標頭檔案 #include <atlst

vs2015:/utf-8選項解決UTF-8 without BOM 原始碼中文輸出亂碼問題

本來我已經參考網上關於C++中文輸出亂碼的文章解決了,如下面的程式碼輸出前呼叫wcout.imbue設定locale,就可以正常輸出中文了。 std::wcout.imbue(std::locale(std::locale(), "", LC_CTYPE)

windows cmd視窗adb logcat 檢視中文輸出亂碼

Android logcat輸出預設是UTF-8編碼,要想正確顯示中文,需要設定在windows cmd視窗的編碼設定,以下為設定步驟。 步驟1:開啟cmd.exe命令列視窗 步驟2:通過 chcp命令改變內碼表,UTF-8的內碼表為65001 cmd下輸入 C:/

關於python中文輸出亂碼的處理

    用python跟中文打交道的時候,常遇到輸出無法識別的亂碼,比如:     1、亂碼:TNSNames.ora����.lnk     2、報錯:UnicodeDecodeError: 'as

eclipse 控制檯中文輸出出現亂碼情況及解決

今天向eclipse中匯入了一個專案,我的eclipse本身預設編碼方式是UTF-8,而這個專案的編碼是gbk,所以很自然的,程式碼檔案中的中文變成了亂碼,於是右擊專案名稱,點選-->Properties,將檔案編碼改成gbk.。(這種方式哦是將該專案的編碼方式改變,其

IDEA_JavaEE_中文在 提示資訊 / 控制檯輸出亂碼的問題

參考文章: 1. 解決IntelliJ IDEA控制檯亂碼問題[包含程式執行時的log4j日誌以及tomcat日誌亂碼] 2. IDEA tomcat熱部署方法及亂碼問題解決 3. IDEA tomcat亂碼 之前博主遇到了在 前端接收到中文資料在後臺顯示亂碼

關於VisualStudio一運行帶中文程序就出錯或輸出亂碼問題的解決

彈出 als 單擊 bsp 由於 進入 cnblogs 中文 標簽 昨晚糾結了老半天,各種查資料最後終於解決了此問題。今天上午便來編寫這篇隨筆了!(由於問題已解決,未附上出狀況的截圖)以下是解決辦法: 此問題的原因應是文件的編碼問題,選定好出錯的文件後,在菜單欄中選擇“文件

xamarin 安卓輸出中文錯誤 亂碼解決

col png alt utf 信息 OS pan family 亂碼 在編譯設置附加參數 -J-Duser.language=en 這個錯誤信息是來自javac 編譯產生的 而中文亂碼問題是 GBK 和UTF8 的問題 解決的辦法就是讓j

Servlet 知識點 中文亂碼的本質解決

img str 讀取文件 因此 let 就會 原因 例如 應該 本質原因:在servlet中出現中文亂碼的原因編碼和解碼采用的不是一個編碼表或者兩個編碼表不是兼容   例如UTF-8編碼、GBK編碼都可以讀取中文,那麽如果采用UTF-8編碼保存文件,但是采用GBK編碼讀取文