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 测试代码 ...
随机推荐
- 敏捷开发流程之Scrum:3个角色、5个会议、12原则
本文主要从Scrum的定义和目的.敏捷宣言.Scrum中的人员角色.Scrum开发流程.敏捷的12原则等几方面帮助大家理解Scrum敏捷开发的全过程. 一.Scrum的定义和目的 Scrum是一个用于 ...
- Go并发编程
概述 简而言之,所谓并发编程是指在一台处理器上"同时"处理多个任务. 随着硬件的发展,并发程序变得越来越重要.Web服务器会一次处理成千上万的请求.平板电脑和手机app在渲染用户画 ...
- 22.Python安装和卸载第三方模块方法
安装和卸载第三方开源模块的步骤:下例,安装urllib3模块的步骤. 1.安装开源模块步骤: 按键盘windows键+r键,输出cmd回车.或开始->windows系统->命令提示符: 输 ...
- Cassandra数据建模中最重要的事情:主键
Cassandra数据建模中要了解的最重要的事情:主键 使用关系数据建模,您可以从主键开始,但是RDBMS中的有效数据模型更多地是关于表之间的外键关系和关系约束.由于Cassandra无法使用JOIN ...
- Oracle数据库设计省市区小区数据库建表
省CREATE TABLE "SF_JECF_BASE"."SF_PROVINCE" ( "id" VARCHAR2(64 BYTE) NO ...
- POJ 1269 Intersecting Lines(判断两直线位置关系)
题目传送门:POJ 1269 Intersecting Lines Description We all know that a pair of distinct points on a plane ...
- 如何制作地图故事使用esri story maps
博客作者原创 制作方法如下:http://url.cn/5dnsVQd
- spring boot通过@Bean注解定义一个Controller
功能需求 提供一个公共的jar包给其他业务模块依赖,需要在这个公共的jar中暴露一个restful API 采用spring auto config机制,在公共jar包中定义spring.factor ...
- document.visibilityState 监听浏览器
document.hidden:表示页面是否隐藏的布尔值.页面隐藏包括 页面在后台标签页中 或者 浏览器最小化 (注意,页面被其他软件遮盖并不算隐藏,比如打开的 sublime 遮住了浏览器). do ...
- java jdk9的特性 jshell
1.进入 jshell 2.推出 /exit() 和python的解释器用法差不多