欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++11 Unicode編碼轉(zhuǎn)換

 更新時(shí)間:2020年08月27日 09:56:23   作者:Dabelv  
這篇文章主要介紹了C++11 Unicode編碼轉(zhuǎn)換的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)c++11,感興趣的朋友可以了解下

1.char16_t與char32_t

在C++98中,為了支持Unicode字符,使用wchar_t類型來表示“寬字符”,但并沒有嚴(yán)格規(guī)定位寬,而是讓wchar_t的寬度由編譯器實(shí)現(xiàn),因此不同的編譯器有著不同的實(shí)現(xiàn)方式,GNU C++規(guī)定wchar_t為32位,Visual C++規(guī)定為16位。由于wchar_t寬度沒有一個(gè)統(tǒng)規(guī)定,導(dǎo)致使用wchar_t的代碼在不同平臺(tái)間移植時(shí),可能出現(xiàn)問題。這一狀況在C++11中得到了一定的改善,從此Unicode字符的存儲(chǔ)有了統(tǒng)一類型:

(1)char16_t:用于存儲(chǔ)UTF-16編碼的Unicode字符。
(2)char32_t:用于存儲(chǔ)UTF-32編碼的Unicode字符。

至于UTF-8編碼的Unicode數(shù)據(jù),C++11還是使用了8bits寬度的char類型數(shù)組來表示,而char16_t和char32_t的寬度由其名稱可以看出,char16_t為16bits,char32_t為32bits。

2.定義字符串的5種方式

除了使用新類型char16_t與char32_t來表示Unicode字符,此外,C++11還新增了三種前綴來定義不同編碼的字符串,新增前綴如下:

(1)u8表示為UTF-8編碼;
(2)u表示為UTF-16編碼;
(3)U表示為UTF-32編碼。

C++98中有兩種定義字符串的方式,一是直接使用雙引號(hào)定義多字節(jié)字符串,二是通過前綴“L”表示wchar_t字符串(寬字符串)。至此,C++中共有5種定義字符串的方式。

3.影響字符串正確處理的因素

在使用不同方式定義不同編碼的字符串時(shí),我們需要注意影響字符串處理和顯示的幾個(gè)因素有編輯器、編譯器和輸出環(huán)境。

代碼編輯器采用何種編碼方式?jīng)Q定了字符串最初的編碼,比如編輯器如果采用GBK,那么代碼文件中的所有字符都是以GBK編碼存儲(chǔ)。當(dāng)編譯器處理字符串時(shí),可以通過前綴來判斷字符串的編碼類型,如果目標(biāo)編碼與原編碼不同,則編譯器會(huì)進(jìn)行轉(zhuǎn)換,比如C++11中的前綴u8表示目標(biāo)編碼為UTF-8的字符,如果代碼文件采用的是GBK,編譯器按照UTF-8去解析字符串常量,則可能會(huì)出現(xiàn)錯(cuò)誤。

//代碼文件為GBK編碼
#include <iomanip>
#include <iostream> 
using namespace std;

int main()
{
  const char* sTest = u8"你好";
  for(int i=0;sTest[i]!=0;++i)
  {
    cout<<setiosflags(ios::uppercase)<<hex<<(uint32_t)(uint8_t)sTest[i]<<" ";
  }
  return 0;
}
//編譯選項(xiàng):g++ -std=c++0x -finput-charset=utf-8 test.cpp

程序輸出結(jié)果:C4 E3 BA C3。這個(gè)碼值是GBK的碼值,因?yàn)椤澳恪钡腉BK碼值是0xC4E3,“好”的GBK碼值是0xBAC3??梢?,編譯器未成功地將GBK編碼的“你好”轉(zhuǎn)換為UTF-8的碼值“你”(E4 BD A0)“好”(E5 A5 BD),原因是使用編譯選項(xiàng)-finput-charset=utf-8指定代碼文件編碼為UTF-8,而實(shí)際上代碼文件編碼為GBK,導(dǎo)致編譯器出現(xiàn)錯(cuò)誤的認(rèn)知。如果使用-finput-charset=gbk,那么編譯器在編譯時(shí)會(huì)將GBK編碼的“你好”轉(zhuǎn)換為UTF-8編碼,正確輸出E4 BD A0 E5 A5 BD。

代碼編輯器和編譯器這兩個(gè)環(huán)節(jié)在處理字符串如果沒有問題,那么最后就是顯示環(huán)節(jié)。字符串的正確顯示依賴于輸出環(huán)境。C++輸出流對(duì)象cout能夠保證的是將數(shù)據(jù)以二進(jìn)制輸出到輸出設(shè)備,但輸出設(shè)備(比如Linux shell或者Windows console)是否能夠支持特定的編碼類型的輸出,則取決于輸出環(huán)境。比如Linux虛擬終端XShell,配置終端編碼類型為GBK,則無法顯示輸出的UTF-8編碼字符串。

