01、CString类介绍

前言:串操作是编程中最常用也最基本的操作之一。 做为VC程序员,无论是菜鸟或高手都曾用过Cstring。而且好像实际编程中很难离得开它(虽然它不是标准C++中的库)。因为MFC中提供的这个类对我们操作字串实在太方便了,CString不仅提供各种丰富的操作函数、操作符重载,使我们使用起串起来更象basic中那样直观;而且它还提供了动态内存分配,使我们减少了多少字符串数组越界的隐患。但是,我们在使用过程中也体会到CString简直太容易出错了,而且有的不可捉摸。所以有许多高人站过来,建议抛弃它。

在此,我个人认为:CString封装得确实很完美,它有许多优点,如“容易使用 ,功能强,动态分配内存,大量进行拷贝时它很能节省内存资源并且执行效率高,与标准C完全兼容,同时支持多字节与宽字节,由于有异常机制所以使用它安全方便” 其实,使用过程中之所以容易出错,那是因为我们对它了解得还不够,特别是它的实现机制。因为我们中的大多数人,在工作中并不那么爱深入地去看关于它的文档,何况它还是英文的。

CString 是一种很有用的数据类型。它们很大程度上简化了MFC中的许多操作,使得MFC在做字符串操作的时候方便了很多。不管怎样,使用CString有很多特殊的技巧,特别是对于纯C背景下走出来的程序员来说有点难以学习。这篇文章就来讨论这些技巧。
  使用CString可以让你对字符串的操作更加直截了当。这篇文章不是CString的完全手册,但囊括了大部分常见基本问题。

02、常见函数表

看不懂没关系,下面都会举例描述,如有不明确的地方,评论区、或者私聊我,知道的一定尽力给你们说明白

函数名描述备注
CStringCString类构造函数
GetLength返回CString对象中的字符数对于多字节字符,计算每个8位字符;也就是说,一个多字节字符中的一个前导字节和一个跟踪字节被计算为两个字符。
IsEmpty测试CString对象是否不包含字符为空,返回0;反之,返回非0
Empty强制字符串具有0长度强制清空字符串内容
GetAt返回给定位置的字符参数应避免负数,否则会出现意想不到的结果
SetAt在给定的位置设置字符当参数为负数或超出对象末尾时,会发生无法预料的结果
Compare比较两个字符串区分大小写
CompareNoCase比较两个字符串不区分大小写
Collate比较两个字符串区分大小写,使用特定于地区的信息
CollateNoCase比较两个字符串不区分大小写,使用特定于地区的信息
Mid提取字符串的中间部分此函数于我而言,多用于字符的匹配等功能
Right提取字符串的右边部分Right函数一般来说用于CString的分段或者在正则表达式中叫字符分割
Left提取字符串的左边部分同上
SpanIncluding提取仅包含集合中字符的子字符串于我而言,上面三个已是足够
SpanExcluding提取仅包含不包含在集合中的字符的子字符串同上,相反的含义
Find在母串中查找子串母串:源字符串 子串:查找的字符串 次函数必须完全匹配子串才ok,不然返回 -1,找到则返回开始下标
ReverseFind反向查找,同上同上
FindOneOf从集合中查找第一个匹配字符此为非完全匹配,找到一个即停止查找(下面示例慢慢解释)
MakeUpper将此字符串中的所有字符转换为大写字符有则转换,无则什么都不做,用于防呆
MakeLower将此字符串中的所有字符转换为小写字符同上
MakeReverse顾名思义,翻转字符串,即从尾至头的一串新字符熟悉数据结构就知道,此函数为逆转算法中的一部分
Replace用其他字符替换指示字符次函数在字符串操作时算是高频函数
Remove从字符串中移除指示字符返回值为移除的数目
Insert在字符串中的给定索引处插入单个字符或子字符串返回插入后对象的长度
Delete从字符串中删除一个或多个字符此处略
Format格式化字符串高频中的高频函数
FormatV将字符串格式化为vsprintf我是很少使用次函数
TrimLeft从字符串中修剪前导空格字符前导空格:指变量或者常量值的有效内容前面的空格
TrimRight从字符串中修剪尾随空格字符
FormatMessage格式化消息字符串MFC通常用自带的消息框
GetBuffer返回指向CString中字符的指针CString转string字符串时的中间转换函数
ReleaseBuffer释放GetBuffer返回的缓冲区的控制
LockBuffer禁用引用计数并保护缓冲区中的字符串基本用不上,略
UnlockBuffer启用引用计数,并释放缓冲区中的字符串同上
LoadString从Windows资源加载现有的CString对象用于涉及到多国语言,需进行语言替换的时候,资源切换

