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

来源:互联网 发布:随手记软件 编辑:程序博客网 时间:2024/04/27 21:53
关于字符串类型相关的问题总结

最近老是碰到一些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 stringChineseChinese"chinese"ChineseChinese (simplified)"chinese-simplified" or "chs"ChineseChinese (traditional)"chinese-traditional" or "cht"CzechCzech"csy" or "czech"DanishDanish"dan" or "danish"DutchDutch (default)"dutch" or "nld"DutchDutch (Belgian)"belgian", "dutch-belgian", or "nlb"EnglishEnglish (default)"english"EnglishEnglish (Australian)"australian", "ena", or "english-aus"EnglishEnglish (Canadian)"canadian", "enc", or "english-can"EnglishEnglish (New Zealand)"english-nz" or "enz"EnglishEnglish (United Kingdom)"eng", "english-uk", or "uk"EnglishEnglish (United States)"american", "american english", "american-english", "english-american", "english-us", "english-usa", "enu", "us", or "usa"FinnishFinnish"fin" or "finnish"FrenchFrench (default)"fra" or "french"FrenchFrench (Belgian)"frb" or "french-belgian"FrenchFrench (Canadian)"frc" or "french-canadian"FrenchFrench (Swiss)"french-swiss" or "frs"GermanGerman (default)"deu" or "german"GermanGerman (Austrian)"dea" or "german-austrian"GermanGerman (Swiss)"des", "german-swiss", or "swiss"GreekGreek"ell" or "greek"HungarianHungarian"hun" or "hungarian"IcelandicIcelandic"icelandic" or "isl"ItalianItalian (default)"ita" or "italian"ItalianItalian (Swiss)"italian-swiss" or "its"JapaneseJapanese"japanese" or "jpn"KoreanKorean"kor" or "korean"NorwegianNorwegian (default)"norwegian"NorwegianNorwegian (Bokmal)"nor" or "norwegian-bokmal"NorwegianNorwegian (Nynorsk)"non" or "norwegian-nynorsk"PolishPolish"plk" or "polish"PortuguesePortuguese (default)"portuguese" or "ptg"PortuguesePortuguese (Brazilian)"portuguese-brazil" or "ptb"RussianRussian (default)"rus" or "russian"SlovakSlovak"sky" or "slovak"SpanishSpanish (default)"esp" or "spanish"SpanishSpanish (Mexican)"esm" or "spanish-mexican"SpanishSpanish (Modern)"esn" or "spanish-modern"SwedishSwedish"sve" or "swedish"TurkishTurkish"trk" or "turkish"

setlocale国家字符串参考

 

Country/RegionCountry/Region stringAustralia"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字符集

CString,int,string,char*之间的转换(转)

 

原创粉丝点击