一個(gè)字符串從定義到處理再到輸出,涉及到編輯器、編譯器和輸出環(huán)境三個(gè)因素,正確的處理和顯示需要三個(gè)因素的共同保障,每一個(gè)環(huán)節(jié)都不能出錯(cuò)。一個(gè)字符串的處理流程與因素如下圖所示:

當(dāng)然如果想避開編輯器編碼對(duì)字符串的影響,可以使用Unicode碼值來定義字符串常量,參看如下代碼:

//代碼文件為GBK編碼
#include <iomanip>
#include <iostream> 
using namespace std;

int main()
{
  const char* sTest = u8"\u4F60\u597D";  //你好的Uunicode碼值分別是:0x4F60和0x597D
  for(int i=0;sTest[i]!=0;++i)
  {
    cout<<setiosflags(ios::uppercase)<<hex<<(uint32_t)(uint8_t)sTest[i]<<" ";
  }
  return 0;
}
//編譯選項(xiàng):g++ -std=c++0x -finput-charset=utf-8 test.cpp

程序輸出結(jié)果:E4 BD A0 E5 A5 BD??梢?,即使編譯器對(duì)代碼文件的編碼理解有誤,仍然可以正確地以UTF-8編碼輸出“你好”的碼值。原因是ASCII字符使用GBK與UTF-8編碼碼值是相同的,所以直接書寫Unicode碼值來表示字符串是一種比較保險(xiǎn)的做法,缺點(diǎn)就是難以閱讀。

4.Unicode的庫支持

C++11在標(biāo)準(zhǔn)庫中增加了一些Unicode編碼轉(zhuǎn)換的函數(shù),開發(fā)人員可以使用庫中的一些新增編碼轉(zhuǎn)換函數(shù)來完成各種Unicode編碼間的轉(zhuǎn)換,函數(shù)原型如下:

//多字節(jié)字符轉(zhuǎn)換為UTF-16編碼
size_t mbrtoc16 ( char16_t * pc16, const char * pmb, size_t max, mbstate_t * ps);

//UTF-16字符轉(zhuǎn)換為多字節(jié)字符
size_t c16rtomb ( char * pmb, char16_t c16, mbstate_t * ps );

//多字節(jié)字符轉(zhuǎn)換為UTF-32編碼
size_t mbrtoc32 ( char32_t * pc32, const char * pmb, size_t max, mbstate_t * ps);

//UTF-32字符轉(zhuǎn)換為多字節(jié)字符
size_t c32rtomb ( char * pmb, char32_t c32, mbstate_t * ps );

函數(shù)名稱中mb表示multi-byte(多字節(jié)),rto表示convert to(轉(zhuǎn)換為),c16表示char16_t,了解這些,可以根據(jù)函數(shù)名稱直觀的理解它們的作用。下面給一下UTF-16字符串轉(zhuǎn)換為多字節(jié)字符串(以GBK為例)的例子:

#include <uchar.h>
#include <string.h>
#include <locale>
#include <iomanip>
#include <iostream> 
using namespace std;

int main()
{
  const char16_t* utf16 = u"\u4F60\u597D\u554A";
  size_t utf16Len=char_traits<char16_t>::length(utf16);

  char* gbk =new char[utf16Len*2+1];
  memset(gbk,0, utf16Len * 2 + 1);
  char* pGbk = gbk;

  setlocale(LC_ALL, "zh_CN.gbk");
  mbstate_t mbs;            //轉(zhuǎn)換狀態(tài)
  size_t length = 0;
  while (*utf16)
  {
    pGbk += length;
    length = c16rtomb(pGbk, *utf16, &mbs);
    if (length == 0 || pGbk - gbk>sizeof(gbk))
    {
      cout << "failed" << endl;
      break;           //轉(zhuǎn)換失敗
    }
    ++utf16;
  }
  for (int i = 0; gbk[i] != 0; ++i)
  {
    cout << setiosflags(ios::uppercase) << hex << (uint32_t)(uint8_t)gbk[i] << " ";
  }
  return 0;
}
//編譯選項(xiàng):g++ -std=c++0x test.cpp

