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 测试代码 ...
随机推荐
- 【一起学源码-微服务】Ribbon 源码四:进一步探究Ribbon的IRule和IPing
前言 前情回顾 上一讲深入的讲解了Ribbon的初始化过程及Ribbon与Eureka的整合代码,与Eureka整合的类就是DiscoveryEnableNIWSServerList,同时在Dynam ...
- CentOS7设置静态IP以及windows下ping不通虚拟机、虚拟机ping不通外网解决方案
问题:CentOS7安装完成后默认使用的是动态IP,当你每次重新启动CentOS7后,它的IP地址都不一样.一般我们都是使用远程连接工具连接CentOS7进行操作,如果每次IP都不一样,系统启动后,每 ...
- 【Java基础总结】多线程
1. 实现多线程的两种方式 //第一种:继承Thread类,重写run()方法 class ThreadTest1 extends Thread{ public void run(){ String ...
- AtCoder Beginner Contest 151 题解报告
总的来说,这次的题目比较水,然而菜菜的我并没有把所有题目都做完,话不多说,直接来干货: A:Next Alphabet 题目链接:https://atcoder.jp/contests/abc151/ ...
- 修饰符new与override
new:在作为修饰符时,可以隐藏从父类的继承的成员. override:修改父类的方法.属性. 上代码比较清楚: using System; using System.Collections.Gene ...
- 什么样的项目适合docker部署,docker应用场景
docker官网上说明了docker的典型场景: 使应用的打包与部署自动化 创建轻量.私密的PAAS环境 实现自动化测试和持续的集成/部署 根据这些特性,我们可以想象一下,如果你的项目有如下痛点或者需 ...
- Render函数详解
一.虚拟dom DOM是文档对象模型(Document Object Model)的简写,在浏览器中通过js来操作DOM的操作性能很差,于是虚拟Dom应运而生.虚拟Dom就是在js中模拟DOM对象树来 ...
- Mysql.新建数据库和用户
//建立数据库 drop database if exists 你的db名; create database 你的db名 CHARACTER SET utf8 COLLATE utf8_general ...
- 1.Java和Python的选择
我认为高级语言分为Java/c系列和其他. Java:1995年,让程序员设计一些大型分布式复杂应用. Python:1991年,面向系统管理.科研教育.等非程序员群体用的多. C系列语言:奠定了现在 ...
- 玩转Django2.0---Django笔记建站基础九(二)(Auth认证系统)
9.4 设置用户权限 用户权限主要是对不同的用户设置不同的功能使用权限,而每个功能主要以模型来划分.以9.3节的MyDjango项目为例,在Admin后台管理系统可以查看并设置用户权限,如下图: 用户 ...