字符串类型处理实战

来源:互联网 发布:音乐编辑软件手机版 编辑:程序博客网 时间:2024/05/17 23:28

概述

在C语言中表示字符串,没有string概念,只可以用char*和char[];在MFC中,为了服务于字符串操作,添加了类CString,该类的头 文件是afx.h.从char* 到CString的转换很简单,只需要用CString的构造函数即可。 

CString到const char*和char*

   新手在将CString向C的字符数组转换时容易出现很多问题.因为CString已经重载了LPCTSTR运算符,所以CString类向const char *转换时没有什么麻烦,如下所示:
 
  char a[100];
  CString str("aaaaaa");
  strncpy(a,(LPCTSTR)str,sizeof(a));
 
  或者如下:
 
  strncpy(a,str,sizeof(a));                       flyhorse注:上面语句将CString-->const char*
  以上两种用法都是正确地.因为strncpy的第二个参数类型为const char *.所以编译器会自动将CString类转换成const char *.很多人对LPCTSTR是什么东西迷惑不解,让我们来看看:

  1.LP表示长指针,在win16下有长指针(LP)和短指针(P)的区别,而在win32下是没有区别的,都是32位.所以这里的LP和P是等价的.

  2.C表示const

  3.T是什么东西呢,我们知道TCHAR在采用UNICODE方式编译时是wchar_t,在普通时编译成char那么就可以看出LPCTSTR(PCTSTR)在UINCODE时是const wchar_t *,PCWSTR,LPCWSTR,在多字节字符模式时是const char *, PCSTR,LPCSTR.

接下来我们看在非UNICODE情况下,怎样将CString转换成char *,很多初学者都为了方便采用如下方法:
 
  (char *)(LPCSTR)str

        注:如果只是将CString-->char*,转换后不修改字符串的内容,这个转换是可以的。
  
这样对吗?我们首先来看一个例子:
 
  CString str("aa");
  strcpy((char *)(LPCTSTR)str,"aaaaaaaa");
  cout<<(LPCTSTR)str<<endl;
  在Debug下运行出现了异常,我们都知道CString类内部有自己的字符指针,指向一个已分配的字符缓冲区.如果往里面写的字符数超出了缓冲区范围,当然会出现异常.但这个程序在Release版本下不会出现问题.原来对CString类已经进行了优化.当需要分配的内存小于64字节时,直接分配64字节的内存,以此类推,一般CString类字符缓冲区的大小为64,128,256,512...这样是为了减少内存分配的次数,提高速度.

  那有人就说我往里面写的字符数不超过它原来的字符数,不就不会出错了,比如
 
  CString str("aaaaaaa");
  strcpy((char *)(LPCTSTR)str,"aa");
  cout<<(LPCTSTR)str<<endl;
  这样看起来是没什么问题.我们再来看下面这个例子:
 
  CString str("aaaaaaa");
  strcpy((char *)(LPCTSTR)str,"aa");
  cout<<(LPCTSTR)str<<endl;
  cout<<str.GetLength()<<endl;
  我们看到str的长度没有随之改变,继续为7而不是2.还有更严重的问题:
 
  CString str("aaaaaaa");
  CString str1 = str;
  strcpy((char *)(LPCTSTR)str,"aa");
  cout<<(LPCTSTR)str<<endl;
  cout<<(LPCTSTR)str1<<endl;
  按说我们只改变了str,str1应该没有改变呀,可是事实时他们都变成了"aa".

难道str和str1里面的字符指针指向的缓冲区是同一个?我们在Effective C++里面得知,如果你的类内部有包含指针,请为你的类写一个拷贝构造函数和赋值运算符.不要让两个对象内部的指针指向同一区域,而应该重新分配内存.难道是微软犯了错?原来这里还有一个"写时复制"和"引用计数"的概念.CString类的用途很广,这样有可能在系统内部产生大量的CString临时对象.这时为了优化效率,就采用在系统软件内部广泛使用的"写时复制"概念.即当从一个CString产生另一个CString并不复制它的字符缓冲区内容,而只是将字符缓冲区的"引用计数"加1.当需要改写字符缓冲区内的内容时,才分配内存,并复制内容.以后我会给出一个"写时复制"和"引用计数"的例子我们回到主题上来,