03、CString类成员函数示例

3.1、CString(构造函数)

CString类所需头文件:#include <afx.h>,下面示例略,仅展示示例核心代码

//五种方法,以换行分割,请忽略变量名重名。
CString str;  //最简单的无参构造

CString str("ABCDE"); //带内容的构造
CString buf(str);  //类的拷贝构造
//buf输出:ABCDE

CString str("ABCDEFGH",3);
//str输出:ABC

CString str('a',5);
//str输出:aaaaa

wchar_t s[]=L"abcdef";
CString str(s);
//str输出:abcdef

/* 或许有人会问,你全用英文,那中文的呢?  OK,他来了,请看*/
CString str = _T("我是小猪");
CString buf(str,4);
//buf输出: 我是  ~~~~~~为啥没有小猪,因为小猪被我吃了(开玩笑)
//因为:英文我们一般用1个字节就够了,但是中文字符,一个中文占2个字节,编译器不一样,或许也不一样,想知道你的是多少,可以用下面要说的GetLength方法查看
3.2、GetLength
CString str("AaBbCc");
CString buf = _T("你也是小猪");

//因为GetLength返回的是int类型,所以我们用int类型接收他的返回值
int nOneCharLen = str.GetLength();      //6
int nOneChinaesLen = buf.GetLength();  //10

/* 比如:我们现在想遍历一串字符串,该怎么做? 现有知识肯定已经是够了,如下 */
for(int i = 0; i < str.GetLength(); i++)
{
	CString demo;
	demo = str[i];  //是不是每一个字符都获取到了,想看就打印,想保存就存起来
}
3.3、IsEmpty
//测试对象是否为空,为空时返回零,不为空时返回非零
CString str(_T("C++ is a very good language!"));
CString buf;

if(buf.IsEmpty())
{
	//非0进入
}
else {
	//进入此节点
}


if(str.IsEmpty())
{
	//进入此节点
}
else {
	//为0才进入
}
3.4、Empty
//此函数的意思是将字符串中的内容强制清空,例如:
CString str = _T("No NULL");
int len = str.GetLength();  //6

str.Empty();  //强制清空内容

int len2 = str.GetLength(); //0
3.5、GetAt
TCHAR GetAt( int nIndex ) const;
//返回下标为nIndex的字符,与字符串的[]用法相同

CString str(_T("hello,Cain Or Xcy!"));

char ch = GetAt(3);  //l,看见这个,上面遍历的时候,str[i]也可以替换为str.GetAt(i)
3.6、SetAt
void SetAt( int nIndex, TCHAR ch );
//给下标为nIndex的字符重新赋值

//nIndex: 将要重新赋值的下标
//ch:将要替换的内容

CString buf(_T(ABCD));
buf.SetAt(2,c);  //温馨提示:编程里面很多都是以0为起始下标,不清楚的时候,可以自己输出测试一下,
//实际编程的时候,差之毫厘,失之千里。

//重新输出
AfxMessageBox(buf,MB_ICONINFORMATION);  //ABcD
3.7、Compare
int Compare( LPCTSTR lpsz ) const;
//区分大小写比较两个字符串,相等时返回0,大于时返回1,小于时返回-1

CString str(_T("AAAA"));
int bRet = str.Compare(_T("aaaa"));
if(bRet == 0)
{
	//相等
}
else if(bRet == 1)
{
	//大于,进入此节点
}
else
{
	//小于
}
3.8、CompareNoCase
int CompareNoCase( LPCTSTR lpsz ) const;
//不区分大小写比较两个字符串,相等时返回0,大于时返回1,小于时返回-1

CString str(_T("AAAA"));
int bRet = str.Compare(_T("aaaa"));
if(bRet == 0)
{
	//相等,进入此节点
}
else if(bRet == 1)
{
	//大于
}
else
{
	//小于
}

Ps:上面的比较依据是ASCII码值。

3.9、Mid、Right、Left

如果,现在让你取两个字符串中的某一段来对比,他们是不是全等的,两个CString完成,那么你就不得不使用下面的三个函数。

CString Left( int nCount ) const;
//从左取字串
CString Right( int nCount ) const;
//从右取字串
CString Mid( int nFirst ) const;
CString Mid( int nFirst, int nCount ) const;
//从中间开始取字串

CString str = _T("Cain");
CString buf = _T("Beck");

//首先,演示三个函数的效果
CString temp;
temp = str.Right(2);   //in
temp = str.Left(1);    //C
temp = str.Mid(1);     //ain,无第二参数,默认从参数一开始直接取到结尾


