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 测试代码 ...
随机推荐
- Spring应用事件(Application Event)
Spring的事件为Bean与Bean的消息通信提供的支持.当一个Bean处理完了一个任务以后,希望另一个Bean知道并能做出相应的处理,这是我们就需要让另一个Bean监听当前Bean所发送的事件. ...
- Persistence.beans
SF_USERS user = new SF_USERS(); user.setCTIME("20170103"); String ids = "fish,water&q ...
- dp - 3维背包(东四省)
题意: 给你 n 张卡片,总共可以消耗的法力值,求最多可以造成多少伤害, 卡片分为2种,一种是魔法卡,使用后可以使所有的连环卡的费用全部减1,另一种是连环卡,因魔法卡的使用可以使其费用减1,问最终最多 ...
- Spring Framework 源码编译导入
预先准备环境 Window 10 JDK环境 List item Gradle 以及其环境变量配置 spring-framework源码(https://gitee.com/mirrors/Sprin ...
- 阿里云Centos7.X 如何对外开放端口
一句话:如果你是买的各大厂商的云服务器,去安全组配置对应需要使用到的端口就可以啦! 因为博主用的是阿里云,所以这里就只介绍下阿里云如何开放端口,按着下面三张图来操作就行嘞 这个时候有同学就说了,不通过 ...
- struts2学习第一天
Stuts2是基于MVC设计模式成熟的Web应用框架.不仅仅是Struts1的下一个版本,是一个全新的Struts架构.由WebWork社区跟Strut社区联手打造的.(教程来自W3Cschool) ...
- 关于爬虫的日常复习(16)—— pyspider的初高级用法
- 洛谷 UVA1395 苗条的生成树 Slim Span
题目链接 题目描述 求所有生成树中最大边权与最小边权差最小的,输出它们的差值. 题目分析 要求所有生成树中边权极差最小值,起初令人无从下手.但既然要求所有生成树中边权极差最小值,我们自然需要对每一棵生 ...
- springboot的yml不显示的原因
首先排除插件原因 1 安装好插件Ctrl+Alt+S 2 查看修改的application.yml是什么格 在yaml格式中添加*.yaml和*.yml 3 查看maven是否配置完善
- java小心机(4)| 继承与组合的爱恨情仇
在java中,有两种主要复用代码的方法:继承和组合. 继承,是OOP的一大特性,想必大家都非常熟悉了;组合,其实也很常见,只是不知道它的名字罢了. 继承 子类拥有父类的基本特性,需使用extend关键 ...