欢迎光临~湖南智能应用科技有限公司-hniat.com
语言选择: 中文版 ∷  英文版

基础知识

CString用法总结

概述:CString是MFC中提供的用于处理字符串的类,是一种很有用的数据类型。

它很大程度上简化了MFC中的许多操作,使得MFC在做字符串操作时方便了很多。

不管怎样,使用CString有很多的特殊技巧,特别对于纯C背景下走出来的程序员来说有点难以学习。

 

一、前言

CString位于头文件afx.h中,这篇文章就来讨论这些技巧。

参考百度百科并截取部分对我有用的内容记录在这篇随笔中,这篇文章包括以下内容:

<1>CString对象的连接

<2>格式化字符串(包括int转化为CString)

<3>CString类的成员函数

<4>CString转化为int型

<5>CString和char*的相互转化

char*转化为CString

CString转化为char*之一:使用LPCTSTR

CString转化为char*之二:使用CString对象的GetBuffer方法

CString转化为char*之三:和控件的接口

 

 

二、对象连接

CString类重载了+运算符,因此CString对象能对直接使用+号进行连接。而不再使用strcat函数

 CString s1, s2, s3;
    s1 = _T("Hello");
    s2 = _T("World");
    s3 = s1 + _T(",") + s2 +_T("!");
    MessageBox(s3);
View Code

注意:MFC编程中,为了兼容Unicode编码,字符串尽量都使用_T()宏

 

三、格式化字符串

可以利用CString的Format方法使用C风格字符串格式化一个CString对象,而不再使用sprintf函数

 CString str; int dollar = 100;
    str.Format(_T("I have %d dollars\n"), dollar);    
View Code

 

四、成员函数

1.构造函数

CString有很多构造函数,下面只介绍几个常用的:

(注意函数原型的变量名都是我自己起的,源码中名字可能不是这个名字,不过不影响)

(另一个类对象引用名采用other, 字符个数采用n)

<1>用已存在的 CString对象 初始化CString对象

函数原型: CString(const CString& other);

<2>可以使用常量字符串初始化CString对象

函数原型: CString(const LPCTSTR other);  //这个原型是我猜的

实例: CString str(_T("Hello,World!"));

<3>将字符串other中的前n个字符拷贝到该CString对象

函数原型: CString(LPCTSTR other,int n);

实例: CString str(_T("Hello,World! this is redundant\n"), 12);

<4>用n个字母ch初始化CString对象

函数原型:CString(TCHAR ch,int n = 1);

实例: CString str('6', 6);  //str初始化为6个6,即666666

    //构造函数 CString s1 = _T("Hello,World!"); //用字符串初始化CString类对象 CString s2 = s1; //用CString类对象初始化CString类对象 CString s3(s1, 5); //用s1的前5个字符初始化类对象 CString s4(_T("Hello,World!"), 5); //用字符串的前n个字符初始化类对象 CString s5(_T('6'), 5); //用n个给定的字符初始化类对象  MessageBox(s1); //输出Hello,World! MessageBox(s2); //输出Hello,World! MessageBox(s3); //输出Hello MessageBox(s4); //输出Hello MessageBox(s5); //输出66666
View Code

注意:在初始化时使用=运算符调用的是相应类型的构造函数,而不是重载的=运算符,

此外,部分构造函数无法写成=的形式,比如使用n个ch初始化CString对象,使用字符串的前n个字符初始化类对象等


2.CString类的大小写转换及顺序转换函数

<1> 将字符串中的所有大写字符转换为小写字符

函数原型:CString& MakeLower();

<2>将字符串中的所有小写字符转换为大写字符

函数原型:CString& MakeUpper();

<3>将字符串中所有字符的顺序颠倒

函数原型:CString& MakeReverse()

<4>要做相应操作,则使用类对象调用相应函数,那么该对象的值便被修改

    //CString类的大小写转换及顺序转换函数 CString s1 = _T("aaBBCCddEe");

    s1.MakeLower();
    MessageBox(s1); //输出 aabbccddee  s1.MakeUpper();
    MessageBox(s1); //输出 AABBCCDDEE MessageBox(s1.MakeLower().MakeReverse()); //输出eeddccbbaa MessageBox(s1); //输出eeddccbbaa
View Code

 

3.CString对象的连接