//示例(对比,第5~7位是否相等)
CString str = _T("This is example!");
CString buf = _T("Cain want to go home!");

CString tmp1,tmp2;
tmp1 = str.Mid(5,2); 
tmp2 = buf.Mid(5,2);

if(str.Find(buf) != -1)
{
	//相等,Find函数,下面就会提及
}
else
{
	//不相等
}
3.10、Find、FindOneOf
int Find( TCHAR ch ) const;   //找字符,默认从0开始
int Find( LPCTSTR lpszSub ) const;  //找字符串,默认从0开始
int Find( TCHAR ch, int nStart ) const;  //找字符,从nStart开始
int Find( LPCTSTR pstr, int nStart ) const;  // 找字符串,从nStart开始
//查找字串,nStart为开始查找的位置。未找到匹配时返回-1,否则返回字串的开始位置
int FindOneOf( LPCTSTR lpszCharSet ) const;
//查找lpszCharSet中任意一个字符在CString对象中的匹配位置。未找到时返回-1,否则返回字串的开始位置

CString str(_T("ABC is enough!"));
if(str.Find('e') == -1);  //使用形式之一,其他亦相同
{
	AfxMessageBox("未找到匹配的子串");
}
else {
AfxMessageBox("匹配成功!");
}


if(str.FindOneOf("AB") == -1)
{
	//有A 或者 B 或者 AB都不会等于-1,这里要注意一下,区别在于这里
	AfxMessageBox("未找到匹配的子串");
}
else {
	AfxMessageBox("匹配成功!");
}

//我主要说一下他们的区别
//Find:完全匹配,只有完全匹配子串才ok;  FindOneOf:非完全匹配,找到一个以上即可,找一个字符就相当于完全匹配。
3.11、MakeUpper、MakeLower、MakeReverse
void MakeUpper( );
//将小写字母转换为大写字母
void MakeLower( );
//将大写字母转换为小写字母
void MakeReverse( );
//颠倒字符串的顺序

CString str = _T("abCdEFG");

//当我们需要使用的字符串必须全部大写的时候,我们用原字符串调用MakeUpper
str.MakeUpper(); 
AfxMessageBox("转换为大写后:" + str);  //此时弹窗输出的就是 ABCDEFG

//小写亦同
str.MakeLower();
AfxMessageBox("转换为小写后:" + str);  //此时弹窗的输出: abcdefg

//字符翻转,很多时候面试,就会让我们翻转数组或者字符串,感兴趣可以去看下此函数的实现,非常的精简
str.MakeReverse();
AfxMessageBox("翻转后:" + str);  //output: gfedcba
3.12、Replace

int Replace( TCHAR chOld, TCHAR chNew );
int Replace( LPCTSTR lpszOld, LPCTSTR lpszNew );
//用新的字符或者字符串替换老的字符或者字符串

CString str = _T("0x11,0x12,0x13,0x14,0x15");
//倘若这是某服务器发送的莫一段程序中的内存位置,现在我们要将逗号替换为换行符,换行显示
std.Replace(',','\n');  //某系程序中解析是\r\n

//输出str
AfxMessageBox(str);  //显示有换行输出
3.13、Delete、Insert、Remove
int Delete( int nIndex, int nCount = 1 )
//删除字符,删除从下标nIndex开始的nCount个字符

int Insert( int nIndex, TCHAR ch )
int Insert( int nIndex, LPCTSTR pstr )
//在下标为nIndex的位置,插入字符或字符串。返回插入后对象的长度

int Remove( TCHAR ch );
//移除对象内的指定字符。返回移除的数目

CString str = _T("123ABC789");
str.Delete(0,2);  //3ABC789

str.GetLength();  //7
str.Insert(0,"12");
str.GetLength();  //9

str.Remove('8');  //如果字符串str中有多个8,那么,全部8都会被移除,然后返回移除后的GetLength()大小。

在C++很多库中或者同事封装的类中会看见很多类似:

BOOL ChangeCharToVessel(char OldChar, char NewChar, size_t len = 5);

这是C++中的新语法,函数重载,这里需要注意的是,如果你在形参中第二个参数中分配了默认值,那么,第二参数后面不管有多少参数,全部必须是有默认值的,否则就报错,C++分栏中我记得是有简单说过一次函数重载,感兴趣可以看看或者搜索一下,看看其他的都是OK的。