当我们需要将CString转换成char *时,我们应该怎么做呢?其实只是麻烦一点,如下所示:
 
  CString str("aaaaaaa");
  strcpy(str.GetBuffer(10),"aa");
  str.ReleaseBuffer();
 
       注:将CString-->char*,并修改字符串内容。
  当我们需要字符数组时调用GetBuffer(int n),其中n为我们需要的字符数组的长度.使用完成后一定要马上调用ReleaseBuffer();还有很重要的一点就是,在能使用const char *的地方,就不要使用char *

There are many ways you can cast a CString to a char-array (aka c-string). I have listed them all for you.
ANSI PROJECTS
CString to const char*
CString Str;
const char* Text = (LPCSTR)Str;
// only works in ANSI projectsCString to char*CString Str;char* Text = Str.GetBuffer( 256 ); // only works in ANSI projects
// do something with Text here
Str.ReleaseBuffer();
CString to const wchar*
CString Str( "MfcTips.com" );CStringW StrW( Str ); // use an intermediate CStringWconst wchar* Text = (LPCWSTR)StrW;
CString to wchar*
CString Str( "MfcTips.com" );
CStringW StrW( Str );
// use an intermediate CStringW
wchar* Text = StrW.GetBuffer( 256 );
// do something with Text hereStrW.ReleaseBuffer();
CString to std::wstring
CString StrIn( "MfcTips.com" );
std::wstring StrOut;StrOut = (LPCWSTR)CStringW( StrIn );
// use an intermediate CStringW
CString to std::string
CString StrIn( "MfcTips.com" );
std::wstring StrOut;
StrOut = (LPCSTR)StrIn;
UNICODE PROJECTS
CString to const wchar*
CString Str;
const wchar* Text = (LPCWSTR)Str;
// only works in Unicode projectsCString to wchar*wchar* Text = Str.GetBuffer( 256 ); // only works in Unicode projects
// do something with Text here
Str.ReleaseBuffer();
CString to const char*
CString Str( L"MfcTips.com" );
CStringA StrA( Str );
// use an intermediate CStringA
const char* Text = (LPCSTR)StrA;
CString to char*
CString Str( L"MfcTips.com" );
CStringA StrA( Str );
// use an intermediate CStringA
char* Text = StrA.GetBuffer( 256 );
// do something with Text here
StrA.ReleaseBuffer();
CString to std::wstring
CString StrIn( L"MfcTips.com" );
std::wstring StrOut;
StrOut = (LPCTSTR)StrIn;
CString to std::string
CString StrIn( L"MfcTips.com" );
std::wstring StrOut;
StrOut = (LPCSTR)CStringA( StrIn );
// use an intermediate CStringA
Warning:Using (LPSTR)(LPCSTR), or (LPTSTR)(LPCTSTR), or (LPWSTR)(LPCWSTR) cast on a CString object to get non-const PSTR is unsafe.The double cast is handled from right to left so the (LPCSTR) or (LPCTSTR) or (LPCWSTR) is handled first. This will give you a const pointer to the CString object’s internal buffer.The CString object’s internal buffer is “locked” when you access it this way.The (LPSTR) or (LPTSTR) or (LPWSTR) cast is handled next. This simply removes the const-ness attribute from the pointer. It does not change anything about the CString object’s internal buffer which is what the pointer points to. The CString object’s internal buffer is still “locked” when you access it this way.So the resulting (non-const) PSTR points to data which is pseudo-const, which could be a very bad thing.It compiles, and executes, but if you attempt to modify the data in this PSTR your application may crash.For example, if you copy too many characters into this PSTR, you could cause a buffer overflow, because the CString object’s internal buffer does not automatically reallocate on demand when accessed this way. Copying more than 4 characters will most likely cause a buffer overflow and cause your application to crash.It is technically “safe” to do this if you can guarantee the data in this PSTR will not be modified. For example, if you need to retrieve the string as non-const for use in a function which is guaranteed to not modify it.If you want to get a non-const PSTR to a CString object’s internal buffer, you should use CString::GetBuffer( Size ).
CString Str;
strcpy( (LPTSTR)(LPCTSTR)Str, "Hello" );
// your application may crash