程序輸出結(jié)果:C4 E3 BA C3 B0 A1??梢?,使用c16rtomb()完成了將“你好啊”從UTF-16編碼到多字節(jié)編碼(GBK)的轉(zhuǎn)換。上面的轉(zhuǎn)換,我們用到了locale機(jī)制。locale表示的是一個(gè)地域的特征,包括字符編碼、數(shù)字時(shí)間表示形式、貨幣符號(hào)等。locale串使用“zh_CN.gbk”表示目的多字節(jié)字符串使用GBK編碼。

上面通過Unicode字符的轉(zhuǎn)換來完成字符串的轉(zhuǎn)換,實(shí)際上C++提供了一個(gè)類模板codecvt用于完成Unicode字符串與多字節(jié)字符串之間的轉(zhuǎn)換,主要分為4種:

codecvt<char,char,mbstate_t>   //performs no conversion
codecvt<wchar_t,char,mbstate_t> //converts between native wide and narrow character sets
codecvt<char16_t,char,mbstate_t> //converts between UTF16 and UTF8 encodings, since C++11
codecvt<char32_t,char,mbstate_t> //converts between UTF32 and UTF8 encodings,since C++11

上面的codecvt實(shí)際上是locale的一個(gè)facet,facet可以簡(jiǎn)單地理解為locale的一些接口。通過codecvt,可以完成當(dāng)前l(fā)ocale下多字節(jié)編碼字符串與Unicode字符間的轉(zhuǎn)換,也包括Unicode字符編碼間的轉(zhuǎn)換。這里的多字節(jié)字符串不僅可以試UTF-8,也可以是GBK或者其它編碼,實(shí)際依賴于locale所采用的編碼方式。每種codecvt負(fù)責(zé)不同類型編碼的轉(zhuǎn)換,但是目前編譯器的支持情況并沒有那么完整,一種locale并不一定支持所有的codecvt,程序員可以通過has_facet函數(shù)模板來查詢指定locale下的支持情況。參考代碼如下:

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

int main()
{
  //定義一個(gè)locale并查詢?cè)搇ocale是否支持一些facet
  locale lc("zh_CN.gbk");
  bool can_cvt = has_facet<codecvt<char, char, mbstate_t>>(lc);
  if (!can_cvt)
    cout<<"do not support char-char facet"<<endl;
  can_cvt = has_facet<codecvt<wchar_t, char, mbstate_t>>(lc);
  if (!can_cvt)
    cout << "do not support wchar_t-char facet" << endl;
  can_cvt = has_facet<codecvt<char16_t, char, mbstate_t>>(lc);
  if (!can_cvt)
    cout << "do not support char16_t-char facet" << endl;
  can_cvt = has_facet<codecvt<char32_t, char, mbstate_t>>(lc);
  if (!can_cvt)
    cout << "do not support char32_t-char facet" << endl;
}
//編譯選項(xiàng):g++ -std=c++11 test.cpp
//g++版本:gcc version 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC)

程序輸出結(jié)果:

do not support char16_t-char facet
do not support char32_t-char facet

由此可見,從char到char16_t與char32_t轉(zhuǎn)換的兩種facet還沒有被實(shí)驗(yàn)機(jī)使用的編譯器支持。

假如實(shí)驗(yàn)機(jī)支持從char與char16_t的轉(zhuǎn)換,可參考如下代碼:

#include <uchar.h>
#include <string.h>
#include <locale>
#include <iomanip>
#include <iostream> 
using namespace std;

int main()
{
  typedef std::codecvt<char16_t,char,std::mbstate_t> facet_type;
  std::locale mylocale("zh_CN.gbk");

  try
  {
    const facet_type& myfacet = std::use_facet<facet_type>(mylocale);

    const char16_t* utf16 = u"\u4F60\u597D\u554A";   //你好啊
    size_t utf16Len = char_traits<char16_t>::length(utf16);
    cout<< utf16Len <<endl;
    char* gbk = new char[utf16Len*2+1];
    memset(gbk, 0, utf16Len * 2 + 1);
    std::mbstate_t mystate;               //轉(zhuǎn)換狀態(tài)
    const char16_t* pwc;                //from_next
    char* pc;                      //to_next

    facet_type::result myresult = myfacet.out(mystate,utf16,utf16+utf16Len+1,pwc, gbk, gbk + utf16Len * 2+1, pc);

    if (myresult == facet_type::ok)
    {
      std::cout << "Translation successful:" << endl;
    }
    for (int i = 0; gbk[i] != 0; ++i)
    {
      cout << setiosflags(ios::uppercase) << hex << (uint32_t)(uint8_t)gbk[i] << " ";
    }
    delete[] gbk;
  }
  catch(...)
  {
    cout<<"do not support char16_t-char facet"<<endl;
    return -1;
  }
  return 0;
}