多个CString对象的连接可以通过重载运算符+、+=实现。

详细参见上面的内容(二、对象连接)

    //CString对象的连接 CString s1 = _T("Hello");
    CString s2 = _T("World!");

    MessageBox(s1 + _T(",") + s2 + _T("!")); //输出Hello,World!  s1 += _T(",");
    s1 += s2;
    s1 += _T("!"); //s1内容变为Hello,World! MessageBox(s1); //输出Hello,World!
View Code

 

4.CString对象的比较

CString对象的比较可以通过==、!=、<;、>;、<=、>=等重载运算符实现,也可以使用Compare和CompareNoCase成员函数实现。

<1>==,!=,<,>,<=,>=都是根据ASCII值大小(字符串的字典序)进行比较,

返回值为0或1,1表示使用的比较运算符判断成立。

<2>Compare函数类似strcmp函数,相等返回0,

小于传进来的参数则返回小于0的数,大于传进来的参数则返回大于0的数

<3>CompareNoCase类似Compare,只是不区分大小写。

    //CString对象的比较 CString s1 = _T("aabbcc");
    CString s2 = _T("aabbdd");
    CString s3 = _T("AAbbcc");
    CString s4 = _T("aabbcc"); //部分运算符用法 int a1 = (s1 == s2); //s1 != s2,a1为0 int a2 = (s1 != s2); //s1 != s2,a2为1 int a3 = (s1 <= s3); //s1 > s3, a3为0, 注意大写字母的ASCII码比较小  CString str1, str2, str3;
    str1.Format(_T("%d"), a1);
    str2.Format(_T("%d"), a2);
    str3.Format(_T("%d"), a3);
    MessageBox(str1);
    MessageBox(str2);
    MessageBox(str3); //Compare用法 int a4 = s1.Compare(s2); //s1 < s2,a4为-1 int a5 = s1.Compare(s3); //s1 > s2,a5为1 int a6 = s1.Compare(s4); //s1 = s2,a6为0  CString str4,str5,str6;
    str4.Format(_T("%d"), a4);
    str5.Format(_T("%d"), a5);
    str6.Format(_T("%d"), a6);
    MessageBox(str4);
    MessageBox(str5);
    MessageBox(str6); //CompareNoCase用法 int a7 = s1.CompareNoCase(s2); //不区分大小写, s1 < s2, a7 = -1 int a8 = s1.CompareNoCase(s3); //不区分大小写, s1 = s2, a8 = 0 int a9 = s1.CompareNoCase(s4); //不区分大小写, s1 = s2, a9 = 0  CString str7, str8, str9;
    str7.Format(_T("%d"), a7);
    str8.Format(_T("%d"), a8);
    str9.Format(_T("%d"), a9);
    MessageBox(str7);
    MessageBox(str8);
    MessageBox(str9);
View Code


5.CString对象字符串的提取操作

<1>提取该字符串左边nCount个字符的子字符串,并返回一个包含这个子字符串的拷贝的CString对象

函数原型:CString Left(int nCount) const;

<2>提取该字符串右边nCount个字符的子字符串,并返回一个包含这个子字符串的拷贝的CString对象

函数原型:CString Right(int nCount) const;
<3>提取该字符串中以索引iFirst位置开始的nCount个字符组成的子字符串,并返回一个包含这个子字符串的拷贝的CString对象
函数原型:
CString Mid(int iFirst,int nCount) const;
提取该字符串中以索引iFirst位置开始直至字符串结尾的子字符串,并返回一个包含这个子字符串的拷贝的CString对象
函数原型:CString Mid(int iFirst) const;
 
 
    //CString对象字符串的提取操作 CString s1 = _T("aabbccddee");

    MessageBox(s1.Left(4)); //左边四个字符,输出aabb MessageBox(s1.Right(4)); //右边4个字符,输出ddee MessageBox(s1.Mid(4, 2)); //输出从第4个位置开始的两个字符,即输出cc MessageBox(s1.Mid(4)); //输出从第4个位置开始到结尾的子串,即输出ccddee
View Code
 

6.CString对象字符串的查找操作