补充1 微软官方参考

同时你可以参考微软官方说明,http://msdn.microsoft.com/en-us/library/ms235631(v=VS.80).aspx 但这个是基于CLR的而非本地C++程序 。

补充2 WINAPI中的字符与char的转换

LPCWSTR与char之间的转换char很难转换成LPCWSTR,就目前我没有看到解决方法,不过我们可以用迂回的办法,先将char转换成wchar_t然后将其转化成LPCWSTR;另一个方式是包含tchar.h头文件,将char类型转换成TCHAR;大致如上。

2011.10.17

补充3 字符代码在计算机中的表示

计算机字符代码是由Herman Hollerith发明的H...卡上的编码演变而来的,其史前时代是字符集像Morse码转换码(shift)和转义码(escape)以及电传码(Telex);ASCII起源于1950s末,1967年最终定型。编码字符集,7位美国国家信息交换标准码(7位ASCII)。

双字节字符集(DBCS)

前128是ASCII(单字节),后128有些还跟随有第二个字节(双字节)。这两个字节(称为前导字节和尾随字节)在一起代表一个单独的字符,常常是一个复杂的象形文字。缺陷:混乱(当有指针指向DBCS字符串的中间,那前一个字符的地址是什么呢?要额外编码来确定。

Unicode字符集

每一个在Unicode里的字符都是16位的宽度,8位值在Unicode里是没有意义的。而且它只有一个字符集,避免了二义性。缺点是占用内存比较大。

宽字符和C语言

宽字符(用多个字节来代表一个字符集)不一定是Unicode。Unicode只是宽字符的一种实现。

char数据类型

定义和存储字符和字符串。举例说明:

字符                                         char c= 'A';变量c只需要一个字节的存储空间而且会用16进制值0x41来初始化,

字符指针                                 char *p;Win32是32位的要32/8个字节的存储空间。还可以如下初始化也更指向字符串的指针:char *p = “hello!";变量p仍要4个字节的存储空间,字符存储在静态内存中并使用7个字节的存储空间——其中6个字节放字符,剩的存储表示字符串结束的标记0;

字符数组                                char a[10]; sizeof(a)==10; 全局变量(在任何的函数外面):我们可以这样初始化这个字符素组: char a[] = "hello!"; 局部变量的话:static char a[] = "hello!";

但无论是哪一种情况字符串都最终被存储在静态内存中,并有一个表示结束的0加在最后。

更宽的字符

c语言中更宽的字符类型是基于wchar_t的,WCAHR.H,L(不能有空格"Hello!"

宽字符库函数

strlen(“”)不能出来宽字符;它的宽字符版是wcslen(“”)(定义在WCHAR.H和STRING.H)中,printf 的宽字符版是wprintf;所以平时代码在字符上过不去,可以尝试还函数;

维护一个源代码文件

TCHAR.H 微软VC++的,非ANSI C标准库,

#define _tcslen wcslen

也可以:

#define _tcslen strlen

还有

_UNICODE(使用宽字符的标记)

typedef wchar_t  TCAHR ;(默认)

typedef char TCHAR;

 

#define _T(x)  ##x

#define _T(x)  x(默认)

补充四:LPCTSTR 与 string 互相转化 

 

typedef char*  LPSTR ;

LPCTSTR a = L"LPCTSTR to string ";

string b;

b = (LPSTR) a;

 补充五:QString中文支持

在windows下,QString转char*,使用toLocal8Bit().data()就能获得,当然前提你要是QString中是正确的中文,即

str="我是中国人";
qDebug()<<str;   //能输出"我是中国人"。

如果是在linux下,就跟系统有关系了。有区别的,而且如果是qt4,linux下中文还需要翻译器。

QString的中文变量声明也是有区别的。

linux下TextCodec是GBK的,而windows下是GB18030的。所以你可以在main.cpp中定义

    QTextCodec::setCodecForLocale(QTextCodec::codecForName("GBK"));
    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("GBK"));
    QTextCodec::setCodecForTr(QTextCodec::codecForName("GBK"));

或是
    QTextCodec::setCodecForLocale(QTextCodec::codecForName("GB18030"));
    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("GB18030"));
    QTextCodec::setCodecForTr(QTextCodec::codecForName("GB18030"));

