C++ STL IO流 与 Unicode (UTF-16 UTF-8) 的协同工作
#include<string>
#include<iostream>
#include<locale>
#include <fstream>
#include <codecvt>
#include <io.h>
#include <fcntl.h>
using namespace std; int main()
{
wstring wstr; wifstream file("test.txt");
file.imbue(locale(file.getloc(), new codecvt_utf8<wchar_t, 0x10FFFF, consume_header>)); _setmode(_fileno(stdout), _O_U8TEXT);
//wcout.imbue(locale("chs"));//在console输出 while (file >> wstr)
wcout << wstr << '\n';
file.close();
}
#include<string>
#include<iostream>
#include<locale>
using namespace std; int main()
{
locale prevloc;
locale loc("chs");
string str1("string class");
string str2("汉字与字符");
wstring wstr1(L"wstring class"); //去掉L前缀则编译错误
wstring wstr2(L"汉字与字符"); prevloc = cout.imbue(locale(""));
cout << "Default Locale: " << prevloc.name() << endl;
cout << "System Locale: " << locale("").name() << endl;
cout << "C风格字符串\n" << L"w-string\n" << str1 << '\n' << str2 << '\n' << endl; prevloc = wcout.imbue(loc); //若去掉此句,则wstr2无法正常输出
wcout << "Default Locale: " << prevloc.name().c_str() << endl; //若不加 .c_str() 则编译错误
wcout << "chs Locale Name: " << loc.name().c_str() << endl;
wcout << "C-string\n" << "C风格字符串\n" << L"宽字符串\n" << wstr1 << '\n' << wstr2 << '\n' << endl;
}
输出
Default Locale: C
System Locale: Chinese (Simplified)_People's Republic of China.936
C风格字符串
00,963,004string class
汉字与字符 Default Locale: C
chs Locale Name: Chinese (Simplified)_People's Republic of China.936
C-string
C
宽字符串
wstring class
汉字与字符 请按任意键继续. . .
2.wstring 初始化时需用 L"xxx" 的宽字符形式,同样 string 初始化时不能加 L 前缀
3.默认locale ("C")下 cout 可以正常输出 C风格字符串与std::string类型,包括汉字也能正常显示
但对 L"xxx" 宽字符串无能为力
默认locale ("C")下 wcout 不能输出中文,包括C风格字符串(可以输出 wcout << "你好"; wcout << L"你好";不能输出)、宽字符串与std::wstring
设定系统 locale ("chs")后,正常输出宽字符串与std::wstring,但 C风格字符串 中的汉字无法显示
#include<string>
#include<iostream>
#include<locale>
using namespace std; int main()
{
cout.imbue(locale(""));
wcout.imbue(locale("")); string str1("string class");
string str2("汉字与字符"); string str3("abc汉字");
wstring wstr1(L"wstring class");
wstring wstr2(L"汉字与字符");
wstring wstr3(L"abc汉字"); cout << "str1 length: " << str1.length() << '\n'; // 12
cout << "str2 length: " << str2.length() << '\n'; // 10
cout << "str3 length: " << str3.length() << '\n'; // 7
cout << str2[0] << " " << str2[1] << '\n'; // 输出:?
cout << endl;
wcout << L"wstr1 length: " << wstr1.length() << '\n'; // 13
wcout << L"wstr2 length: " << wstr2.length() << '\n'; // 5
wcout << L"wstr3 length: " << wstr3.length() << '\n'; // 5
wcout << wstr2[0] << " " << wstr2[1] << '\n'; // 输出:汉 字
}
输出:
str1 length: 12
str2 length: 10
str3 length: 7
? wstr1 length: 13
wstr2 length: 5
wstr3 length: 5
汉 字
请按任意键继续. . .
字符串所占字节数而不是字符数
std::wstring 内部以 wchar_t 类型存储字符,字母汉字统一都是双字节,此时 length()
给出是正确的字符数。
5.当std::string中有汉字存在时,通过下标访问不能得到正确的字符。这是显而易见的,
一方面字符宽度不统一无法随机访问,另一方面 std::string[] 返回 char 类型。std::wstring
#include<string>
#include<iostream>
#include<locale>
#include <fstream>
using namespace std; int main()
{
string str;
wstring wstr; ifstream fin("test.txt");
//fin.imbue(locale(""));
while (fin >> str)
cout << str << '\n';
fin.close(); wifstream wfin("test.txt");
//wfin.imbue(locale(""));
//wfin.imbue(locale(".936"));
while (wfin >> wstr)
wcout << wstr << '\n';
wfin.close();
}
输出:
abc汉字
wstring
class
汉字与字符
abc汉字 abc汉字
wstring
class
汉字与字符
abc汉字
请按任意键继续. . .
std::wifstream 设置 imbue(locale("")) 或 locale(".936") 后正常读取(不能正确读取)。936 为 GB2312 的代码页。
test.txt 为 Shift-JIS 编码,内容为
うみねこのなく頃に
偆傒偹偙偺側偔崰偵
wifstream 设定 imbue(locale("")) 后输出相同
或读取时产生乱码的原因。
test.txt 为 Shift-JIS 编码,内容同上
ifstream 与 wifstream 都添加 imbue(locale("jpn")) 或 locale(".932")
输出为:
うみねこのなく頃に
Unicode 储存,在 wcout 输出时又按照 ANSI (GB2312) 转换,其结果是 —— 正确显示
test.txt 存为 UTF-16 编码(Win32 默认的 little endian),内容同上。
wifstream 设定为 imbue(locale(".1200"))
1200 为 UTF-16 的 code page
试着将 ".1200" 改为 ".936" 则运行正常,输出乱码。(936是 GB2312 的代码页)
翻 MSDN 时在 Code Page 那页1200 UTF-16 后面发现一行小字:
"available only to managed applications"…郁闷
看来用 locale 转Unicode的想法到此结束了?记得 STL 书中貌似说过,locale 的名
字在各平台上是不统一的,因为关系到各平台的支持问题。这样的话,要么自己写
代码,要么就只好用 API 显式转换了:MultiByteToWideChar
的编码不被支持,所以 UTF-8 也只能用 MultiByteToWideChar 转咯…
目前大概只能得出结论 C++ STL locale 在 Win32 平台上支持不完善吧
if(INVALID_HANDLE_VALUE != (hFile = CreateFileW(L"test.txt",
GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL))){
int iFileLength, iUniTest, i;
iFileLength = GetFileSize(hFile,NULL);
char *pBuffer, *pText;
pBuffer = new char[iFileLength+2];
DWORD dwBytesRead;
CloseHandle(hFile);
pBuffer[iFileLength] = ”;
pBuffer[iFileLength + 1] = ”;
if(IsTextUnicode(pBuffer,iFileLength,&iUniTest)){
pText = pBuffer + 2;
iFileLength -= 2;
if(iUniTest & IS_TEXT_UNICODE_REVERSE_SIGNATURE){
for(i = 0;i < iFileLength; i+=2)
swap(pText[i],pText[i+1]);
}
wstr = (wchar_t*)(pBuffer+2);
}
delete [] pBuffer;
}
wchar_t* 后 std::wstring 即可正确识别,说明程序中的宽字符存储格式实际上用的就是
UTF-16 little endian
不死心又去翻了 boost 库,发现 codecvt_null 这个好东西,看下实现是把文件存储内容
按照 wchar_t 为单位直接读入内存不做任何转换。这其实不正好是 UTF-16 需要做的么
以下把 test.txt 存为 UTF-16 little endian 再次实验
locale utf16(loc, new boost::archive::codecvt_null<wchar_t>);
wfin.imbue(utf16);
while(wfin>>wstr){
wcout<<wstr<<endl;
wfin.close();
来使用,避免使用类似上面 API 那么多代码。
将 test.txt 存为 UTF-16 Big Endian ,内容不变。程序不变
codecvt_utf16_reverse 实现 little endian 与 big endian 的输入。
locale utf16(loc,new tvt::codecvt_utf16<wchar_t>);
wfin.imbue(utf16);
while(wfin>>wstr){
wcout<<wstr<<endl;
wfin.close();
locale utf16(loc,new tvt::codecvt_utf16_reverse<wchar_t>);
wfin.imbue(utf16);
while(wfin>>wstr){
wcout<<wstr<<endl;
wfin.close();
wchar_t 类型的 buffer[i] 中。其实 buffer 中每个 wchar_t 的高位都字节是 0 …
加入判断条件,在 wfin 中自动加入合适的 utf16 facet,使得自动识别并读取
little endian 和 big endian 编码的文件:
wifstream wfin(L"test.txt");
wfin.read(buf,2);
cout<<"little endian"<<endl;
wfin.imbue(locale(loc,new tvt::codecvt_utf16<wchar_t>));
}
else if(buf[0] == wchar_t(0xFE) && buf[1] == wchar_t(0xFF)){
cout<<"big endian"<<endl;
wfin.imbue(locale(loc,new tvt::codecvt_utf16_reverse<wchar_t>));
}
while(wfin>>wstr){
wcout<<wstr<<endl;
}
也有 IsTextUnicode 这 API 用专门方法判断。UTF-8 就很麻烦了,开头不一定都有 BOM 标
if(!tvt::IsStreamUnicode(wfin))
wfin.imbue(loc);
while(wfin>>wstr)
wcout<<wstr<<endl;
并正确设定转码 locale .
std::wofstream wfout(L"testout.txt", ios_base::binary);
wfout.imbue(locale(locale(""), new tvt::codecvt_utf16));
wfin.imbue(locale(""));
// wcout<<wstr<<endl;
// wfout<<wstr<<endl;
//}
wcout<<wstr<<endl;
wfout<<wstr<<endl;
}
wfout.close();
locale 的使用总结
C++ STL IO流 与 Unicode (UTF-16 UTF-8) 的协同工作的更多相关文章
- 16个常用IO流
在包java.io.*:下 有以下16个常用的io流类: (Stream结尾的是字节流,是万能流,通常的视频,声音,图片等2进制文件, Reader/Writer结尾的是字符流,字符流适合读取纯文本文 ...
- JAVA基础知识总结16(IO流)
IO流:用于处理设备上数据. 流:可以理解数据的流动,就是一个数据流.IO流最终要以对象来体现,对象都存在IO包中. 流也进行分类: 1:输入流(读)和输出流(写). 2:因为处理的数据不同,分为字节 ...
- 35 编码 ASCII Unicode UTF-8 ,字符串的编码、io流的编码
* 编码表: * 信息在计算机上是用二进制表示的,这种表示法让人理解就很困难.为保证人类和设备,设备和计算机之间能进行正确的信息交换,人们编制的统一的信息交换代码,这就是ASCII码表 *ASCII ...
- Java基础知识强化之IO流笔记16:IO流的概述和分类
1. IO流的分类 流向: (1)输入流:读取数据到内存 (2)输出流:写入数据到硬盘(磁盘) 操作的数据类型: (1)字节流:操作的数据是字节 ...
- (16)IO流之输入字节流FileInputStream和输出字节流FielOutputStream
IO流技术解决的问题:设备与设备之间的传输问题,内存-->硬盘,硬盘-->内存,等等 IO流技术 如果按照数据的流向划分可以划分为:输入流和输出流 输入输出的标准是以程序为参考物的,如果流 ...
- Java IO流
File类 ·java.io.File类:文件和目录路径名的抽象表示形式,与平台无关 ·File能新建.删除.重命名文件和目录,但File不能访问文件内容本身.如果需要访问文件内容本身,则需要使用输入 ...
- Android(java)学习笔记167:Java中操作文件的类介绍(File + IO流)
1.File类:对硬盘上的文件和目录进行操作的类. File类是文件和目录路径名抽象表现形式 构造函数: 1) File(String pathname) Creat ...
- 第十一章 IO流
11.IO流 11.1 java.io.File类的使用 1课时 11.2 IO原理及流的分类 1课时 11.3 节点流(或文件流) 1课时 11.4 缓冲流 1课时 11.5 转换流 1课时 11. ...
- IO流 简介 总结 API 案例 MD
目录 IO 流 简介 关闭流的正确方式 关闭流的封装方法 InputStream 转 String 的方式 转换流 InputStreamReader OutputStreamWriter 测试代码 ...
随机推荐
- .net 异步编程总结
异步的方式,就是,先发起IO.CPU密集工作等,然后函数返回,在IO.CPU密集工作等完成了以后——某个不确定的时刻,再执行后续的代码. 所以,如果使用异步代码,必须注意代码的执行顺序. 所以,异 ...
- 1052 卖个萌 (20 分)C语言
萌萌哒表情符号通常由"手"."眼"."口"三个主要部分组成.简单起见,我们假设一个表情符号是按下列格式输出的: [左手]([左眼][口][右 ...
- js滑动导航栏点击后居中效果
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- python常用英语单词(初学,英语不好的适用)
对于刚才是学习python这些也足够了,一天学个六七个单词记一下在配合自己寻找的视频.书籍等等方法去学习是有一定帮助的. 这里还是要说一句,仅供兴趣爱好学习使用,个人开发者(非考虑未来靠此为生的人士) ...
- 【记】VM VirtualBox 网络地址转换(NAT)使用详解
1. 查看虚拟机Centos6的ip 但是这个IP地址并不能直接连接,因为本地VBox网络连接方式采用的是“网络地址转换(NAT)”(如上上图所示),也就是说 10.0.2.15 这地址是转换的. 2 ...
- 【转】最受欢迎的8位Java牛人
本文由 ImportNew - 唐尤华 翻译自 javatyro.如需转载本文,请先参见文章末尾处的转载要求. 下面是8位Java牛人,他们为Java社区编写框架.产品.工具或撰写书籍改变了Java编 ...
- Spring Boot2 系列教程(一) | 如何使用 IDEA 构建 Spring Boot 工程
微信公众号:一个优秀的废人 如有问题或建议,请后台留言,我会尽力解决你的问题. Search 前言 新年立了个 flag,好好运营这个公众号.具体来说,就是每周要写两篇文章在这个号发表.刚立的 fla ...
- Tarjin + 缩点
链接:https://www.nowcoder.com/acm/contest/81/C来源:牛客网 题目描述 给出一个 0 ≤ N ≤ 105 点数.0 ≤ M ≤ 105 边数的有向图, 输出一个 ...
- 使用 OAS(OpenAPI标准)来描述 Web API
无论哪种类型的Web API, 都可能需要给其他开发者使用. 所以API的开发者体验是很重要的. API的开发者体验, 简写为 API DX (Developer Experience). 它包含很多 ...
- python 黏包现象
一.黏包 1.tcp有黏包现象 表现两种情况 发送的数据过小且下面还有一个发送数据,这两个数据会一起发送 发送的数据过大,超过最大缓存空间,超出的部分在下一次发送的时候发送 原因: tcp是面向流的, ...