C/C++寬窄字符轉換與輸出的多種實現方法

前言

如果是C/C++程序員,對於VS應該是不陌生的,可謂是C/C++程序手中的利器

但如果稍微深入學習就會發現,windows API大部分都是分為寬字節與窄字節的,比如常見的MessageboxA與MessageBoxW函數,這時候就會出現很多問題,最常見的便是亂碼

需要註意的是,WIndows底層函數均采用的是寬字節,即使你使用的是char,程序真正執行的時候,還是會在底層將char轉化為wchar_t,這就意味著使用窄字節效率是比不上寬字節

同時需要知道.wchar_t是支持多個國傢語言的,而char隻支持本國語言.

一、什麼是寬字節?什麼是窄字節?

認識寬窄字節最好的辦法就是動手實驗一下

可以看到,最直接的影響就是大小,char隻占一個字節,而wchar_t要占兩個字節,並且需要在字符串前加 L 才表示是寬字節

其實還有很多細節,比如這裡是使用的字符c,如果是使用的漢字,還能正常使用嗎?很多問題需要自己碰到並解決,最後才能是自己的東西

二、寬窄字節之間的轉化方法

1.Windows API進行轉化

頭文件:

#include<Windows.h>

用到的函數

窄字節轉寬字節:

int MultiByteToWideChar(
UINT    CodePage,  //要轉換的代碼頁,一般直接填CP_ACP,表示當前系統使用的代碼頁
DWORD   dwFlags, //轉換標志,直接填0即可
LPCCH lpMultiByteStr, //要轉換的窄字節字符串
int   cbMultiByte, //窄字節字符串的長度,以字節計算
LPWSTR  lpWideCharStr, //存放轉換完成的寬字符緩沖區
int    cchWideChar //存放寬字符緩沖區的大小
);

寬字節轉窄字節:

int WideCharToMultiByte(
UINT CodePage,//要轉換的代碼頁,一般直接填CP_ACP,表示當前系統使用的代碼頁
 DWORD dwFlags,//轉換標志,直接填0即可
LPCWCH lpWideCharStr,//要轉換的寬字節字符串
int cchWideChar, //寬字節字符串的長度,以字符計算
LPSTR lpMultiByteStr, //存放轉換完成的窄字符緩沖區
 int cbMultiByte, //存放寬字符緩沖區的大小
LPCCH lpDefaultChar, //如果字符無法轉換,則使用該字符填充,一般填0,默認即可
LPBOOL lpUsedDefaultChar //如果出現無法轉換的字符,該參數被設為true,默認填NULL即可
);

一般寬字符是窄字符字節長度的兩倍,但也有可能出現意外情況,或者不想自己計算需要多大的緩沖,則可以調用兩次該函數,第一次返回需要的大小,第二次進行轉換

下面是我封裝的兩個函數,可直接使用,但需要自己delete內存,可以自己使用wstring和string進行替換

窄字節轉寬字節:

//str:要轉換的窄字符串
//len:接受轉換成功後寬字符的長度,可直接填NULL,不接收
wchar_t* AtoW(const char* str, int* len)
{
	int wcLen = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
	wchar_t* newBuf = new wchar_t[wcLen + 1]{};
	MultiByteToWideChar(CP_ACP, 0, str, -1, newBuf, wcLen);
	if (len != NULL) {
		*len = wcLen;
	}
	return newBuf;
}

寬字節轉窄字節

//str:要轉換的寬字符串
//len:接受轉換成功後窄字符的長度,可直接填NULL,不接收
char* WtoA(const wchar_t* str, int* len)
{
	int cLen = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, 0, NULL);
	char* newBuf = new char[cLen + 1]{};
	WideCharToMultiByte(CP_ACP, 0, str, -1, newBuf, cLen, 0, NULL);
	if (len != NULL) {
		*len = cLen;
	}
	return newBuf;
}

2.C/C++庫函數轉換

用到的頭文件:

#include<cstdlib> //包含轉換函數
#include<locale> //包含設置地域函數

用到的函數:

設置地域,當試圖轉換中文時,需要設置,否則為亂碼

char* setlocale(
int  _Category, //設置該函數影響范圍,一般直接填LC_ALL,即全部影響
char const* _Locale //一般填空,即使用本地地域信息
);

標準窄轉寬:

size_t mbstowcs(
wchar_t    _Dest, //轉換後存放的地方
const char * _Source, //要轉換的內容
size_t   _MaxCount //轉換後存放地方的大小,以字符個數計算
)

使用:

#define _CRT_SECURE_NO_WARNINGS //必須定義宏,否則VS報錯
#include<iostream>
#include<cstdlib>
#include<locale>
using namespace std;
int main() {
	setlocale(LC_ALL, ""); //設置本地地域信息.否則轉換中文出現亂碼
	wchar_t buf[0xFF];
	mbstowcs(buf, "哈哈哈哈", 0xFF);
}

標準寬轉窄:

size_t wcstombs(
char*_Dest, //轉換後存放的地方
const wchar_t* _Source, //要轉換的內容
size_t   _MaxCount //轉換後存放地方的大小,以字符個數計算
)

使用:

#define _CRT_SECURE_NO_WARNINGS //必須定義宏,否則VS報錯
#include<iostream>
#include<cstdlib>
#include<locale>
using namespace std;
int main(){
	setlocale(LC_ALL,""); //設置本地地域信息.否則轉換中文出現亂碼
	char buf[0xFF];
	wcstombs(buf,L"哈哈哈哈哈",sizeof(buf));
}

安全函數窄轉寬:

errno_t mbstowcs_s(
size_t* _PtNumOfCharConverted, //接收轉換成功的字符個數
 wchar_t*    _DstBuf, //接受成功轉換的字符
size_t  _SizeInWords, //_DesBuf緩沖區大小,以字符計算
 char const* _SrcBuf, //要轉化的字符
 size_t      _MaxCount //最大要轉化的字符數量
    );

使用:

#include<iostream>
#include<cstdlib>
using namespace std;
int main() {
	wchar_t buf[0xFF];
	mbstowcs_s(NULL,buf,0xFF, "哈哈哈哈", 0xFF);
}

安全函數寬轉窄:

errno_t wcstombs_s(
size_t* _PtNumOfCharConverted, //接收轉換成功的字符個數
 wchar_t*    _DstBuf, //接受成功轉換的字符
size_t  _SizeInWords, //_DesBuf緩沖區大小,以字符計算
 char const* _SrcBuf, //要轉化的字符
 size_t      _MaxCount //最大要轉化的字符數量
    );

使用:

#include<iostream>
#include<cstdlib>
#include<locale>
using namespace std;
int main() {
	setlocale(LC_ALL,"");
	char buf[0xFF];
	wcstombs_s(NULL, buf, 0xFF, L"哈哈哈哈哈", sizeof(buf));
	printf("%s",buf);
}

大傢可能看到,我有時使用瞭setlocal,有時沒有使用,這個可以根據具體情況而定,如果出現中文無法轉化的情況,就要考慮使用這個函數瞭

而且我都沒有接收轉化字符個數,也就是第一個參數,如果需要準確接受轉化成功字符的個數,就必須要使用setlocal函數

可能大傢還看到過_wcstombs_s_l等函數,這個函數還需要_create_locale與 _free_locale函數配合使用,考慮下來,過於麻煩,不如上面的幾種轉化方法,所以便不予講解,大傢有興趣可以去查看官網說明,鏈接在此 函數說明 設置本地說明

3.ATL庫轉換

ATL模板庫是微軟推出的一個C++模板庫,在visual studio中安裝瞭C++開發環境就可以正常使用

寬轉窄:

#include<iostream>
#include<atlconv.h> //頭文件
using namespace std;
int main() {
    USES_CONVERSION; //必須添加
    wchar_t buf[] = L"哈哈哈哈哈哈";
    char *nbuf=W2A(buf); //使用棧空間進行轉換,不需要delete
    cout << nbuf << endl;
}

窄轉寬:

#include<iostream>
#include<locale>
#include<atlconv.h> //頭文件
using namespace std;
int main() {
    setlocale(LC_ALL,""); //本地化,為輸出漢字
    USES_CONVERSION; //必須添加
    char buf[] = "哈哈哈哈哈哈";
    wchar_t *nbuf=A2W(buf); //使用棧空間進行轉換,不需要delete
    wcout << nbuf;
}

4.COM組件轉換

寬轉窄:

#include<comutil.h>
#pragma comment(lib, "comsuppw.lib")
int main() {
	wchar_t str[] = L"哈哈哈";
	char *s=_com_util::ConvertBSTRToString(str); //轉換函數
	//其它操作
	delete[] s; //釋放內存
}

窄轉寬:

#include<comutil.h>
#pragma comment(lib, "comsuppw.lib")
int main() {
	char str[] = "哈哈哈哈";
	wchar_t *s=_com_util::ConvertStringToBSTR(str); //轉換函數
	//其它操作
	SysFreeString(s); //釋放內存
}

三.解決VS控制臺無法輸出寬字符問題

方法一:使用setlocal和printf函數:

#include<iostream>
#include<locale>
using namespace std;
int main() {
	setlocale(LC_ALL,"");
	printf("%ls",L"瞭解");
}

方法二:使用setlocal與wcout

#include<iostream>
#include<locale>
using namespace std;
int main() {
	setlocale(LC_ALL,"");
	wcout << L"哈哈哈哈啊";
}

以上兩種方法,如果寬字符串中沒有中文字符,則可不使用setlocal函數

方法三:使用WriteConsoleW函數

#include<Windows.h>
int main() {
	wchar_t buf[]=L"哈哈哈哈哈哈哈哈哈哈";
	WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),buf,sizeof(buf)/2, NULL, 0);
}

到此這篇關於C/C++寬窄字符轉換與輸出的多種實現方法的文章就介紹到這瞭,更多相關C/C++寬窄字符轉換與輸出內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: