关于字符串类型相关的问题总结

最近老是碰到一些CString,char* ,const char*, char[],TCHAR[] ---相互之间转换的问题,也在网上找了一些文章,转载了一篇认为比较全面并且比较完善。但在实际写代码的过程中有些并不足够,或者说并不完善,很多细节并没有交代,尤其是字符集之间的问题,主要是Unicode和宽(多)字节,下面就先介绍一下字符集:

深入了解字符集和编码问题(转)

一、什么是字符集?什么是编码? 

字符(Character)是文字与符号的总称,包括文字、图形符号、数学符号等。 
一组抽象字符的集合就是字符集(Charset)。 
字符集常常和一种具体的语言文字对应起来,该文字中的所有字符或者大部分常用字符就构成了该文字的字符集,比如英文字符集。 
一组有共同特征的字符也可以组成字符集,比如繁体汉字字符集、日文汉字字符集。 
字符集的子集也是字符集。 

计算机要处理各种字符,就需要将字符和二进制内码对应起来,这种对应关系就是字符编码(Encoding)。 
制定编码首先要确定字符集,并将字符集内的字符排序,然后和二进制数字对应起来。根据字符集内字符的多少,会确定用几个字节来编码。 
每种编码都限定了一个明确的字符集合,叫做被编码过的字符集(Coded Character Set),这是字符集的另外一个含义。通常所说的字符集大多是这个含义。 

二、有哪些字符集? 

ASCII: 
American Standard Code for Information Interchange,美国信息交换标准码。 
目前计算机中用得最广泛的字符集及其编码,由美国国家标准局(ANSI)制定。 
它已被国际标准化组织(ISO)定为国际标准,称为ISO 646标准。 
ASCII字符集由控制字符和图形字符组成。 
在计算机的存储单元中,一个ASCII码值占一个字节(8个二进制位),其最高位(b7)用作奇偶校验位。 
所谓奇偶校验,是指在代码传送过程中用来检验是否出现错误的一种方法,一般分奇校验和偶校验两种。 
奇校验规定:正确的代码一个字节中1的个数必须是奇数,若非奇数,则在最高位b7添1。 
偶校验规定:正确的代码一个字节中1的个数必须是偶数,若非偶数,则在最高位b7添1。 

ISO 8859-1: 
ISO 8859,全称ISO/IEC 8859,是国际标准化组织(ISO)及国际电工委员会(IEC)联合制定的一系列8位字符集的标准,现时定义了15个字符集。 
ASCII收录了空格及94个“可印刷字符”,足以给英语使用。 
但是,其他使用拉丁字母的语言(主要是欧洲国家的语言),都有一定数量的变音字母,故可以使用ASCII及控制字符以外的区域来储存及表示。 
除了使用拉丁字母的语言外,使用西里尔字母的东欧语言、希腊语、泰语、现代阿拉伯语、希伯来语等,都可以使用这个形式来储存及表示。 
    * ISO 8859-1 (Latin-1) - 西欧语言 
    * ISO 8859-2 (Latin-2) - 中欧语言 
    * ISO 8859-3 (Latin-3) - 南欧语言。世界语也可用此字符集显示。 
    * ISO 8859-4 (Latin-4) - 北欧语言 
    * ISO 8859-5 (Cyrillic) - 斯拉夫语言 
    * ISO 8859-6 (Arabic) - 阿拉伯语 
    * ISO 8859-7 (Greek) - 希腊语 
    * ISO 8859-8 (Hebrew) - 希伯来语(视觉顺序) 
    * ISO 8859-8-I - 希伯来语(逻辑顺序) 
    * ISO 8859-9 (Latin-5 或 Turkish) - 它把Latin-1的冰岛语字母换走,加入土耳其语字母。 
    * ISO 8859-10 (Latin-6 或 Nordic) - 北日耳曼语支,用来代替Latin-4。 
    * ISO 8859-11 (Thai) - 泰语,从泰国的 TIS620 标准字集演化而来。 
    * ISO 8859-13 (Latin-7 或 Baltic Rim) - 波罗的语族 
    * ISO 8859-14 (Latin-8 或 Celtic) - 凯尔特语族 
    * ISO 8859-15 (Latin-9) - 西欧语言,加入Latin-1欠缺的法语及芬兰语重音字母,以及欧元符号。 
    * ISO 8859-16 (Latin-10) - 东南欧语言。主要供罗马尼亚语使用,并加入欧元符号。 
很明显,iso8859-1编码表示的字符范围很窄,无法表示中文字符。 
但是,由于是单字节编码,和计算机最基础的表示单位一致,所以很多时候,仍旧使用iso8859-1编码来表示。
而且在很多协议上,默认使用该编码。 

UCS: 
通用字符集(Universal Character Set,UCS)是由ISO制定的ISO 10646(或称ISO/IEC 10646)标准所定义的字符编码方式,采用4字节编码。 
UCS包含了已知语言的所有字符。 
除了拉丁语、希腊语、斯拉夫语、希伯来语、阿拉伯语、亚美尼亚语、格鲁吉亚语,还包括中文、日文、韩文这样的象形文字,UCS还包括大量的图形、印刷、数学、科学符号。 
    * UCS-2: 与unicode的2byte编码基本一样。 
    * UCS-4: 4byte编码, 目前是在UCS-2前加上2个全零的byte。 

Unicode: 
Unicode(统一码、万国码、单一码)是一种在计算机上使用的字符编码。 
它是http://www.unicode.org制定的编码机制, 要将全世界常用文字都函括进去。 
它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。 
1990年开始研发,1994年正式公布。随着计算机工作能力的增强,Unicode也在面世以来的十多年里得到普及。 
但自从unicode2.0开始,unicode采用了与ISO 10646-1相同的字库和字码,ISO也承诺ISO10646将不会给超出0x10FFFF的UCS-4编码赋值,使得两者保持一致。 
Unicode的编码方式与ISO 10646的通用字符集(Universal Character Set,UCS)概念相对应,目前的用于实用的Unicode版本对应于UCS-2,使用16位的编码空间。 
也就是每个字符占用2个字节,基本满足各种语言的使用。实际上目前版本的Unicode尚未填充满这16位编码,保留了大量空间作为特殊使用或将来扩展。 

UTF: 
Unicode 的实现方式不同于编码方式。 
一个字符的Unicode编码是确定的,但是在实际传输过程中,由于不同系统平台的设计不一定一致,以及出于节省空间的目的,对Unicode编码的实现方式有所不同。 
Unicode的实现方式称为Unicode转换格式(Unicode Translation Format,简称为 UTF)。 
    * UTF-8: 8bit变长编码,对于大多数常用字符集(ASCII中0~127字符)它只使用单字节,而对其它常用字符(特别是朝鲜和汉语会意文字),它使用3字节。 
    * UTF-16: 16bit编码,是变长码,大致相当于20位编码,值在0到0x10FFFF之间,基本上就是unicode编码的实现,与CPU字序有关。 


汉字编码: 
    * GB2312字集是简体字集,全称为GB2312(80)字集,共包括国标简体汉字6763个。 
    * BIG5字集是台湾繁体字集,共包括国标繁体汉字13053个。 
    * GBK字集是简繁字集,包括了GB字集、BIG5字集和一些符号,共包括21003个字符。 
    * GB18030是国家制定的一个强制性大字集标准,全称为GB18030-2000,它的推出使汉字集有了一个“大一统”的标准。 

ANSI和Unicode big endia: 
我们在Windows系统中保存文本文件时通常可以选择编码为ANSI、Unicode、Unicode big endian和UTF-8,这里的ANSI和Unicode big endia是什么编码呢? 
ANSI: 
使用2个字节来代表一个字符的各种汉字延伸编码方式,称为ANSI编码。 
在简体中文系统下,ANSI编码代表GB2312编码,在日文操作系统下,ANSI编码代表JIS编码。 
Unicode big endia: 
UTF-8以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。 
Unicode规范中推荐的标记字节顺序的方法是BOM(即Byte Order Mark)。 
在UCS编码中有一个叫做ZERO WIDTH NO-BREAK SPACE的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。 
UCS规范建议我们在传输字节流前,先传输字符ZERO WIDTH NO-BREAK SPACE。 
这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。 
因此字符ZERO WIDTH NO-BREAK SPACE又被称作BOM。 
Windows就是使用BOM来标记文本文件的编码方式的。 

三、编程语言与编码 

C、C++、Python2内部字符串都是使用当前系统默认编码 
Python3、Java内部字符串用Unicode保存 
Ruby有一个内部变量$KCODE用来表示可识别的多字节字符串的编码,变量值为EUC SJIS UTF8 NONE之一。 
$KCODE的值为EUC时,将假定字符串或正则表达式的编码为EUC-JP。 
同样地,若为SJIS时则认定为Shift JIS。若为UTF8时则认定为UTF-8。 
若为NONE时,将不会识别多字节字符串。 
在向该变量赋值时,只有第1个字节起作用,且不区分大小写字母。 
e E 代表 EUC,s S 代表 SJIS,u U 代表 UTF8,而n N 则代表 NONE。 
默认值为NONE。 
即默认情况下Ruby把字符串当成单字节序列来处理。 

四、为什么会乱码? 

乱码是个老问题,从上面我们知道,字符在保存时的编码格式如果和要显示的编码格式不一样的话,就会出现乱码问题。 
我们的Web系统,从底层数据库编码、Web应用程序编码到HTML页面编码,如果有一项不一致的话,就会出现乱码。 
所以,解决乱码问题说难也难说简单也简单,关键是让交互系统之间编码一致。 

五、有没有万金油? 

在如此多种编码和字符集弄的我们眼花缭乱的情况下,我们只需选择一种兼容性最好的编码方式和字符集,让它成为我们程序子系统之间 
交互的编码契约,那么从此恼人的乱码问题即将远离我们而去 -- 这种兼容性最好的编码就是UTF-8! 
毕竟GBK/GB2312是国内的标准,当我们大量使用国外的开源软件时,UTF-8才是编码界最通用的语言。 


看到这里也就差不多对字符集有了一定的了解,其实有的时候并不是转换函数的问题,是你的转换函数没有选对,不同的字符集下转换函数稍微有点区别的,各种不同的数据类型之间的转换更是相关。

下面我就列出常见的各种格式之间相互转换的函数(如有 遗漏请大家留言指出)

atoi 

itoa

double   atof( const   char   *str  ); 
double   _wtof( const  wchar_t   *str ); 
atof是非unicode版本的,所以接受参数为const   char   *。 
_wtof才是unicode下的,所以接受宽字符为参数。 
为了统一,你可以用_tstof或者_ttof。 


CString的GetBuffer(0)可以把CString转为LPTSTR. 
然后需要把LPTSTR转为const   char*. 
比较简单的方法是使用ATL的转换宏,具体的用法和注意事项请参看MSDN。 
USES_CONVERSION; 
const   char*   pSth   =   T2A(SomeLPTSTRString);

非Unicode下,直接用GetBuffer(0)得到的就是单字节字符指针。 
Unicode下,将GetBuffer(0)得到的宽字符指针用W2A宏转成单字节字符指针。

有人为了解决问题把项目属性里的常规->字符集 改成使用多字节字符集,我也这样改过虽然能解决对应的问题,但不建议这样做。

  

UNICODE下宽字符的CString转换为const char * 

一、使用函数_tcscpy_s

CString theString("This is atest"); 

 

int sizeOfString=theString.GetLength()+1; 

 

LPTSTR lpsz=new TCHAR [sizeOfString]; 

 

_tcscpy_s(lpsz,sizeOfString,theString); 

最后再转换一下lpsz为const型

LPTSTR在UNICODE环境下编译是wchar_t类型

二、

CString str=_T("hello world");

char szStr[256]={0};

wcstombs(szStr,str,str.GetLength());

const char* p=szStr;

 

   

还有类似这样的代码

这种代码我不推荐,有安全隐患,strcpy不检查sz的长度是否够装下str。个人认为strcpy中第二个参数不能从CString转到char*,不能自动转换(在绝大部分编译器中)

 

C/C++ code
CString s1;
char dest[256]; sprintf(pdest, "%S", (LPCTSTR)s1); strcpy(pNumber,dest);


VS2005、vs2008、vs2010默认使用wchar_t,定义了可移植转换宏TCHAR,同时也定义了一套相应的通用函数,建议查一下msdn,尽量使用各个库函数的通用形式
如 strcpy - wcscpy - _tcscpy
故无需转换,上面的程序可修改为:

 

 

C/C++ code
#include <tchar.h> CString s1; CString s2; CMyString mstr; m_edit1.GetWindowTextW(s1); char * pNumber; // char改为TCHAR strcpy(pNumber,s1); // strcpy改为_tcscpy,CString可自动转换为LPCTSTR(这里定义为CONST WCHAR*) int x=mstr.GetLength(pNumber); s2.Format(_T("%d"), x); m_edit2.SetWindowTextW(s2); //长度的函数是 int CMyString::GetLength(char *t) // char修改为TCHAR { int len=0; for(int i=0;t[i]!='\0';i++) len++; return len; }

 


再来看看这样一段程序

  CString str = _T("中国abc");
        setlocale(LC_ALL,"chs");//为什么会有这样的一个函数起什么作用??
        char sz[128]={0};
        wcstombs(sz,str,127);
        MessageBoxA(0,sz,0,0);
char*会自动 转换成const char*的

当字符串中出现中文汉字的时候,需要这样的函数不然可能会出现乱码。

下面就介绍一下这个函数:

#include<locale.h>

setlocale:
函数原形为:char *setlocale( int category, const char *locale );
头文件:<locale.h>
所支持的操作系统为:ANSI, Win 95, Win NT
对于简体中文可以使用如下设置:setlocale( LC_ALL, "chs" );

为什么一定要调用setlocale呢?
因为在C/C++语言标准中定义了其运行时的字符集环境为"C",也就是ASCII字符集的一个子集,那么mbstowcs在工作时会将cstr中所包含的字符串看作是ASCII编码的字符,而不认为是一个包含有chs编码的字符串,所以他会将每一个中文拆成2个ASCII编码进行转换,这样得到的结果就是会形成4个wchar_t的字符组成的串,那么如何才能够让mbstowcs正常工作呢?在调用mbstowcs进行转换之间必须明确的告诉mbstowcs目前cstr串中包含的是chs编码的字符串,通过setlocale( LC_ALL, "chs" )函数调用来完成,需要注意的是这个函数会改变整个应用程序的字符集编码方式,必须要通过重新调用setlocale( LC_ALL, "C" )函数来还原,这样就可以保证mbstowcs在转换时将cstr中的串看作是中文串,并且转换成为2个wchar_t字符,而不是4个。
 

本地化设置需要具备三个条件:
    a. 语言代码 (Language Code)
    b. 国家代码 (Country Code)
    c. 编码(Encoding)
    本地名字可以用下面这些部分来构造:
    语言代码_国家代码.编码 比如(zh_CN.UTF-8, en_US等)
locale的别名表见 /usr/lib/X11/locale/locale.alias(以Debian GNU/Linux为例)

setlocale语言字符串参考

Primary
language

Sublanguage

Language string
Chinese Chinese "chinese"
Chinese Chinese (simplified) "chinese-simplified" or "chs"
Chinese Chinese (traditional) "chinese-traditional" or "cht"
Czech Czech "csy" or "czech"
Danish Danish "dan" or "danish"
Dutch Dutch (default) "dutch" or "nld"
Dutch Dutch (Belgian) "belgian", "dutch-belgian", or "nlb"
English English (default) "english"
English English (Australian) "australian", "ena", or "english-aus"
English English (Canadian) "canadian", "enc", or "english-can"
English English (New Zealand) "english-nz" or "enz"
English English (United Kingdom) "eng", "english-uk", or "uk"
English English (United States) "american", "american english", "american-english", "english-american", "english-us", "english-usa", "enu", "us", or "usa"
Finnish Finnish "fin" or "finnish"
French French (default) "fra" or "french"
French French (Belgian) "frb" or "french-belgian"
French French (Canadian) "frc" or "french-canadian"
French French (Swiss) "french-swiss" or "frs"
German German (default) "deu" or "german"
German German (Austrian) "dea" or "german-austrian"
German German (Swiss) "des", "german-swiss", or "swiss"
Greek Greek "ell" or "greek"
Hungarian Hungarian "hun" or "hungarian"
Icelandic Icelandic "icelandic" or "isl"
Italian Italian (default) "ita" or "italian"
Italian Italian (Swiss) "italian-swiss" or "its"
Japanese Japanese "japanese" or "jpn"
Korean Korean "kor" or "korean"
Norwegian Norwegian (default) "norwegian"
Norwegian Norwegian (Bokmal) "nor" or "norwegian-bokmal"
Norwegian Norwegian (Nynorsk) "non" or "norwegian-nynorsk"
Polish Polish "plk" or "polish"
Portuguese Portuguese (default) "portuguese" or "ptg"
Portuguese Portuguese (Brazilian) "portuguese-brazil" or "ptb"
Russian Russian (default) "rus" or "russian"
Slovak Slovak "sky" or "slovak"
Spanish Spanish (default) "esp" or "spanish"
Spanish Spanish (Mexican) "esm" or "spanish-mexican"
Spanish Spanish (Modern) "esn" or "spanish-modern"
Swedish Swedish "sve" or "swedish"
Turkish Turkish "trk" or "turkish"

setlocale国家字符串参考

 

Country/RegionCountry/Region string
Australia "aus" or "australia"
Austria "aut" or "austria"
Belgium "bel" or "belgium"
Brazil "bra" or "brazil"
Canada "can" or "canada"
China "china", "chn", "pr china", or "pr-china"
Czech Republic "cze" or "czech"
Denmark "dnk" or "denmark"
Finland "fin" or "finland"
France "fra" or "france"
Germany "deu" or "germany"
Greece "grc" or "greece"
Hong Kong SAR "hkg", "hong kong", or "hong-kong"
Hungary "hun" or "hungary"
Iceland "iceland" or "isl"
Ireland "irl" or "ireland"
Italy "ita" or "italy"
Japan "jpn" or "japan"
Korea "kor" or "korea"
Mexico "mex" or "mexico"
The Netherlands "nld", "holland", or "netherlands"
New Zealand "nzl", "new zealand", "new-zealand", or "nz"
Norway "nor" or "norway"
Poland "pol" or "poland"
Portugal "prt" or "portugal"
Russia "rus" or "russia"
Singapore "sgp" or "singapore"
Slovakia "svk" or "slovak"
Spain "esp" or "spain"
Sweden "swe" or "sweden"
Switzerland "che" or "switzerland"
Taiwan "twn" or "taiwan"
Turkey "tur" or "turkey"
United Kingdom "gbr", "britain", "england", "great britain", "uk", "united kingdom", or "united-kingdom"
United States

"usa", "america", "united states", "united-states", or "us"

CString、wchar和char相互转换(转载)

1.头文件中要定义宏; 
      #define   UNICODE 
      #define   _UNICODE 
   
2.char转换成wchar 
      const   char   *pFilePathName   =   "c:\\aa.dll"; 
      int   nLen   =   strlen(pFilePathName)   +   1; 
      int   nwLen   =   MultiByteToWideChar(CP_ACP,   0,   pFilePathName,   nLen,   NULL,   0); 
   
      TCHAR   lpszFile[256]; 
      MultiByteToWideChar(CP_ACP,   0,   pFilePathName,   nLen,   lpszFile,   nwLen); 
   
3.wchar转换成char 
        char   *pFilePathName; 
        TCHAR   lpszFile[256]; 
      _tcscpy(lpszFile,   _T("c:\\aa.dll")); 
   
      int   nLen   =   wcslen(wstr)+1;    
      WideCharToMultiByte(CP_ACP,   0,   lpszFile,   nLen,   pFilePathName,   2*nLen,   NULL,   NULL);

 

char*和CString转换

CString 是一种很特殊的 C++ 对象,它里面包含了三个值:一个指向某个数据缓冲区的指针、一个是该缓冲中有效的字符记数(它是不可存取的,是位于 CString 地址之下的一个隐藏区域)以及一个缓冲区长度。有效字符数的大小可以是从0到该缓冲最大长度值减1之间的任何数(因为字符串结尾有一个NULL字符)。字符记数和缓冲区长度被巧妙隐藏。

(1) char*转换成CString

  若将char*转换成CString,除了直接赋值外,还可使用CString::Format进行。例如:

         char chArray[] = "Char test";
         TCHAR * p = _T("Char test");( 或LPTSTR p = _T("Char test");)
         CString theString = chArray;
         theString.Format(_T("%s"), chArray);
         theString = p;

(2) CString转换成char*

  若将CString类转换成char*(LPSTR)类型,常常使用下列三种方法:

  方法一,使用强制转换。例如:

       CString theString( (_T("Char test "));
        LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;

  方法二,使用strcpy。例如:

       CString theString( (_T("Char test "));
       LPTSTR lpsz = new TCHAR[theString.GetLength()+1];
        _tcscpy(lpsz, theString);

  需要说明的是,strcpy(或可移值的_tcscpy)的第二个参数是 const wchar_t* (Unicode)或const char* (ANSI),系统编译器将会自动对其进行转换。

  方法三,使用CString::GetBuffer。

        如果你需要修改 CString 中的内容,它有一个特殊的方法可以使用,那就是 GetBuffer,它的作用是返回一个可写的缓冲指针。 如果你只是打算修改字符或者截短字符串,例如:
       CString s(_T("Char test "));
        LPTSTR p = s.GetBuffer();

        LPTSTR dot = strchr(p, ''.'');

         // 在这里添加使用p的代码

          if(p != NULL)

         *p = _T('\0');
         s.ReleaseBuffer();                     // 使用完后及时释放,以便能使用其它的CString成员函数

         在 GetBuffer 和 ReleaseBuffer 之间这个范围,一定不能使用你要操作的这个缓冲的 CString 对象的任何方法。因为 ReleaseBuffer 被调用之前,该 CString 对象的完整性得不到保障。

 

这里还有一篇文章可以加深大家的理解

CString 转换为 char* (VC6.0与Visual Studio 2005兼容问题)UNICODE字符集

 

posted on 2011-01-20 15:12  Daywei  阅读(757)  评论(0编辑  收藏  举报

导航

技术追求卓越 梦想创造未来