由于實(shí)驗(yàn)環(huán)境并不支持char與char16_t相互轉(zhuǎn)換的facet,所以程序輸出結(jié)果為:do not support char16_t-char facet。

5.u16string與u32string

C++11新增了UTF-16和UTF-32編碼的字符類型char16_t和char32_t,當(dāng)然少不了對(duì)應(yīng)的字符串類型,分別是u16string與與u32string,二者的存在類似與string與wstring。四者的定義如下:

typedef basic_string<char> string;
typedef basic_string<wchar_t> wstring;
typedef basic_string<char16_t> u16string;
typedef basic_string<char32_t> u32string;

我們對(duì)string與wstring應(yīng)該比較熟悉,對(duì)于u16string與u32string在用法上是差不多了,有相同的成員接口與類型,只需要記住其存儲(chǔ)的字符編碼類型不同即可。下面看一下u16string使用的簡(jiǎn)單示例。

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

int main()
{
  u16string u16str = u"\u4F60\u597D\u554A";  //你好啊
  cout << u16str.length() << endl;      //字符數(shù)  
  for (int i = 0; i<u16str.length(); ++i)
  {
    cout << setiosflags(ios::uppercase) << hex << (uint16_t)u16str[i] << " ";
  }
}

程序輸出:

3
4F60 597D 554A。

以上就是C++11 Unicode編碼轉(zhuǎn)換的詳細(xì)內(nèi)容,更多關(guān)于C++11 Unicode編碼轉(zhuǎn)換的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C語言二叉樹的概念結(jié)構(gòu)詳解

    C語言二叉樹的概念結(jié)構(gòu)詳解

    二叉樹可以簡(jiǎn)單理解為對(duì)于一個(gè)節(jié)點(diǎn)來說,最多擁有一個(gè)上級(jí)節(jié)點(diǎn),同時(shí)最多具備左右兩個(gè)下級(jí)節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)。本文將詳細(xì)介紹一下C++中二叉樹的實(shí)現(xiàn)和遍歷,需要的可以參考一下
    2022-08-08
  • C語言的各類變量和零值的比較詳解

    C語言的各類變量和零值的比較詳解

    這篇文章主要為大家介紹了C語言的各類變量和零值的比較,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2021-11-11
  • vscode中配置LeetCode插件的教程(愉快刷題)

    vscode中配置LeetCode插件的教程(愉快刷題)

    這篇文章主要介紹了vscode中配置LeetCode插件的教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2020-03-03
  • c++作用域運(yùn)算符用法(全局變量和局部變量)

    c++作用域運(yùn)算符用法(全局變量和局部變量)

    這篇文章主要介紹了c++作用域運(yùn)算符用法,需要的朋友可以參考下
    2014-04-04
  • C語言冒泡排序算實(shí)現(xiàn)代碼

    C語言冒泡排序算實(shí)現(xiàn)代碼

    本文主要介紹C語言冒泡排序算法,這里給大家舉例說明冒泡排序的思想,并附有代碼示例,有需要的小伙伴可以參考下
    2016-07-07
  • C++函數(shù)參數(shù)取默認(rèn)值的深入詳解

    C++函數(shù)參數(shù)取默認(rèn)值的深入詳解

    本篇文章是對(duì)C++中函數(shù)參數(shù)取默認(rèn)值進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • 深入理解C++函數(shù)棧幀

    深入理解C++函數(shù)棧幀

    本文主要介紹了C++函數(shù)棧幀,詳細(xì)的介紹了C++函數(shù)棧幀的概念以及使用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • C++ POSIX API超詳細(xì)分析

    C++ POSIX API超詳細(xì)分析

    這篇文章主要介紹了C++ POSIXAPI的使用方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2022-11-11
  • C語言實(shí)現(xiàn)的一個(gè)三子棋游戲詳解流程

    C語言實(shí)現(xiàn)的一個(gè)三子棋游戲詳解流程

    三子棋是一種民間傳統(tǒng)游戲,又叫九宮棋、圈圈叉叉、一條龍、井字棋等。將正方形對(duì)角線連起來,相對(duì)兩邊依次擺上三個(gè)雙方棋子,只要將自己的三個(gè)棋子走成一條線,對(duì)方就算輸了
    2021-10-10
  • C語言中 “_at()” 特殊地址定位詳解

    C語言中 “_at()” 特殊地址定位詳解

    這篇文章主要介紹了C語言中 “_at()” 特殊地址定位詳解的相關(guān)資料,需要的朋友可以參考下
    2017-05-05

最新評(píng)論