<1>在CString对象字符串的iStart索引位置开始,查找子字符串pszSub或字符ch第一次出现的位置,如果没有找到则返回-1
int Find(PCXSTR pszSub,int iStart=0) const throw();  //查找子串第一次出现位置
int Find(XCHAR ch,int iStart=0) const throw();    //查找某字符第一次出现位置
<2>查找pszCharSet字符串中的任意字符,返回第一次出现的位置,找不到则返回-1
int FindOneOf(PCXSTR pszCharSet) const throw();  //查找给定字符串中的任意一个字符在原串的第一次出现位置
<3>从字符串末尾开始查找指定的字符ch,返回其位置,找不到则返回-1。这里要注意,尽管是从后向前查找,但是位置的索引还是要从开始算起
int ReverseFind(XCHAR ch) const throw();
    //CString对象字符串的查找操作 CString s1 = _T("aabbccbbaa"); //查找子串 int p1 = s1.Find(_T("aa")); //第二个参数默认为0, 故p1为0, int p2 = s1.Find(_T("aa"), 1); //从下表为1开始往后找子串aa, 故p2为8 int p3 = s1.Find(_T("abc")); //未找到,返回-1,即-1  CString str1, str2, str3;
    str1.Format(_T("%d"), p1);
    str2.Format(_T("%d"), p2);
    str3.Format(_T("%d"), p3);
    MessageBox(str1);
    MessageBox(str2);
    MessageBox(str3); //查找字符 int p4 = s1.Find(_T('b')); //第二个参数默认为0,p4为2 int p5 = s1.Find(_T('b'), 4); //从标为4的位置开始往后找,p5为6 int p6 = s1.Find(_T('c'), 6); //未找到,p6为-1  CString str4, str5, str6;
    str4.Format(_T("%d"), p4);
    str5.Format(_T("%d"), p5);
    str6.Format(_T("%d"), p6);
    MessageBox(str4);
    MessageBox(str5);
    MessageBox(str6);
View Code
7.CString类对象字符串的替换与删除
<1>用字符串pszNew替换CString对象中的子字符串pszOld,返回替换的字符串个数
int Replace(PCXSTR pszOld,PCXSTR pszNew);    //替换子串
    //使用Replace替换子串 CString s1 = _T("aabbccddaabbccdd"); int cnt = s1.Replace(_T("aa"), _T("##"));
    CString s2;
    s2.Format(_T("%d"), cnt);
    MessageBox(s1); //输出##bbccdd##bbccdd MessageBox(s2); //输出2
View Code
<2>用字符chNew替换CString对象中的字符chOld,返回替换的字符个数
int Replace(XCHAR chOld,XCHAR chNew);    //替换字符
    //使用Replace替换字符 CString s1 = _T("aabbccddaabbccdd"); int cnt = s1.Replace('a', '#');
    CString s2;
    s2.Format(_T("%d"), cnt);

    MessageBox(s1); //输出##bbccdd##bbccdd MessageBox(s2); //输出4
View Code
<3>从字符串中删除iIndex位置开始的nCount个字符,返回删除操作后的字符串的长度
int Delete(int iIndex,int nCount = 1);
    //使用Delete删除字符 CString s1 = _T("aabbccdd"); int len = s1.Delete(2, 2); //删除bb,len应为6  CString s2;
    s2.Format(_T("%d"), len);

    MessageBox(s1); //输出aaccdd MessageBox(s2); //输出6
View Code
<4>删除字符串中的所有由chRemove指定的字符,返回删除的字符个数。
int Remove(XCHAR chRemove);
    //使用Remove删除字符 CString s1 = _T("aabbccdd"); int len = s1.Remove(_T('b')); //len为删除字符个数,应该为2  CString s2;
    s2.Format(_T("%d"), len);

    MessageBox(s1); //输出aaccdd MessageBox(s2); //输出2
View Code

 

8.CString类的格式化字符串方法
<1>使用CString类的Format成员函数可以将int、short、long、float、double等数据类型格式化为字符串对象。
函数原型: void __cdecl Format(PCXSTR pszFormat,[,argument]...);
<2>参数pszFormat为格式控制字符串;参数argument可选,为要格式化的数据,
一般每个argument在pszFormat中都有对应的表示其类型的子字符串,int型的argument对应的应该是"%d",float型的应对应"%f",等等
    //CString类格式化字符串的方法  CString s1, s2; int cnt = 100; const double PI = 3.141592653;
    s1.Format(_T("I have %d dollars!"), cnt);
    s2.Format(_T("PI is approximate to %.2f!"), PI);

    MessageBox(s1); //输出I have 100 dollars! MessageBox(s2); //输出PI is approximate to 3.14!