3.14、Format
格式化字符串forma("%d",12)意思是将一个整形的格式化的字符(我认为是保持其形状不变) 
1).格式说明总是以%字符开始,以下是不同类型数据的格式方式%号后的说明: 
d输出带符号十进制数 
o输出无符号八进制数 
x输出无符号十六进制数 
u输出无符号数 
c输出单个字符 
s输出一串字符 
f输出实数(6位小数) 
e以指数形式输出实数 
g选用f与e格式中输出宽度较小的格式,不输出0 
ld输入输出long型数据 
lf输入输出double型数据 
m数据输出宽度为m 
.n输出小数位数为n 

//示例:
CString str;
int len = 1024;
str.Format(_T("%d"),len);

AfxMessageBox("长度:" + str);  

//此方法跟C语言中的printf函数功能基本一样,看大家怎么理解了
3.15、GetBuffer、ReleaseBuffer
LPTSTR GetBuffer( int nMinBufLength );
//申请新的空间,并返回指针

void ReleaseBuffer( int nNewLength = -1 );
//使用GetBuffer后,必须使用ReleaseBuffer以更新对象内部数据

CString csStr="abcde";
CString pStr=csStr.GetBuffer(10);
strcpy(pStr,"12345");
csStr.ReleaseBuffer();
pStr=NULL;
cout<<csStr                 //12345


CString csStr="abc";
CString pStr=csStr.GetBuffer(10);
strcpy(pStr,"12345");
cout<<csStr.GetLength();       //3(错误的用法)
csStr.ReleaseBuffer();
cout<<csStr.GetLength();       //5(正确)
pStr=NULL;
3.16、LoadString
CString::LoadString

BOOL LoadString( UINT nID );
  throw( CMemoryException );

//返回值:如果加载资源成功则返回非零值;否则返回0。

//参数: nID 一个Windows字符串资源ID。  

//说明:
//此成员函数用来读取一个由nID标识的Windows字符串资源,并放入一个已有的CString对象中。

//示例:下面的例子说明了如何使用CString::LoadString。
// CString::LoadString示例:
#define IDS_FILENOTFOUND 1
CString s;
if (! s.LoadString( IDS_FILENOTFOUND ))
{
  AfxMessageBox("Error Loading String: IDS_FILENOTFOUND");
  ...
} 

04、常见类型转换

平时我们在MFC编程的时候,会遇到很多类型的转换、比如string 转 CString 等
注意和char 转换时,要把char定义成为const char,这样是最安全的。

// string 转 CString
CString str;
std::string strStr = "test";
str = strStr.c_str();


//CString 转 string
std::string buf;
CString str = _T("test");
buf = str.GetBuffer(str.GetLength());

//string 转 char*
const char* p = NULL;
std::string buf = "aaaaa";
p = buf.c_str();

//char* 转 string
const char* p = "ttttt";
std::string buf(p);

//CString 转 char*
CString str(_T("aaa"));
const char* buf = NULL;
strcpy(buf ,str,sizeof(buf ));

//char* 转 CString
CString str;
const char* buf = "test is ok";
str.Format(_T("%s"),buf);



//
// CStringA转CStringW
//
CStringW CStrA2CStrW(const CStringA &cstrSrcA)
{
    int len = MultiByteToWideChar(CP_ACP, 0, LPCSTR(cstrSrcA), -1, NULL, 0);
    wchar_t *wstr = new wchar_t[len];
    memset(wstr, 0, len*sizeof(wchar_t));
    MultiByteToWideChar(CP_ACP, 0, LPCSTR(cstrSrcA), -1, wstr, len);
    CStringW cstrDestW = wstr;
    delete[] wstr;
 
    return cstrDestW;
}


//
// CStringW转CStringA
//
CStringA CStrW2CStrA(const CStringW &cstrSrcW)
{
    int len = WideCharToMultiByte(CP_ACP, 0, LPCWSTR(cstrSrcW), -1, NULL, 0, NULL, NULL);
    char *str = new char[len];
    memset(str, 0, len);
    WideCharToMultiByte(CP_ACP, 0, LPCWSTR(cstrSrcW), -1, str, len, NULL, NULL);
    CStringA cstrDestA = str;
    delete[] str;
 
    return cstrDestA;
}

05、小结

关于MFC 中 CString 基础类,单凭上面的这点东西肯定是无法以一概全的,但是我相信大家了解了上面的知识后,对于后面CString涉及的编程,想来会有自己的一点想法,这样,假以时日,定能将这个基础类拿捏得死死的。

上面有些方法我并没有举例或者说贴出代码,其原因是,那些没有贴出代码的,我自己也没有用过,不知道里面有哪些坑,如果我随意贴出代码,到时候出现了问题,可能会浪费大家的时间,但是如果大家感兴趣,可以一起讨论一下关于CString 中遇到的问题。

版权说明:创作不易,仅此记录自己学习的过程,转载请注明出处,谢谢!

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