然后在使用QString中文是,采用QString::fromLocal8Bit("我是中国人").或tr("我是中国人")来进行变量声明。这样才能正确显示在窗体控件上,而不是乱码。

-----------------------------------------------------------------------------------------------------------------------------------------------------------

为在main中加:
 #ifdef _WINDOWS
     QTextCodec::setCodecForCStrings(QTextCodec::codecForName("System"));
     QTextCodec::setCodecForTr(QTextCodec::codecForName("System"));
     QTextCodec::setCodecForLocale(QTextCodec::codecForName("System"));
 #else
     QTextCodec::setCodecForCStrings(QTextCodec::codecForName("GB18030"));
     QTextCodec::setCodecForTr(QTextCodec::codecForName("GB18030"));
     QTextCodec::setCodecForLocale(QTextCodec::codecForName("GB18030"));
 #endif

 也可以用.fromLocal8Bit() 和 .toLocal8Bit()

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

补充六:从XML中读取的中文在QT中显示为乱码

修改encoding 例如:<?xml version="1.0" encoding="UTF-8"?>

设置XML文件自身的编码方式。。。。可以通过Notepad等软件另存为,一般它会用ANSII编码,可以改为UTF-8,Unicode,GBK等。

2013-7-3

补充七:string input = "ahgjasgj"; input调试模式下显示“错误的指针”

 C风格的string类初始化Visual Studio可能已经不支持了,尝试使用string input("ahgjasgj")。

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


补充八:关于com等其他情况的编码

(1) char*转换成CString

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

[cpp] view plaincopy
  1. char chArray[] = "This is a test";  
  2. char * p = "This is a test";   

  或

[cpp] view plaincopy
  1. LPSTR p = "This is a test";   

  或在已定义Unicode应的用程序中

[cpp] view plaincopy
  1. TCHAR * p = _T("This is a test");  

  或

[cpp] view plaincopy
  1. LPTSTR p = _T("This is a test");  
  2. CString theString = chArray;  
  3. theString.Format(_T("%s"), chArray);  
  4. theString = p;   

 

(2) CString转换成char*

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

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

 

[cpp] view plaincopy
  1. CString theString( "This is a test" );  
  2. LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;   

  方法二,使用strcpy。例如:

 

[cpp] view plaincopy
  1. CString theString( "This is a test" );  
  2. LPTSTR lpsz = new TCHAR[theString.GetLength()+1];  
  3. _tcscpy(lpsz, theString);   

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

  方法三,使用CString::GetBuffer。例如:

[cpp] view plaincopy
  1. CString s(_T("This is a test "));  
  2. LPTSTR p = s.GetBuffer();   
  3. // 在这里添加使用p的代码  
  4. if(p != NULL) *p = _T('/0');  
  5. s.ReleaseBuffer();   
  6. // 使用完后及时释放,以便能使用其它的CString成员函数   

 

(3) BSTR转换成char*

  方法一,使用ConvertBSTRToString。例如:

[cpp] view plaincopy
  1. #include   
  2. #pragma comment(lib, "comsupp.lib")  
  3. int _tmain(int argc, _TCHAR* argv[]){  
  4. BSTR bstrText = ::SysAllocString(L"Test");  
  5. char* lpszText2 = _com_util::ConvertBSTRToString(bstrText);  
  6. SysFreeString(bstrText); // 用完释放  
  7. delete[] lpszText2;  
  8. return 0;  
  9. }   

 

  方法二,使用_bstr_t的赋值运算符重载。例如:

[cpp] view plaincopy
  1. _bstr_t b = bstrText;  
  2. char* lpszText2 = b;   

 

(4) char*转换成BSTR

  方法一,使用SysAllocString等API函数。例如:

[cpp] view plaincopy
  1. BSTR bstrText = ::SysAllocString(L"Test");  
  2. BSTR bstrText = ::SysAllocStringLen(L"Test",4);  
  3. BSTR bstrText = ::SysAllocStringByteLen("Test",4);   

 

  方法二,使用COleVariant或_variant_t。例如:

[cpp] view plaincopy
  1. //COleVariant strVar("This is a test");  
  2. _variant_t strVar("This is a test");  
  3. BSTR bstrText = strVar.bstrVal;   

 

  方法三,使用_bstr_t,这是一种最简单的方法。例如:

[cpp] view plaincopy
  1. BSTR bstrText = _bstr_t("This is a test");   

 

  方法四,使用CComBSTR。例如:

[cpp] view plaincopy
  1. BSTR bstrText = CComBSTR("This is a test");   

  或

[cpp] view plaincopy
  1. CComBSTR bstr("This is a test");  
  2. BSTR bstrText = bstr.m_str;   

 

  方法五,使用ConvertStringToBSTR。例如:

[cpp] view plaincopy
  1. char* lpszText = "Test";  
  2. BSTR bstrText = _com_util::ConvertStringToBSTR(lpszText);   

 

(5) CString转换成BSTR

  通常是通过使用CStringT::AllocSysString来实现。例如:

[cpp] view plaincopy
  1. CString str("This is a test");  
  2. BSTR bstrText = str.AllocSysString();  
  3. …  
  4. SysFreeString(bstrText); // 用完释放  

 

(6) BSTR转换成CString

  一般可按下列方法进行:

[cpp] view plaincopy
  1. BSTR bstrText = ::SysAllocString(L"Test");  
  2. CStringA str;  
  3. str.Empty();  
  4. str = bstrText;   

  或

[cpp] view plaincopy
  1. CStringA str(bstrText);   

 

(7) ANSI、Unicode和宽字符之间的转换

  方法一,使用MultiByteToWideChar将ANSI字符转换成Unicode字符,使用WideCharToMultiByte将Unicode字符转换成ANSI字符。

  方法二,使用“_T”将ANSI转换成“一般”类型字符串,使用“L”将ANSI转换成Unicode,而在托管C++环境中还可使用S将ANSI字符串转换成String*对象。例如:

[cpp] view plaincopy
  1. TCHAR tstr[] = _T("this is a test");  
  2. wchar_t wszStr[] = L"This is a test";  
  3. String* str = S”This is a test”;   

 

  方法三,使用ATL 7.0的转换宏和类。ATL7.0在原有3.0基础上完善和增加了许多字符串转换宏以及提供相应的类,它具有如图3所示的统一形式:

  其中,第一个C表示“类”,以便于ATL 3.0宏相区别,第二个C表示常量,2表示“to”,EX表示要开辟一定大小的缓冲。SourceType和DestinationType可以是A、 T、W和OLE,其含义分别是ANSI、Unicode、“一般”类型和OLE字符串。例如,CA2CT就是将ANSI转换成一般类型的字符串常量。下面是一些示例代码:

[cpp] view plaincopy
  1. LPTSTR tstr= CA2TEX<16>("this is a test");  
  2. LPCTSTR tcstr= CA2CT("this is a test");  
  3. wchar_t wszStr[] = L"This is a test";  
  4. char* chstr = CW2A(wszStr);   

 -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------



文章出处:飞诺网(www.firnow.com):http://dev.firnow.com/course/3_program/c++/cppjs/2008821/136752.html

QTCN社区 http://www.qtcn.org/bbs/read-htm-tid-13581.html    http://www.qtcn.org/bbs/read-htm-tid-29932-displayMode-1.html

CSDN论坛 http://bbs.csdn.net/topics/330157015

诺基亚开发者论坛 http://www.developer.nokia.com/Community/Wiki/说说Qt中中文字符的编解码那些事

MFC Tips http://mfctips.com/2012/07/06/cast-cstring-to-c-string/onLoad(){alert('Welcome');return false}

com字符串转换:http://blog.csdn.net/zeuskaaba/article/details/4082826 

原创粉丝点击