一、C++11对Unicode的支持
在C++98中,引入wchar_t对Unicode支持,但是后来由于不同平台下wchar_t的宽度并不相同(8,16,32位),导致可移植性受到影响。因此从C++11开始引入了char16_t、char32_t以及原有的char,分别存储utf16,utf32和utf8编码的数据。
此外还定义了字符串常量的前缀:
- u8表示以utf8编码
- u表示utf16编码
- U表示utf32编码
以上三种,再加上wchar_t格式的L以及不带前缀的字符串,C++11则包含了5种字符串常量的前缀。
通常情况下,对于连续的字符串常量,C++会要求编译器将其连起来,比如"a" "b"和"ab"没有区别。
此外,对于多个连续声明的字符串常量,只要有一个有前缀,则编译器会将这些连续的字符串常量都以此前缀处理。(实际上vs2017并不允许)
此外,C++11还支持"\u十六进制码位"表示unicode字符。
std::string str = "\u4F60\u597d"; //中文字符:你好
而想要正确显示一个字符(不乱码),要求输入编码格式、文件存储编码格式,编译时选择的编码格式、以及输出显示编码格式四者一致才能正确,否则都会出现乱码的现象。
二、对于Unicode库的支持
1.单码位转换
size_t mbrtoc16(char16_t * pc16, const char * s, size_t n, mbstate_t * ps);size_t c16rtomb(char * s, char16_t c16, mbstate _t * ps);size_t mbrtoc32(char32_t * pc32, const char * s, size_t n, mbstate_t * ps);size_t c32rtomb(char * s, char32_t c32, mbstate_t * ps);
mb表示multiple bytes,这四个函数都表示单码位的转换。
#include <climits>
#include <clocale>
#include <cuchar>
#include <iomanip>
#include <iostream>
#include <string>int main()
{std::setlocale(LC_ALL, "en_US.utf8");std::u16string strv = u"zß水🍌"; // or z\u00df\u6c34\U0001F34Cstd::cout << "Processing " << strv.size() << " UTF-16 code units: [ ";for (char16_t c : strv)std::cout << std::showbase << std::hex << static_cast<int>(c) << ' ';std::cout << "]\n";std::mbstate_t state{};char out[MB_LEN_MAX]{};for (char16_t c : strv){std::size_t rc = std::c16rtomb(out, c, &state);std::cout << static_cast<int>(c) << " converted to [ ";if (rc != (std::size_t) - 1)for (unsigned char c8 : std::string{ out, rc })std::cout << +c8 << ' ';std::cout << "]\n";}
}
#include <clocale>
#include <cstring>
#include <cuchar>
#include <cwchar>
#include <iomanip>
#include <iostream>int main()
{std::setlocale(LC_ALL, "en_US.utf8");std::string str = "z\u00df\u6c34\U0001F34C"; // or u8"zß水🍌"std::cout << "Processing " << str.size() << " bytes: [ " << std::showbase;for (unsigned char c : str)std::cout << std::hex << +c << ' ';std::cout << "]\n";std::mbstate_t state{}; // zero-initialized to initial statechar16_t c16;const char *ptr = &str[0], *end = &str[0] + str.size();while (std::size_t rc = std::mbrtoc16(&c16, ptr, end - ptr + 1, &state)){std::cout << "Next UTF-16 char: " << std::hex<< static_cast<int>(c16) << " obtained from ";if (rc == (std::size_t) - 3)std::cout << "earlier surrogate pair\n";else if (rc == (std::size_t) - 2)break;else if (rc == (std::size_t) - 1)break;else{std::cout << std::dec << rc << " bytes [ ";for (std::size_t n = 0; n < rc; ++n)std::cout << std::hex << +static_cast<unsigned char>(ptr[n]) << ' ';std::cout << "]\n";ptr += rc;}}
}
2.多码位转换
std::codecvt<char, char, std::mbstate_t> // 完成多字节与char之间的转换std::codecvt<char16_t, char, std::mbstate_t> // 完成UTF-16与UTF-8间的转换std::codecvt<char32_t, char, std::mbstate_t> // 完成UTF-32与UTF-8间的转换std::codecvt<wchar_t, char, std::mbstate_t> // 完成多字节与wchar_t之间的转换
以及派生codecvt_utf8、codecvt_utf16、codecvt_utf8_utf16等可以用于字符串转换的模板类。这些模板类配合C++11定义的wstring_convert模板,可以进行一些不同字符串的转换。
三、原生字符串字面量支持
C++11开始支持原生字符串字面量,语法是R"(string)".
#include <iostream>int main()
{std::cout << "12345\t\n890" << std::endl;std::cout << R"(12345\t\n890)" << std::endl;
}
输出:
12345
890
12345\t\n890
而原生字符串字面值也可以添加前缀u,u8,U等表示字符编码内容:
std::cout << uR"(12345\t\n890)" << std::endl;std::cout << UR"(12345\t\n890)" << std::endl;std::cout << u8R"(12345\t\n890)" << std::endl;
输出:
006DEBE4
006DEB3C
12345\t\n890