View Code
 
9.做编译原理作业时,需要用到Insert方法,特地去查询了一下,特此补充:
<1>函数原型
int Insert(int iIndex, PCXSTR psz);
int Insert(int iIndex, XCHAR ch);
<2>参数说明
iIndex 要往该位置之前插入字符或字符串
psz  要插入的字符串
ch   要插入的字符
<3>返回新的CString的长度
<4>iIndex 参数标识是移动腾出空间。字符或子字符串的第一个字符。
如果 nIndex 为零,在整个字符串之前发生。如果 nIndex 高于该字符串的长度,函数将连接 ch 或 psz和新材料提供的当前字符串
    CString s1 = _T(", World!");
    s1.Insert(0, _T("Hello")); //s1为"Hello, World!"  MessageBox(s1);

    CString s2 = _T("ello, World!");
    s2.Insert(0, _T('H')); //s2为"Hello, World!" MessageBox(s2);
View Code
 
五、int型
把 CString 类型的数据转化成整数类型最简单的方法就是使用标准的字符串到整数转换例程
<1>虽然通常你怀疑使用_atoi()函数是一个好的选择,它也很少会是一个正确的选择
<2>如果你准备使用 Unicode 字符,你应该用_ttoi(),它在 ANSI 编码系统中被编译成_atoi(),而在 Unicode 编码系统中编译成_wtoi()。
 注意:第一个参数为要转化的字符串,第二个参数为结尾位置,一般设置为NULL,第三个为需要转化的参数
<3>你也可以考虑使用_tcstoul()或者_tcstol(),它们都能把字符串转化成任意进制的长整数(如二进制、八进制、十进制或十六进制),
不同 点在于前者转化后的数据是无符号的(unsigned),而后者相反。
    CString s1 = _T("123"); int n1 = _ttoi(s1); //  unsigned long long n2 = _tcstoul(s1, 0, 10); //结果123 long long n3 = _tcstol(s1, 0, 8); //结果为83,八进制123的十进制为83  CString str1, str2, str3;
    str1.Format(_T("%d"), n1);
    str2.Format(_T("%llu"), n2);
    str3.Format(_T("%lld"), n3);
    MessageBox(str1);
    MessageBox(str2);
    MessageBox(str3);
View Code
 
 
六、CString和char*的相互转化
1.char*转化为CString
//char*和CString CString s1 = _T("Hello,World!");

    CString s2;
    s2.Format(_T("Hello,World!"));
View Code

2.CString转化为char*

<1>强制类型转换为 LPCTSTR

A.这是一种略微硬性的转换,有关"正确"的做法,人们在认识上还存在许多混乱,

正确的使用方法有很多,但错误的使用方法可能与正确的使用方法一样多。

我们首先要了解 CString 是一种很特殊的 C++ 对象,它里面包含了三个值:
一个指向某个数据缓冲区的指针、一个是该缓冲中有效的字符记数以及一个缓冲区长度。
有效字符数的大小可以是从0到该缓冲最大长度值减1之间的任何数(因为字符串结尾有一个NULL字符)。
字符记数和缓冲区长度被巧妙隐藏。
除非你做一些特殊的操作,否则你不可能知道给CString对象分配的缓冲区的长度。
这样,即使你获得了该0缓冲的地址,你也无法更改其中的内容,不能截短字符串,也 绝对没有办法加长它的内容,否则第一时间就会看到溢出。
LPCTSTR 操作符(或者更明确地说就是 TCHAR * 操作符)在 CString 类中被重载了,该操作符的定义是返回缓冲区的地址,
因此,如果你需要一个指向 CString 的 字符串指针的话,可以这样做:
CString s("GrayCat");
LPCTSTR p = s;  //是指针常量,不可更改其值
它可以正确地运行。这是由C语言的强制类型转化规则实现的。当需要强制类型转化时,C++规则容许这种选择。
B.如果你要格式化字符串怎么办呢?
CString graycat("GrayCat");
CString s;
s.Format("Mew! I love %s",graycat);  //注意这里使用的是类对象而没有使用显示转换
注意由于在可变参数列表中的值(在函数说明中是以"..."表示的)并没有隐含一个强制类型转换操作符。你会得到什么结果呢?
一个令人惊讶的结果,我们得到的实际结果串是:
"Mew! I love GrayCat"。
因为 MFC 的设计者们在设计 CString数据类型时非常小心, CString 类型表达式求值后指向了字符串,
所以这里看不到任何象 Format 或 sprintf 中的强制类型转换,你仍然可以得到正确的行为。
描述 CString 的附加数据实际上在 CString 名义地址之后。(名即代表地址)
 
