https://www.cnblogs.com/my_life/articles/7016406.html
windows平台
char 表示单字符,占用一个字节
wchar_t 表示宽字符,占用两个字节
Linux平台
char 占用一个字节
wchar_t 占用四个字节
char叫多字节字符(内存),一个char占一个字节,之所以叫多字节字符是因为它表示一个字时可能是一个字节也可能是多个字节。一个英文字符(如’s’)用一个char(一个字节)表示,一个中文汉字(如’中’)用3个char(三个字节)表示
-
void TestChar()
-
{
-
char ch1 = 's'; // 正确
-
cout << "ch1:" << ch1 << endl;
-
char ch2 = '中'; // 错误,一个char不能完整存放一个汉字信息
-
cout << "ch2:" << ch2 << endl;
-
char str[4] = "中"; //前三个字节存放汉字'中',最后一个字节存放字符串结束符\0
-
cout << "str:" << str << endl;
-
//char str2[2] = "国"; // 错误:'str2' : array bounds overflow
-
//cout << str2 << endl;
-
}
wchar_t被称为宽字符,一个wchar_t占2个字节。之所以叫宽字符是因为所有的字都要用两个字节(即一个wchar_t)来表示,不管是英文还是中文。看下面的例子:
-
void TestWchar_t()
-
{
-
wcout.imbue(locale("chs")); // 将wcout的本地化语言设置为中文
-
wchar_t wch1 = L's'; // 正确
-
wcout << "wch1:" << wch1 << endl
-
wchar_t wch2 = L'中'; // 正确,一个汉字用一个wchar_t表示
-
wcout << "wch2:" << wch2 << endl;
-
wchar_t wstr[2] = L"中"; // 前两个字节(前一个wchar_t)存放汉字'中',最后两个字节(后一个wchar_t)存放字符串结束符\0
-
wcout << "wstr:" << wstr << endl;
-
wchar_t wstr2[3] = L"中国";
-
wcout << "wstr2:" << wstr2 << endl;
-
}
说明:
1. 用常量字符给wchar_t变量赋值时,前面要加L。如: wchar_t wch2 = L’中’;
2. 用常量字符串给wchar_t数组赋值时,前面要加L。如: wchar_t wstr2[3] = L”中国”;
3. 如果不加L,对于英文可以正常,但对于非英文(如中文)会出错。
string是普通的多字节版本,是基于char的,对char数组进行的一种封装。
wstring是Unicode版本,是基于wchar_t的,对wchar_t数组进行的一种封装。
string 与 wstring的相关转换:
以下的两个方法是跨平台的,可在Windows下使用,也可在Linux下使用。
#include <cstdlib> #include <string.h> #include <string> // wstring => string std::string WString2String(const std::wstring& ws) //宽字符转成多字节 { std::string strLocale = setlocale(LC_ALL, ""); const wchar_t* wchSrc = ws.c_str(); size_t nDestSize = wcstombs(NULL, wchSrc, 0) + 1; //wide charactor string to multi bytes string char *chDest = new char[nDestSize]; memset(chDest,0,nDestSize); wcstombs(chDest,wchSrc,nDestSize); std::string strResult = chDest; delete []chDest; setlocale(LC_ALL, strLocale.c_str()); return strResult; } // string => wstring std::wstring String2WString(const std::string& s) //多字节转成宽字符 { std::string strLocale = setlocale(LC_ALL, ""); const char* chSrc = s.c_str(); size_t nDestSize = mbstowcs(NULL, chSrc, 0) + 1; wchar_t* wchDest = new wchar_t[nDestSize]; wmemset(wchDest, 0, nDestSize); mbstowcs(wchDest,chSrc,nDestSize); std::wstring wstrResult = wchDest; delete []wchDest; setlocale(LC_ALL, strLocale.c_str()); return wstrResult; }
Unicode编码字符集是最通用的字符编码标准,Windows应用程序使用Unicode字符集的UTF-16实现版本。同时,Windows也支持传统的字符集:单字节字符集(Single-byte character sets, SBCS)和多字节字符集(Multibyte character sets)。
ANSI、UTF-8、Unicode为字符代码的三种编码格式,一个字符可以被编码成ANSI、UT-F8或Unicode格式,这三种格式只是表现形式不一样,其表示内容是一样的。如下表:
char | ANSI(GBK) | Unicode | UTF-8 |
---|---|---|---|
中 | 0xD6D0 | 0x4E2D | 0xE4B8AD |
ANSI编码【英文一个字节,中文两个字节,不同的国家有不同的编码规范】
ANSI表示英文字符时用一个字节,表示中文用两个字节
为了使计算机支持多种语言,不同的国家和地区制定了不同的标准,由此产生了 GB2312, BIG5, JIS 等各自的编码标准。这些使用 2 个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码。
在简体中文系统下,ANSI 编码代表 GB2312 编码,在日文操作系统下,ANSI 编码代表 JIS 编码。
对于ANSI编码而言,0x00~0x7F
之间的字符,依旧是1个字节代表一个字符(ASCII编码),而这之外的字符通常是使用0x80~0xFF
范围内的两个字节来表示一个字符。比如汉字找那个的'中'在简体中文中使用[0xD6, 0xD0]
这两个字节存储。
下表中展示了文在不同ANSI标准下的编码:
char | ANSI(GBK) | ANSI(Big5) | ANSI(JIS) | Unicode | UTF-8 |
---|---|---|---|---|---|
文 | 0xCEC4 | 0xA4E5 | 0x95B6 | 0x6587 | 0xE69687 |
可以看出,不同ANSI编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,存储在同一段 ANSI 编码的文本中。需要将不同的ANSI编码都转换成UTF-8编码,进而存储。
Unicode编码【不管中英文,全部两个字节,所有的国家都可以用,不会冲突】
Unicode字符集编码全称:Universal Multiple-Octet Coded Character Set,通用多八位编码字符集。Unicode字符集是国际组织制定的可以容纳世界上所有文字和符号的编码方案。
Unicode编码使用两个字节(0x0000-0xFFFF
)来表示一个字符,世界上任何文字和符号都对应于Unicode字符集中的一个二进制代码,但是:
Unicode只是一个符号集, 它只规定了符号的二进制代码, 却没有规定这个二进制代码应该如何存储。
Unicode编码的优点是覆盖了世界上所有的文字和符号,缺陷则是对于英文字符浪费了一个字节。例如:英文A在unicode中表示为0x0041。
UTF-8编码【可变字节存储,中文是三个字节】
UTF-8是Unicode的实现方式之一。
UTF-8全称:8bit Unicode Transformation Format,8比特Unicode通用转换格式。UTF-8是一种针对Unicode的可变长度字符编码。可以表示Unicode标准中的任何一个字符,且其编码中的第一个字节仍然与ASCII兼容。
UTF-8是一种变长的编码方式,可以使用1~6个字节对Unicode字符集进行编码,编码规则如下:
-
对于单字节的符号, 字节的第一位设为0, 后面7位为这个符号的unicode码. 因此对于
英语字母, UTF-8编码和ASCII码是相同的. -
对于n字节的符号(n>1), 第一个字节的前n位都设为1, 第n+1位设为0, 后面字节的前
两位一律设为10. 剩下的没有提及的二进制位, 全部为这个符号的unicode码.
n | Unicode符号范围 | UTF-8编码方式 |
---|---|---|
1 | 0000 0000 - 0000 007F | 0xxxxxxx |
2 | 0000 0080 - 0000 07FF | 110xxxxx 10xxxxxx |
3 | 0000 0800 - 0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
4 | 0001 0000 - 0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
5 | 0020 0000 - 03FF FFFF | 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx |
6 | 0400 0000 - 7FFF FFFF | 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx |
注:在UTF-8编码中,英文字符占一个字节,中文字符占用3个字节。
ASCII码相信很熟悉了,只是自己一直都记不住基本规律,有时候用起来十分不爽。
ASCII,共7位,表示128个字符,一般都在前面加0补全8位形成一个Bit。0~31以及127为控制字符,不能打印;32为空格;48(HEX: 0x30)为0;65(HEX: 0x41)为A;97(HEX: 0x61)为a。
GB2312:ANSI编码中的一种,对ASNI编码最初始的ASCII编码进行扩充,为了满足国内在计算机中使用汉字的需要,中国国家标准总局发布了一系列的汉子字符集国家标准编码,统称为GB码,或国标码。其中最有影响的是于1980年发布的《信息交换用汉字编码字符集 基本集》,标准号为GB 2312-1980,因其使用非常普遍,也常被通称为国标码。GB2312是一个简体中文字符集,由6763个常用汉字和682个全角的非汉字字符组成。GB2312编码用两个字节表示一个汉字,所以理论上最多可以表示256x256=65536个汉字。(摘自百度百科)
GBK:汉字内码扩展规范,K为扩展的汉语拼音中“扩”字的声母。GBK兼容GB2312,共收录汉字21003个、符号883个,并提供1894个造字码位。(摘自百度百科)
Unicode:把世界上所有的符号都纳入其中,包括英文、日本、中文等等,现在能容纳100多万个符号。这样效率上就不好,于是UTF-8出现了,它可以根据不同的符号自动选择编码的长短。
========================
字符到整数
char是一种整数类型,这句话的含义是,char所能表示的字符在C/C++中都是整数类型。好,接下来,很多文章就会举出一个典型例子,比如,'a'的数值就是0x61。这种说法对吗?如果你细心的读过K&R和BS对于C和C++描述的原著,你就会马上反驳道,0x61只是'a'的ASCII值,并没有任何规定C/C++的char值必须对应ASCII。C/C++甚至没有规定char占几位,只是规定了sizeof(char)等于1。
当然,目前大部分情况下,char是8位的,并且,在ASCII范围内的值,与ASCII对应。
本地化策略集(locale)
“将'a'翻译成0x61的整数值”,“将ASCII范围内的编码与char的整数值对应起来”,类似这样的规定,是特定系统和特定编译器制定的,C/C++中有个特定的名词来描述这种规定的集合:本地化策略集(locale。也有翻译成“现场”)。而翻译——也就是代码转换(codecvt)只是这个集合中的一个,C++中定义为策略(facet。也有翻译为“刻面”)
C/C++的编译策略
“本地化策略集”是个很好的概念,可惜在字符和字符串这个层面上,C/C++并不使用(C++的locale通常只是影响流(stream)),C/C++使用更直接简单的策略:硬编码。
简单的说,字符(串)在程序文件(可执行文件,非源文件)中的表示,与在程序执行中在内存中的表示一致。考虑两种情况:
A、char c = 0x61;
B、char c = 'a';
情况A下,编译器可以直接认识作为整数的c,但是在情况B下,编译器必须将'a'翻译成整数。编译器的策略也很简单,就是直接读取字符(串)在源文件中的编码数值。比如:
const char* s = "中文abc";
这段字符串在GB2312(Windows 936),也就是我们的windows默认中文系统源文件中的编码为:
0xD6 0xD0 0xCE 0xC4 0x61 0x62 0x63
在UTF-8,也就是Linux默认系统源文件中的编码为:
0xE4 0xB8 0xAD 0xE6 0x96 0x87 0x61 0x62 0x63
一般情况下,编译器会忠实于源文件的编码,为s赋值,例外的情况比如VC会自作聪明的把大部分其他类型编码的字符串转换成GB2312(除了像UTF-8 without signature这样的幸存者)。
程序在执行的时候,s也就保持是这样的编码,不会再做其他的转换。
- 宽字符 wchar_t
正如char没有规定大小,wchar_t同样没有标准限定,标准只是要求一个wchar_t可以表示任何系统所能认识的字符,在win32中,wchar_t为16位;Linux中是32位。wchar_t同样没有规定编码,因为Unicode的概念我们后面才解释,所以这里只是提一下,
在win32中,wchar_t的编码是UCS-2BE;而Linux中是UTF-32BE(等价于UCS-4BE),不过简单的说,在16位以内,一个字符的这3种编码值是一样的。因此:
const wchar_t* ws = L"中文abc";
的编码分别为:
0x4E2D 0x6587 0x0061 0x0062 0x0063 //win32,16位
0x00004E2D 0x00006587 0x00000061 0x00000062 0x00000063 //Linux,32位
大写的L是告诉编译器:这是宽字符串。所以,这时候是需要编译器根据locale来进行翻译的。
比如,在Windows环境中,编译器的翻译策略是GB2312到UCS-2BE;Linux环境中的策略是UTF-8到UTF-32BE。
这时候就要求源文件的编码与编译器的本地化策略集中代码翻译的策略一致,例如VC只能读取GB2312的源代码(这里还是例外,VC太自作聪明了 ,会将很多其他代码在编译时自动转换成GB2312),
而gcc只能读取UTF-8的源代码(这里就有个尴尬,MinGW运行win32下,所以只有GB2312系统才认;而MinGW却用gcc编写,所以自己只认UTF-8,所以结果就是,MinGW的宽字符被废掉了)。
宽字符(串)由编译器翻译,还是被硬编码进程序文件中。
c++ 为什么叫’wchar_t’而不是简单的’wchar’?
这是C的遗产,其中wchar_t是一个typedef,typedef在C标准库中具有该后缀。
https://blog.csdn.net/leitianjun/article/details/5605410
在本文开始之处,先简要地说一下何为短字符和宽字符.
所谓的短字符,就是用8bit来表示的字符,典型的应用是ASCII码.
而宽字符,顾名思义,就是用16bit表示的字符,典型的有UNICODE.
关于windows下的ASCII和UNICODE的更多信息,可以参考这两本经典著作:《windows 程序设计》,《windows 核心编程》.这两本书关于这两种字符都有比较详细的解说.
宽字符转换为多个短字符是一个难点,不过我们只要掌握到其中的要领,便可如鱼得水. 好吧,那就让我们开始吧.
这个是我们需要转化的多字节字符串: //多/短字节字符转换成宽字符
char sText[20] = {"多字节字符串!OK!"};
我们需要知道转化后的宽字符需要多少个数组空间.虽然在这个里程里面,我们可以直接定义一个20*2宽字符的数组,并且事实上将运行得非常轻松愉快.
但假如多字节字符串更多,达到上千个乃至上万个,我们将会发现其中浪费的内存将会越来越多.所以以多字节字符的个数的两倍作为宽字符数组下标的声明绝对不是一个好主意.
我们只需要将MultiByteToWideChar()的第四个形参设为-1,即可返回所需的短宽字符数组空间的个数:
DWORD dwNum = MultiByteToWideChar (CP_ACP, 0, sText, -1, NULL, 0);
接下来,我们只需要分配响应的数组空间:
wchar_t *pwText;
pwText = new wchar_t[dwNum];
if(!pwText)
{
delete []pwText;
}
接着,我们就可以着手进行转换了.在这里以转换成ASCII码做为例子:
MultiByteToWideChar (CP_ACP, 0, psText, -1, sText, dwSize);
最后,使用完毕当然要记得释放占用的内存:
delete []psText;
同理,宽字符转为多字节字符的代码如下:
wchar_t wText[20] = {L"宽字符转换实例!OK!"};
DWORD dwNum = WideCharToMultiByte(CP_OEMCP,NULL,lpcwszStr,-1,NULL,0,NULL,FALSE);
char *psText;
psText = new char[dwNum];
if(!psText)
{
delete []psText;
}
WideCharToMultiByte (CP_OEMCP,NULL,lpcwszStr,-1,psText,dwNum,NULL,FALSE);
delete []psText;
如果之前我们已经分配好空间,并且由于字符串较短,可以不理会浪费的空间,仅仅只是想简单地将短字符和宽字符相互转换,那有没有
UTF-8
现在明白了Unicode,那么UTF-8又是什么呢?又为什么会出现UTF-8呢?ASCII转换成UCS-2,只是在编码前插入一个0x0。用这些编码,会包括一些控制符,比如 '' 或 '/',这在UNIX和一些C函数中,将会产生
因此,才诞生了UTF-8。那么UTF-8是如何编码的?又是如何解决UCS-2的问题呢?
例:
E4 BD A0 11100100 10111101 10100000
这是“你”字的UTF-8编码
4F 60 01001111 01100000
这是“你”的Unicode编码
按照UTF-8的编码规则,分解如下:xxxx0100 xx111101 xx100000
把除了x之外的数字拼接在一起,就变成“你”的Unicode编码了。
注意UTF-8的最前面3个1,表示整个UTF-8串是由3个字节构成的。
经过UTF-8编码之后,再也不会出现敏感字符了,因为最高位始终为1。
以下是Unicode和UTF-8之间的转换关系表:
U-00000000 - U-0000007F: 0xxxxxxx //没有1表示只有1个字节
U-00000080 - U-000007FF: 110xxxxx 10xxxxxx //前面2个1表示由2个字节
U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx //前面3个1表示由3个字节
U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx //依次类推
U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
Unicode编码转换到UTF-8,简单的把Unicode字节流套到x中就变成UTF-8了。
所以,可以看到unicode编码和utf-8编码有线性转换关系,而unicode编码和gb2312编码不存在线性转换关系,所以我们必须使用对照表
·获取所需的编码转换表
·实现码表的快速搜索算法(UTF-8转GB码才需要,其实就是折半查找)
·待转换字符串中的中/西文字符判别
由于折半查找要求码表是事先排序的,正变换和反变换需要各有一张转换表。转换表可以从开源软件中获取也可以自己编段程序生成一份。
由于非unicode编码字串中的西文字母只有一字节/字符,而汉字是2字节/字符,需要在转换时区别对待。判断方法在本文的前面部分有介绍。
由GB2312码转unicode时,由于转换表是按区位表排列的,可以直接由汉字的GB码通过计算得到转换表中的行列值,计算公式为:
Row = MSB - 0xA0 - 16
Col = LSB – 0xA0
由于转换表是从汉字区开始的,即第一个汉字是“啊”,开始行不是0,而是16,所以要从行值中减去一个偏移量。得到行列值后,可以直
Unicode = CODE_LUT[Row][Col];
今天对网上找到的转换表不太满意,于是自己编程序生成了一个新的。转换程序不大,
在LINUX上进行编码转换时,既可以利用iconv函数族编程实现,也可以利用iconv命令来实现,只不过后者是针对文件的,即将指定文件从
一、利用iconv函数族进行编码转换
iconv函数族的头文件是iconv.h,使用前需包含之。
#include <iconv.h>
iconv函数族有三个函数,原型如下:
(1) iconv_t iconv_open(const char *tocode, const char *fromcode);
此函数说明将要进行哪两种编码的转换,tocode是目标编码,fromcode是原编码,该函数返回一个转换句柄,供以下两个函数使用。
(2) size_t iconv(iconv_t cd,char **inbuf,size_t *inbytesleft,char **outbuf,size_t *outbytesleft);
此函数从inbuf中读取字符,转换后输出到outbuf中,inbytesleft用以记录还未转换的字符数,outbytesleft用以记录输出缓冲的剩余空间。
(3) int iconv_close(iconv_t cd);
此函数用于关闭转换句柄,释放资源。
例子1: 用C语言实现的转换示例程序
/* f.c : 代码转换示例C程序 */具体讲,自己的验证实现是根据文中的f.c实现的
#include <iconv.h>
#define OUTLEN 255
main()
{
char *in_utf8 = "姝e?ㄥ??瑁?";<=======此字符串似乎并不是“正在安装”四个字的UTF8的对照串,
char *in_gb2312 = "正在安装";
char out[OUTLEN];
//unicode码转为gb2312码
rc = u2g(in_utf8,strlen(in_utf8),out,OUTLEN);
printf("unicode-->gb2312 out=%sn",out);
//gb2312码转为unicode码
rc = g2u(in_gb2312,strlen(in_gb2312),out,OUTLEN);
printf("gb2312-->unicode out=%sn",out);
}
//代码转换:从一种编码转为另一种编码
int code_convert(char *from_charset,char *to_charset,char *inbuf,int inlen,char *outbuf,int outlen)
{
iconv_t cd;
int rc;
char **pin = &inbuf;
char **pout = &outbuf;
cd = iconv_open(to_charset,from_charset);
if (cd==0) return -1;
memset(outbuf,0,outlen);
if (iconv(cd,pin,&inlen,pout,&outlen)==-1) return -1;
iconv_close(cd);
return 0;
}
//UNICODE码转为GB2312码
int u2g(char *inbuf,int inlen,char *outbuf,int outlen)
{
return code_convert("utf-8","gb2312",inbuf,inlen,outbuf,outlen); //gb2312属于ANSI中的中文编码
}
//GB2312码转为UNICODE码
int g2u(char *inbuf,size_t inlen,char *outbuf,size_t outlen)
{
return code_convert("gb2312","utf-8",inbuf,inlen,outbuf,outlen);
}
例子2: 用C++语言实现的转换示例程序
/* f.cpp : 代码转换示例C++程序 */
#include <iconv.h>
#include <iostream>
#define OUTLEN 255
using namespace std;
// 代码转换操作类
class CodeConverter {
private:
iconv_t cd;
public:
// 构造
CodeConverter(const char *from_charset,const char *to_charset) {
cd = iconv_open(to_charset,from_charset);
}
// 析构
~CodeConverter() {
iconv_close(cd);
}
// 转换输出
int convert(char *inbuf,int inlen,char *outbuf,int outlen) {
char **pin = &inbuf;
char **pout = &outbuf;
memset(outbuf,0,outlen);
return iconv(cd,pin,(size_t *)&inlen,pout,(size_t *)&outlen);
}
};
int main(int argc, char **argv)
{
char *in_utf8 = "姝e?ㄥ??瑁?";
char *in_gb2312 = "正在安装";
char out[OUTLEN];
// utf-8-->gb2312
CodeConverter cc = CodeConverter("utf-8","gb2312");
cc.convert(in_utf8,strlen(in_utf8),out,OUTLEN);
cout << "utf-8-->gb2312 in=" << in_utf8 << ",out=" << out << endl;
// gb2312-->utf-8
CodeConverter cc2 = CodeConverter("gb2312","utf-8");
cc2.convert(in_gb2312,strlen(in_gb2312),out,OUTLEN);
cout << "gb2312-->utf-8 in=" << in_gb2312 << ",out=" << out << endl;
}
二、利用iconv命令进行编码转换
iconv命令用于转换指定文件的编码,默认输出到标准输出设备,亦可指定输出文件。
用法: iconv [选项...] [文件...]
有如下选项可用:
输入/输出格式规范:
-f, --from-code=名称 原始文本编码
-t, --to-code=名称 输出编码
信息:
-l, --list 列举所有已知的字符集
输出控制:
-c 从输出中忽略无效的字符
-o, --output=FILE 输出文件
-s, --silent 关闭警告
--verbose 打印进度信息
-?, --help 给出该系统求助列表
--usage 给出简要的用法信息
-V, --version 打印程序版本号
例子:
iconv -f utf-8 -t gb2312 aaa.txt >bbb.txt
这个命令读取aaa.txt文件,从utf-8编码转换为gb2312编码,其输出定向到bbb.txt文件。
使用iconv进行内码转换(Big5-GB2312)
http://www.freebsd.org/ports/converters.html
概述
iconv是一个通过unicode作为中间码实现各种内码间相互转换的库,它基本上囊括了世界上所有编码方式,例如,ASCII、GB2312、
下载
libiconv是linux版本的iconv,可在 http://www.gnu.org/software/libiconv/ 下载
iconv的win32版本可以在 http://gnuwin32.sourceforge.net/packages/libiconv.htm 下载
SVN源码
另外,还有一些演示代码,需要的可以到我的SVN下载
http://xcyber.googlecode.com/svn/trunk/Convert/
演示代码
1. /****************************************************************************
2. * Big5ToGB2312 - Convert Big5 encoding file to GB2312 encoding file
3. * File:
4. * Big5ToGb2312.c
5. * Description:
6. * Convert Big5 encoding file to GB2312 encoding file using iconv library
7. * Author:
8. * XCyber email:XCyber@sohu.com
9. * Date:
10. * August 7, 2008
11. * Other:
12. * visit http://www.gnu.org/software/libiconv/ for more help of iconv
13. ***************************************************************************/
14.
15.
16. #include <stdio.h>
17. #include <stdlib.h>
18. #include <tchar.h>
19. #include <locale.h>
20. #include "../iconv-1.9.2.win32/include/iconv.h"
21.
22. //#pragma comment(lib, "../iconv-1.9.2.win32/lib/iconv.lib") // using iconv dynamic-link lib, iconv.dll
23. #pragma comment(lib, "../iconv-1.9.2.win32/lib/iconv_a.lib") // using iconv static lib
24.
25. #define BUFFER_SIZE 1024 //BUFFER_SIZE must >= 2
26.
27.
28. void usage()
29. {
30. printf("/nBig5ToGB2312 - Convert Big5 encoding file to GB2312 encoding file/n");
31. printf("XCyber@sohu.com on August 7, 2008/n");
32. printf(" Usage:/n");
33. printf(" Big5ToGB2312 [Big5 file(in)] [GB2312 file(out)]/n/n");
34. }
35.
36.
37. int main(int argc, char* argv[])
38. {
39. FILE * pSrcFile = NULL;
40. FILE * pDstFile = NULL;
41.
42. char szSrcBuf[BUFFER_SIZE];
43. char szDstBuf[BUFFER_SIZE];
44.
45. size_t nSrc = 0;
46. size_t nDst = 0;
47. size_t nRead = 0;
48. size_t nRet = 0;
49.
50. char *pSrcBuf = szSrcBuf;
51. char *pDstBuf = szDstBuf;
52.
53. iconv_t icv;
54. int argument = 1;
55.
56. //check input arguments
57. if(argc != 3)
58. {
59. usage();
60. return -1;
61. }
62.
63.
64. pSrcFile = fopen(argv[1],"r");
65. if(pSrcFile == NULL)
66. {
67. printf("can't open source file!/n");
68. return -1;
69. }
70.
71. pDstFile = fopen(argv[2],"w");
72. if(pSrcFile == NULL)
73. {
74. printf("can't open destination file!/n");
75. return -1;
76. }
77.
78. //initialize iconv routine, perform conversion from BIG5 to GB2312
79. //TODO: if you want to perfom other type of coversion, e.g. GB2312->BIG5, GB2312->UTF-8 ...
80. //just change following two paremeters of iconv_open()
81. icv = iconv_open("GB2312","BIG5");
82. if(icv == 0)
83. {
84. printf("can't initalize iconv routine!/n");
85. return -1;
86. }
87.
88. //enable "illegal sequence discard and continue" feature, so that if met illeagal sequence,
89. //conversion will continue instead of being terminated
90. if(iconvctl (icv ,ICONV_SET_DISCARD_ILSEQ,&argument) != 0)
91. {
92. printf("can't enable /"illegal sequence discard and continue/" feature!/n");
93. return -1;
94. }
95.
96. while(!feof(pSrcFile))
97. {
98. pSrcBuf = szSrcBuf;
99. pDstBuf = szDstBuf;
100. nDst = BUFFER_SIZE;
101.
102. // read data from source file
103. nRead = fread(szSrcBuf + nSrc,sizeof(char),BUFFER_SIZE - nSrc,pSrcFile);
104. if(nRead == 0)
105. break;
106.
107. // the amount of data to be converted should include previous left data and current read data
108. nSrc = nSrc + nRead;
109.
110. //perform conversion
111. nRet = iconv(icv,(const char**)&pSrcBuf,&nSrc,&pDstBuf,&nDst);
112.
113. if(nRet == -1)
114. {
115. // include all case of errno: E2BIG, EILSEQ, EINVAL
116. // E2BIG: There is not sufficient room at *outbuf.
117. // EILSEQ: An invalid multibyte sequence has been encountered in the input.
118. // EINVAL: An incomplete multibyte sequence has been encountered in the input
119. // move the left data to the head of szSrcBuf in other to link it with the next data block
120. memmove(szSrcBuf,pSrcBuf,nSrc);
121. }
122.
123. //wirte data to destination file
124. fwrite(szDstBuf,sizeof(char),BUFFER_SIZE - nDst,pDstFile);
125.
126. }
127. iconv_close(icv);
128. fclose(pSrcFile);
129. fclose(pDstFile);
130.
131. printf("conversion complete./n");
132.
133. return 0;
134. }
#include <stdio.h>
#include <stdlib.h>
#include <iconv.h>
int main(void)
{
unsigned char *src = "魅影追击和歌姬"; /* 需转换的字串 */
unsigned char dst[256] = {0}; /* 转换后的内容 */
unsigned char buf[1024] = {0}; /* 格式化转换后的字串 */
size_t src_len = strlen(src);
size_t dst_len = sizeof(dst);
unsigned char *in = src;
unsigned char *out = dst;
iconv_t cd;
int i;
int j;
cd = iconv_open("UTF-8", "GB2312"); /* 将GB2312字符集转换为UTF-8字符集 */
if ((iconv_t)-1 == cd)
{
return -1;
}
printf("src: %s/n", src);
iconv(cd, &in, &src_len, &out, &dst_len); /* 执行转换 */
/* 以下将转换后的内容格式化为: %XX%XX...形式的字串 */
printf("dst: ");
j = 0;
for (i = 0; i < strlen(dst); i++)
{
printf("%.2X ", dst[i]);
buf[j++] = ''%'';
snprintf(buf + j, 3, "%.2X", dst[i]);
j += 2;
}
printf("/n");
printf("buf: %s/n", buf);
iconv_close(cd); /* 执行清理 */
return 0;
}
1.11.1版本是最后一个支持MSVC编译的版本,1.12及之后的版本只支持MingW和Cygwin编译,下面是我用vs2008编译iconv的过程
1.下载1.11版本的libiconv
2.在srclib/progname.h文件中添加一行:
#define EXEEXT ".exe"
3.将srclib/stdint_.h更名为srclib/stdint.h,并将'@'符号全部移除
4.对srclib/Makefile.msvc进行以下改动:
1) 在OBJS=的定义中添加 width.obj
2) 添加如下定义:
width.obj : width.c
$(CC) $(INCLUDES) $(CFLAGS) -c width.c
5.调用以下命令编译DLL或LIB
nmake -f Makefile.msvc NO_NLS=1 DLL=1 MFLAGS=-MD PREFIX="c:/lib_x86" IIPREFIX="c:/lib_x86"
nmake -f Makefile.msvc NO_NLS=1 DLL=1 MFLAGS=-MD install PREFIX="c:/lib_x86" IIPREFIX="c:/lib_x86"
或
nmake -f Makefile.msvc NO_NLS=1 MFLAGS=-MD PREFIX="c:/slib_x86" IIPREFIX="c:/slib_x86"
nmake -f Makefile.msvc NO_NLS=1 MFLAGS=-MD install PREFIX="c:/slib_x86" IIPREFIX="c:/slib_x86"
PREFIX 和 IIPREFIX中的路径,必须用绝对路径
6.编译完后,程序在./lib_x86目录下
编译完成!
https://blog.csdn.net/xiayuleWA/article/details/32140493
首先给个验证均无问题的c++模板:验证环境windows 7 + vs2012; ubuntu 12.04 + g++(4.6.3); 当然也适用c语言
此c++模板编码最好为utf-8
// 注意,此文档最好采用utf-8编码
#include <stdio.h>
#include <wchar.h>
#include <locale.h>
int main()
{
//此语句重要,在win7 + vs2012和 ubuntu 12.04测试结果一致
//只要打印wchar_t字符,均加此语句,至少不会出错,此语句最好在程序初始化处
setlocale(LC_ALL, "");
// wprintf和printf最好不能同时使用.
// 如下使用printf同时打印了char字符串和wchar_t字符串
// 因此只采用printf是比较好的方法
wchar_t ws[] = L"国家";
printf("printf content start: %ls\n", ws);
printf("content end\n");
return 0;
}
用上面代码的原因:
1. 从后面我的实验可以看到, 有八种组合方式, 唯独选择这么一种是有原因的.
2. wprintf 和 printf 通用会遇到莫名其妙的问题,
3 . printf可以打印char, 使用格式为小写%s, 也可以打印wchar_t, 使用格式为%ls 或者 大写%S
4. 只要源文件编码为utf-8, 上面的代码就是跨操作系统和编译器平台的, 并且可以统一用一种方式打印char 和 wchar_t 字符串
得出上面结论的原因见下面:
下面为比较细节的讨论。
关于编码字符集:简单理解为,ascii码表达了美国英文字符,为一字节.
为了统一世界文字编码,出了unicode,如ucs-2,ucs-4,分别为16字节和32字节
因为历史包袱,unicode不可能完全替代以前的编码,所以出了有使用性的UTF-8编码,字节大小不定。
如何快速查看一个文字编码?如“国“字的utf-8编码是什么? 简单方法是新建一文件,记事本输入“国”, 记事本另存为,选择文件编码。 然后使用如下方式打开此文件,1:使用winhex软件 2. 使用notep++,选中文字”国“,点击菜单插件->converter->ascii to hex, 3, 其它.... (在线的就算了,我看了百度两个排名前两位的网站根本不是转的UTF-8, 而是转成了ucs-2,虽然其号称是转换成UTF-8)
如下表格是使用wchar_t的总结,其中编码格式设置是通过notepad++的格式菜单进行的,如下表格的测试代码后面会贴出来。
表格解释:
1.采用ansi编码方式,在ubuntu下会出错,另外通过g++ -Wall -finput-charset=ISO-8859-1 test.cpp 方式可以指定输入文件格式,但是这样很麻烦,因为首先要知道文件的格式是什么,在ubuntu下 通过file test.cpp 命令可以查到,我把ISO-8859-1到ISO-8859-16都试了,文件编码格式ANSI(即ISO8859-XX)且文件中有L"国家" 类似字符,编译不会通过,或者通过-finput-charset=xxx指定文件格式编译通过后输出也不对,故太麻烦。
2.在windows下和ubuntu下都通过的方式是文件编码采用UTF-8。
3.printf和wprintf不能混用,即一个程序中使用了printf, 就不使用wprintf,反之也是,既然printf输出char 和 wchar_t字符都可以,所以统一使用printf是最佳选择。
当然使用printf打印wchar_t时格式不一样,是%ls 或大写的%S, 例如: printf("wide char: %ls\n", ws);
4. 一旦要打印中文字符,在程序初始化时加一句setlocale(LC_ALL, ""); 否则打印中文会出错。
5. windows 对 字符串的内部编码比较统一,char的编码都成ANSI,如 char s[] = "国家", s里存的是”国家”的ANSI编码
wchar_t的编码都成UCS-2,如 wchar_t ws[] = L"国家", s里存的是”国家”的UCS-2编码
UBUNTU 对字符串的处理不是很统一,char 字符串的编码是随着文件编码格式有所变化,如文件为ansi,则ubuntu也为ansi,文件为UTF-8, 则char s[] = "国家" 里的也是 UTF-8编码。
wchar_t 则默认采用ucs-4编码。
总结:源代码文件编码用UTF-8, 打印采用printf是跨平台(操作系统,编译器), 跨char和wchar_t的最佳解决方案之一,另外不能忘了 setlocale(LC_ALL, "");
说的再多,不如自己测试下,如下为测试代码,一共有5种case,每次注释掉其他case,留要测试的case,并按照case注释处更改文件编码方式便可, 更改编码方式采用notepad++最方便了.
#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include <wchar.h>
#include <locale.h>
// 测试运行环境:
// ubuntu 12.04 + g++ 4.6.3(或gcc,改文件后缀就行)
// windows 7 + vs2012
// 说明,在注释中说的编码E59BBD,是按照打印结果从低字节到高字节排练,并不一定和实际
// 的编码大小端一致。
int main()
{
//此语句重要,在win7 + vs2012和 ubuntu 12.04测试结果一致,只要打印wchar_t字符,均加此语句,否则出错。
setlocale(LC_ALL, "");
/****************** case 1 **********************************/
// 文件utf-8编码, 字符串不加L
// ubuntu : ws是utf-8编码: 如"国":E59BBD; 打印结果:国家
// windows: ws是ansi编码: 如"国":b9fa; 打印结果: 国家
/* char ws[] = "国家";
char *p = (char *)ws;
int i = 0;
printf("sizeof(ws) is %d\n", sizeof(ws));
for (; i < sizeof(ws); i++){
printf("byte: %x\n", p[i]);
}
printf("content start\n");
printf("%s\n", ws);
printf("content end\n"); */
/****************** case 2 **********************************/
// 2: 文件ansi编码, 字符串不加L
// ubuntu : ws是ansi编码,如"国":b9fa; 打印结果:无,出错
// windows: ws是ansi编码: 如"国":b9fa; 打印结果: 国家
/* char ws[] = "国家";
char *p = (char *)ws;
int i = 0;
//setlocale(LC_ALL, "zh_CN.UTF-8");
printf("sizeof(ws) is %d\n", sizeof(ws));
for (; i < sizeof(ws); i++){
printf("byte: %x\n", p[i]);
}
printf("content start: %s\n", ws);
printf("content end\n"); */
/****************** case 3 **********************************/
// 3: 文件utf-8编码, 字符串加L, 必须采用wchar_t, 用char编译器报错, 用wprintf打印
// ubuntu : ws是ucs-4编码,如"国":0x000056fd; 打印结果:国家
// windows: ws是ucs-2编码: 如"国":0x56fd; 打印结果: 国家
/* wchar_t ws[] = L"国家";
char *p = (char *)ws;
int i = 0;
wprintf(L"sizeof(ws) is %d\n", sizeof(ws));
for (; i < sizeof(ws); i++){
wprintf(L"byte: %x\n", p[i]);
}
wprintf(L"wprintf content start:\n"); //必须用wprintf, 且wprintf和printf不能同时使用.
wprintf(L"%ls\n", ws);
wprintf(L"content end\n"); */
/****************** case 4 **********************************/
// 4: 文件utf-8编码, 字符串加L, 必须采用wchar_t, 用char编译器报错, 用printf打印
// ubuntu : ws是ucs-4编码,如"国":0x000056fd; 打印结果:国家
// windows: ws是ucs-2编码: 如"国":0x56fd; 打印结果: 国家
wchar_t ws[] = L"国家";
char *p = (char *)ws;
int i = 0;
printf("sizeof(ws) is %d\n", sizeof(ws));
for (; i < sizeof(ws); i++){
printf("byte: %x\n", p[i]);
}
printf("printf content start:\n"); //wprintf和printf不能同时使用.
printf("%ls\n", ws);
printf("content end\n");
/****************** case 5 **********************************/
// 5: 文件ansi编码, 字符串加L, 必须采用wchar_t, 用char编译器报错
// ubuntu : 编译不通过
// windows: ws是ucs-2编码: 如"国":0x56fd; 打印结果: 国家
/* wchar_t ws[] = L"国家"; // 文件ansi编码,ws是unicode编码: 如国:56fd
char *p = (char *)ws;
int i = 0;
wprintf(L"sizeof(ws) is %d\n", sizeof(ws));
for (; i < sizeof(ws); i++){
wprintf(L"byte: %x\n", p[i]);
}
wprintf(L"wprintf content start:\n"); //必须用wprintf, 且wprintf和printf不能同时使用.
wprintf(L"%ls\n", ws);
wprintf(L"content end\n"); */
while(1); //方便看结果
return 0;
}
---------------------
作者:xiayulewa
来源:CSDN
原文:https://blog.csdn.net/xiayuleWA/article/details/32140493
版权声明:本文为博主原创文章,转载请附上博文链接!
http://www.voidcn.com/article/p-ufzbkkqt-ht.html
C++ wchar_t 的中文输出问题
1.char和wchar_t
众所周知,在C++中,包括char和wchar_t这两种内置类型,主要用来表示单一的字符。其中char类型一般占用一个字节,而wchar_t类型从字面上理解,可以看出它表示宽字符,也就是其占用空间要比char类型更大。当然,内置类型的长度在C++标准中有着最小值的规定,但具体在某台机器上或者某个程序中占用的空间则取决于三个因素:
1.编译器字长
2.操作系统字长
3.机器字长(cpu字长)
因此,不能对于不同环境中的内置类型字长一概而论,具体长度可以调用函数sizeof()获取。例如在32位微机上,使用ubuntu32位系统和GNU编译器,下列两个表式正确:
sizeof(char) == 1 sizeof(wchar_t) == 4
在C++中同时存在char和wchar_t,很明显是为了解决英语以外的其他语言的字符处理问题。因为1字节包含8比特,即其只能表示2^8=256个不同的值,这对于英语这样的只需26个字母就可以完全表示的语言来说是够用的,但对于汉语,日语等字符集较为庞大的语言就不适用了。而使用wchar_t可以表示2^32个不同值,显然更加合适。
2.wchar_t的中文显示问题
既然wchar_t是用来解决中文等文字的字符处理问题,而最基本的字符处理问题就是IO问题,因此可以从IO问题入手初窥wchar_t对于中文字符的处理过程来获得一个大概的印象。我们使用wcin读取窗口中的中文字符输入,之后在标准宽字符输出中打印。
#include <iostream>
#include <locale>
//using namespace std;
//this main function deals with wide character and Chinese character
int main(int argc, char **argv)
{
std::ios_base::sync_with_stdio(false); //garantee that iostreams are not synchronized with stdin or stdout
std::locale loc("zh_CN.utf8"); //set the character set to zh_CN.utf8
std::wcin.imbue(loc); //set the locale of wcin and wcout
std::wcout.imbue(loc);
wchar_t c;
std::wcin >> c; //read the input
std::wcout << c << std::endl; //output
return 0;
}
在这段程序中需要注意: 1.必须使用wcin和wcout对宽字符进行输入和输出,wcin和wcout是cin和cout的宽字符版本,也属于标准库的一部分。 2.main函数首句是标准库中ios_base类的方法,将iostream与C语言中的stdin/stdout的同步关系取消,否则wcout不能正常输出中文。 3.第二句使用std空间中的locale函数设置所用的字符集,在linux环境中的可用字符集使用locale -a获取。 4.三四两句将该字符集应用于wcin和wcout流中。 5.很多文章中提到的locale loc("chs")方法并不适用于linux系统,windows下是否可行本文没有测试。
=======================
一般常用的方法是 C 标准库的 wcstombs、mbstowcs 函数,和 windows API 的 MultiByteToWideChar 与 WideCharToMultiByte 函数来完成向 Unicode 的转入和转出。
这里以 MBs2WCs 函数的实现说明 GB2312 向 Unicode 的转换的主要过程:
清单 1. 多字节字符串向宽字节字符串转换
wchar_t * MBs2WCs(const char* pszSrc){ wchar_t* pwcs = NULL; intsize = 0; #ifdefined(_linux_) setlocale(LC_ALL, "zh_CN.GB2312"); size = mbstowcs(NULL,pszSrc,0); pwcs = new wchar_t[size+1]; size = mbstowcs(pwcs, pszSrc, size+1); pwcs[size] = 0; #else size = MultiByteToWideChar(20936, 0, pszSrc, -1, 0, 0); if(size <= 0) returnNULL; pwcs = new wchar_t[size]; MultiByteToWideChar(20936, 0, pszSrc, -1, pwcs, size); #endif returnpwcs; }
相应的,WCs2MBs 可以将宽字符串转化为字节流。
char* WCs2MBs(const wchar_t * wcharStr){ char* str = NULL; intsize = 0; #ifdefined(_linux_) setlocale(LC_ALL, "zh_CN.UTF8"); size = wcstombs( NULL, wcharStr, 0); str = new char[size + 1]; wcstombs( str, wcharStr, size); str[size] = '\0'; #else size = WideCharToMultiByte( CP_UTF8, 0, wcharStr, -1, NULL, NULL, NULL, NULL ); str = new char[size]; WideCharToMultiByte( CP_UTF8, 0, wcharStr, -1, str, size, NULL, NULL ); #endif returnstr; }
这里需要特别指出的是 setlocale 的第二个参数,Linux 和 Windows 是不同的:
- 1. 笔者在 Eclipse CDT + MinGW 下使用 [country].[charset](如 zh_CN.gb2312 或 zh_CN.UTF8)的格式并不能通过编码转换测试,但可以使用 Code Page,即可以写成 setlocale(LC_ALL, ".20936") 这样的代码。这说明,这个参数与编译器无关,而与系统定义有关,而不同操作系统对于已安装字符集的定义是不同的。
- 2. Linux 系统下可以参见 /usr/lib/locale/ 路径,系统所支持的 locale 都在这里。转换成 UTF8 时,并不需要 [country] 部分一定是 zh_CN,en_US.UTF8 也可以正常转换。
另外,标准 C 和 Win32 API 函数返回值是不同的,标准 C 返回的 wchar_t 数组或者是 char 数组都没有字符串结束符,需要手动赋值,所以 Linux 部分的代码要有区别对待。
======
如果程序需要处理中文,在main开始时:
setlocale(LC_ALL,"zh_CN.UTF-8");=
,对于网络返回的中文,例如返回的xml,头部会有
<?xml version="1.0" encoding="gb2312"?>
<optimization>
<supernode count="2">
在c++的处理程序中需要做
gb2312toutf8((char*)xml_text.c_str(), xml_text.length(), szUtf8, sizeof(szUtf8));
iconv_open("utf-8","gb2312")
的转换,然后就可以直接在程序里进行 if (str == "中文")这样的判断了。
中文字符串相等判断,看你的中文字符串使用的编码,
ANSI 编码使用 strcmp
Unicode 编码使用 wcscmp
==========
https://blog.csdn.net/jcjc918/article/details/52200478
UTF-8 介绍
首先,我们可以假定我们接受到的字符串是 UTF-8 编码的。如果在本地的话可以通过本地环境配置来保证。命令行下运行 locale 命令,LC_CTYPE 应该是 UTF-8 的。vim 打开文件敲下 :set 命令,应该有一行是 fileencoding=utf-8。这样我们就有了工作的基础。
UTF-8 是对 Unicode 字符集的实现,它是一种变长编码,对于一个Unicode 的字符编码成 1 至 4 个字节。我们可以认为,在 UTF-8 中,英文是 1 个字节,中文是 3 个字节。
UTF-8 的详细介绍可以看:
Unicode 和 UTF-8 有何区别? — 知乎
UTF-8 — 维基百科
设计思路
既然知道 UTF-8 的中英文字符字节长度,那我们可能想用这样一个方案:遍历字符串,判断当前字节属于中文还是英文,如果英文则对长度加一并从下一个字节继续处理,如果是中文则对长度加一并跳到后面第三个字节继续处理。达到我们需要的文案长度时,break 跳出循环,返回当前遍历得到的子字符串。
但是这样的实现会感觉很 hack,有点暴力,程序容易写出问题。而且我们前面的假设毕竟是一般情况下(虽然概率很低),如果出现一个四字节的字符那程序会错得一塌糊涂。
如果有一种编码或数据类型,每个中英文字符都占据相同长度,那我们的处理就会简单多了。这时候我们想到了 C++ 的 wstring 类型,wstring 的 size() 函数返回的就是包含的中英文字符个数。wstring 与 string 一样都是基于 basic_string 类模板,不同的是 string 使用 char 为基本类型,而 wstring 是 wchat_t。wchar_t 可以支持 Unicode 字符的存储,在 Win 下是两个字节, Linux 的实现则是四个字节,可以直接用 sizeof(wchar_t) 查看类型长度。
到这里我们已经有了基本的思路:实现 string 和 wstring 的互相转换,并用 wstring 来判断字符个数,在超长时进行截断。
string 与 wstring 的转换
转换版本一
如果你的 g++ 版本够高(5.0以上),那么可以采用下面的写法,这是最好的:
#include <codecvt>
#include <string>
std::wstring s2ws(const std::string& str)
{
using convert_typeX = std::codecvt_utf8<wchar_t>;
std::wstring_convert<convert_typeX, wchar_t> converterX;
return converterX.from_bytes(str);
}
std::string ws2s(const std::wstring& wstr)
{
using convert_typeX = std::codecvt_utf8<wchar_t>;
std::wstring_convert<convert_typeX, wchar_t> converterX;
return converterX.to_bytes(wstr);
}
std::wstring_convert 是 C++11 标准库提供的对 string 和 wstring 的转换,对 Unicode 进行了语言和库级别的支持。但这一特性在 gcc/g++ 5.0 以上才被支持。
参考资料:
How to convert wstring into string? — stackoverflow
std::wstring_convert — cppreference
std::wstring_convert — cplusplus
转换版本二
如果你的 g++ 版本是支持部分 c++11 特性,那么第二个版本可以用 unique_ptr 来管理内存,这样可以避免直接操作指针的尴尬,程序更加安全。
#include <cstdlib>
#include <memory>
#include <string>
std::wstring s2ws(const std::string& str) {
if (str.empty()) {
return L"";
}
unsigned len = str.size() + 1;
setlocale(LC_CTYPE, "en_US.UTF-8");
std::unique_ptr<wchar_t[]> p(new wchar_t[len]);
mbstowcs(p.get(), str.c_str(), len);
std::wstring w_str(p.get());
return w_str;
}
std::string ws2s(const std::wstring& w_str) {
if (w_str.empty()) {
return "";
}
unsigned len = w_str.size() * 4 + 1;
setlocale(LC_CTYPE, "en_US.UTF-8");
std::unique_ptr<char[]> p(new char[len]);
wcstombs(p.get(), w_str.c_str(), len);
std::string str(p.get());
return str;
}
new 数组的长度要考虑到,因为 wchar_t 为 4 个字节,对于 s2ws, wstring 的长度肯定小于等于 string 的长度,而对 ws2s, string 的长度也肯定小于等于 wstring 4 倍的长度。+1 是预留给字符串的结束符 ‘\0’。
setlocale 函数用于运行时的语言环境,可以在命令行用 locale 查看当前系统的语言环境设置,LC_CTYPE 指语言符号及其分类 。
网上很多版本使用 setlocale(LC_CTYPE, ""); , 这里第二个参数用空字符串,会使用系统当前默认的 locale 设置。
但是这样有个问题,也许你写出来的程序在本机运行正确,但到服务器上就错了,因为服务器的 locale 不一定是 utf8,所以这里要强制设置为 en_US.UTF-8。
mbstowcs 和 wcstombs 是两个 C 语言中对多字节字符串和宽字符字符串的互相转换函数,依赖于当前 locale 中所指定的字符编码。
转换版本三
如果 g++ 连 unique_ptr 都不支持,那就只能使用下面的 new/delete 了。
#include <cstdlib>
#include <string>
std::wstring s2ws(const std::string& str) {
if (str.empty()) {
return L"";
}
unsigned len = str.size() + 1;
setlocale(LC_CTYPE, "en_US.UTF-8");
wchar_t *p = new wchar_t[len];
mbstowcs(p, str.c_str(), len);
std::wstring w_str(p);
delete[] p;
return w_str;
}
std::string ws2s(const std::wstring& w_str) {
if (w_str.empty()) {
return "";
}
unsigned len = w_str.size() * 4 + 1;
setlocale(LC_CTYPE, "en_US.UTF-8");
char *p = new char[len];
wcstombs(p, w_str.c_str(), len);
std::string str(p);
delete[] p;
return str;
}
最终实现
实现了 string 和 wstring 的转换后,接下来的处理就很简单了。实现处理函数 FormatText,然后加入 main 函数测试,完整代码如下:
#include <cassert>
#include <cstdlib>
#include <iostream>
#include <string>
static const int kTextSize = 10;
std::wstring s2ws(const std::string& str) {
if (str.empty()) {
return L"";
}
unsigned len = str.size() + 1;
setlocale(LC_CTYPE, "");
wchar_t *p = new wchar_t[len];
mbstowcs(p, str.c_str(), len);
std::wstring w_str(p);
delete[] p;
return w_str;
}
std::string ws2s(const std::wstring& w_str) {
if (w_str.empty()) {
return "";
}
unsigned len = w_str.size() * 4 + 1;
setlocale(LC_CTYPE, "");
char *p = new char[len];
wcstombs(p, w_str.c_str(), len);
std::string str(p);
delete[] p;
return str;
}
bool FormatText(std::string* txt) {
if (NULL == txt) {
return false;
}
std::cout << "before:" << *txt << std::endl;
std::wstring w_txt = s2ws(*txt);
std::cout << "wstring size:" << w_txt.size() << std::endl;
std::cout << "string size:" << (*txt).size() << std::endl;
if (w_txt.size() > kTextSize) {
w_txt = w_txt.substr(0, kTextSize);
*txt = ws2s(w_txt);
*txt += "...";
}
std::cout << "after:" << *txt << std::endl;
return true;
}
int main() {
assert(L"" == s2ws(""));
std::string txt = "龙之谷app好玩等你";
assert(24 == txt.size());
std::wstring w_txt = s2ws(txt);
assert(10 == w_txt.size());
assert("" == ws2s(L""));
w_txt = L"龙之谷app好玩等你";
assert(10 == w_txt.size());
txt = ws2s(w_txt);
assert(24 == txt.size());
txt = "龙之谷app公测开启";
std::string format_txt = txt;
FormatText(&format_txt);
assert(txt == format_txt);
txt = "龙之谷app公测火爆开启";
FormatText(&txt);
format_txt = "龙之谷app公测火爆...";
assert(format_txt == txt);
return 0;
}