C.有一件事情你是不能做的,那就是修改字符串,因为返回的是指针常量。
<2>CString转化成char* 之二:使用 CString 对象的 GetBuffer 方法
A.如果你需要修改 CString 中的内容,它有一个特殊的方法可以使用,那就是 GetBuffer,
它的作用是返回一个可写的缓冲指针
如果你只是打算修改字符或者截短字符串,你完全可以这样做:
CString s(_T("File.ext"));
LPTSTR p = s.GetBuffer();
LPTSTR dot = strchr(p,.); // OK,should have used s.Find...
if(p != NULL)
*p = _T(\0);
s.ReleaseBuffer();
这是 GetBuffer 的第一种用法,也是最简单的一种,
不用给它传递参数,它使用默认值 0,意思是:"给我这个字符串的指针,我保证不加长它"。
当你调用 ReleaseBuffer 时,字符串的实际长度会被重新计算,然后存入 CString 对象中。
 
B.必须强调一点,在 GetBuffer 和 ReleaseBuffer 之间这个范围,一定不能使用你要操作的这个缓冲的 CString 对象的任何方法。
因为 ReleaseBuffer 被调用之前,该 CString 对象的完整性得不到保障。研究以下代码:
CString s(...);
LPTSTR p = s.GetBuffer();
//... 这个指针p 发生了很多事情
int n = s.GetLength(); // 很糟D!!!!! 有可能给出错误的答案!!!
s.TrimRight(); // 很糟!!!!! 不能保证能正常工作!!!!
s.ReleaseBuffer(); // 应该 OK
int m = s.GetLength(); // 这个结果可以保证是正确的。
s.TrimRight(); // 将正常工作。
C.假设你想增加字符串的长度,你首先要知道这个字符串可能会有多长,好比是声明字符串数组的时候用:
char buffer[1024];
表示 1024 个字符空间足以让你做任何想做得事情。
在 CString 中与之意义相等的表示法:
LPTSTR p = s.GetBuffer(1024);
调用这个函数后,你不仅获得了字符缓冲区指针,而且同时还获得了长度至少为 1024 个字符的空间
(注意,我说的是"字符",而不是"字节",因为 CString 是以隐含方式感知 Unicode 的)。
D.同时,还应该注意的是,如果你有一个常量串指针,这个串本身的值被存储在只读内存中,
如果试图存储它,即使你已经调用了 GetBuffer ,并获得一个只读内存的指针,存入操作会失败,并报告存取错误。
我没有在 CString 上证明这一点,但我看到过大把的 C 程序员经常犯这个错误。
E.C 程序员有一个通病是分配一个固定长度的缓冲,对它进行 sprintf 操作,然后将它赋值给一个 CString:
char buffer[256];
sprintf(buffer,"%......",args,...); // ... 部分省略许多细节
CString s = buffer;
虽然更好的形式可以这么做:
CString s;
s.Format(_T("%...."),args,...);
如果你的字符串长度万一超过 256 个字符的时候,不会破坏堆栈
另外一个常见的错误是:既然固定大小的内存不工作,那么就采用动态分配字节,这种做法弊端更大:
int len = lstrlen(parm1) + 13 lstrlen(parm2) + 10 + 100;
char * buffer = new char[len];
sprintf(buffer,"%s is equal to %s,valid data",parm1,parm2);
CString s = buffer;
......
delete [] buffer;
它可以能被简单地写成:
CString s;
s.Format(_T("%s is equal to %s,valid data"),parm1,parm2);
需要注意 sprintf 例子都不是 Unicode 就绪的,尽管你可以使用 tsprintf 以及用 _T() 来包围格式化字符串,
但是基本 思路仍然是在走弯路,这这样很容易出错。

 

参考:百度百科

关闭
用手机扫描二维码关闭
二维码