windows 字符串
一. ANSI窄字节和Unicode宽字节
- 概述:在日常的软件开发过程中,会时不时地去处理不同编码格式的字符串,特别是在处理文件路径的相关场景中,比如我们要通过路径去读写文件、通过路径去加载库文件等。常见的字符编码格式有ANSI窄字节编码、Unicode宽字节编码以及UTF8可变长编码。在Linux系统中,主要使用UTF8编码;在Windows系统中,既支持ANSI编码,也支持Unicode编码。
- 产生原因:在windows编码中,本来只有ANSI,用1个字节存放1个字符,但是,汉字和全角字符用1个字节存放不下,为此,微软推出了Unicode字节,用2个字节来存放字符。
- ANSI窄字节编码:ANSI编码是不同语种下的字符编码,比如GB2312字符编码就是简体中文的本地编码。
ANSI编码是个本地范畴,只适用于对应的语种,每个字符的编码不是全球唯一的编码,只在对应的语种中有效。对于中文GB2312编码的字符串,如果当成英文的ANSI编码来解析,则结果会是乱码!
但是对于大小写英文字母和数字的ANSI编码,是字符ASCII码,英文字母和数字的ACSII码是全球统一的,比如大写字母A的ASCII码是65(十六进制是41H),数字0的ASCII码是48(十六进制是30H)。所以在所有语种中,大小写字母及数字的ANSI编码,都是能识别的。不同语种下的本地文字字符,一般是不能相互识别的。
使用中文ANSI编码的字符串开发的程序(代码中使用的都是中文字符串,使用的是ANSI窄字节编码),拿到俄文操作系统中可能显示的都是乱码,因为在俄文的ANSI编码中只识别俄文的ANSI编码出来的字符串,无法识别中文ANSI编码的字符串。这里主要有两类字符乱码问题,一是UI界面上显示的文字是乱码;二是使用路径去创建文件或访问文件时会因为路径中的字符是乱码,导致文件创建或访问失败。
- Unicode宽字节编码:
Unicode编码是全球统一的字符编码,每个语种下的每个字符的编码值都是全球唯一的,即在Unicode编码集中可以识别每个语种下的所有字符。所以为了实现软件对多语种(多国语言)的支持,我们在开发软件时要选择Unicode字符编码,使用Unicode编码的字符串,调用Unicode版本的API。
系统在提供包含字符串参数的API时,都会提供两个版本,一个是ANSI版本的,一个是Unicode版本的,主要体现在对字符串编码的处理上,比如SetWindowTextA(ANSI版本)和SetWindowTextW(Wide宽字节Unicode版本)。我们可以直接调用W版本API,但一般我们调用API时,我们不指定调用哪个版本,是通过设置工程属性中的编码格式来确定使用哪个版本:
#ifdef _UNICODE#define SetWindowText SetWindowTextW#else#define SetWindowText SetWindowTextA#endif// !UNICODE
在Unicode编码中,每个字符都占两个字节。对于大小写字母和数字,当他们出现在字符串中时,对应的内存中存放的是它们的ASCII码值,只占一个字节,在Unicode 2字节编码中,高位将填充0。
- 代码体现:宽字节与窄字节在表示字符串的区别在于:宽字节表示字符串需在字符串前面加上"L"。
1 //ANSI窄字节 2 CHAR cData1 = 'a';//char 3 PCHAR pcData2 = "abc";//char* 4 PSTR pstrData3 = "def";//char* 5 LPSTR lpData4 = "ghi";//char* 6 LPCSTR lpcData5 = "okz"; //const char* 7 printf("ANSI: %d \n", (strlen(lpcData5) + 1) * sizeof (CHAR));//4 8 9 //Unicode宽字节 10 WCHAR cData6 = 'a'; //wchar_t 11 PWCHAR pcData7 = L"abc";//wchar_t* 12 PWSTR pstrData8 = L"def";//wchar_t* 13 LPWSTR lpData9 = L"ghi";//wchar_t* 14 LPCWSTR lpcData10 = L"okz";//const wchar_t* 15 printf("Unicode: %d \n", (wcslen(lpcData10) + 1) * sizeof (WCHAR));//8 16 17 //通用字节:根据项目-->字符集:多字节/Unicode自动切换 18 TCHAR cData11 = 'a';// char or wchar_t 19 //PTCHAR pcData12 = _T("abc");// char* or wchar_t* _TCHAR_DEFINED未定义时可使用 20 PTSTR pstData13 = _T("def");// char* or wchar_t* 21 LPTSTR lpData14 = _T("ghi");// char* or wchar_t* 22 LPCTSTR lpcData15 = _T("okz");//const char* or const wchar_t* 23 printf("ANSI OR Unicode: %d \n", (_tcslen(lpcData15) + 1) * sizeof (TCHAR));//字符集为Unicode:8
- 相互转换:为什么要转换?这里对于这一块讲的算比较透彻:VS2010与VS2013中的多字节编码与Unicode编码问题
- 工程字符集设置:在对存储要求非常高的时候,或要兼容C的代码时(偏底层开发),或做科研软件开发,则推荐采用多字节编码,因为,这是一种以char为基础的多字节编码方案,会兼容其他开源库的使用,我们才会使用多字节的方式 。在商业软件应用中,一般采用Unicode编码方法,一般推荐使用Unicode的方式,因为它可以适应各个国家语言,在进行软件国际时将会非常便利。
1. 方式1:使用 WideCharToMultiByte 和 MultiByteToWideChar 实现
1 //代码页-----CP_ACP:C++代码用的 UTF-8:网页用的 2 /*=====================wchar_t*===>char*============================*/ 3 char* w2a(wchar_t* lpszSrc, UINT CodePage = CP_ACP) 4 { 5 if (lpszSrc != nullptr) 6 { 7 int nANSILen = WideCharToMultiByte(CodePage, 0, lpszSrc, -1, nullptr, 0, nullptr, nullptr); 8 char* pANSI = new char[nANSILen + 1]; 9 if (pANSI != nullptr) 10 { 11 ZeroMemory(pANSI, nANSILen + 1); 12 WideCharToMultiByte(CodePage, 0, lpszSrc, -1, pANSI, nANSILen, nullptr, nullptr); 13 return pANSI; 14 } 15 } 16 return nullptr; 17 } 18 /*=====================char*===>wchar_t*============================*/ 19 wchar_t* a2w(char* lpszSrc, UINT CodePage = CP_ACP) 20 { 21 if (lpszSrc != nullptr) 22 { 23 int nUnicodeLen = MultiByteToWideChar(CodePage, 0, lpszSrc, -1, nullptr, 0); 24 LPWSTR pUnicode = new WCHAR[nUnicodeLen + 1]; 25 if (pUnicode != nullptr) 26 { 27 ZeroMemory((void*)pUnicode, (nUnicodeLen + 1) * sizeof(WCHAR)); 28 MultiByteToWideChar(CodePage, 0, lpszSrc, -1, pUnicode, nUnicodeLen); 29 return pUnicode; 30 } 31 } 32 return nullptr; 33 } 34 35 36 ... ... 37 38 char* pcTemp1 = w2a(pcData7);//Unicode-->ANSI 39 printf("Unicode to ANSI: %d \n", (strlen(pcTemp1) + 1) * sizeof (CHAR));//4 40 wchar_t* pwcTemp2 = a2w(pcData2);//ANSI-->Unicode 41 printf("ANSI to Unicode: %d \n", (wcslen(pwcTemp2) + 1) * sizeof (WCHAR));//8 42 43 if (nullptr != pcTemp1) delete pcTemp1; 44 if (nullptr != pwcTemp2) delete pwcTemp2;
2. 方式2:使用 ATL的CW2A和CA2W 实现 #include "atlbase.h" #include "atlstr.h"
1 ATL::CW2A objW2A(pcData7);//Unicode-->ANSI 2 char* pcTemp3 = (char*)objW2A; 3 printf("ATL Unicode to ANSI: %d \n", (strlen(pcTemp3) + 1) * sizeof (CHAR));//4 4 5 ATL::CA2W objA2W(pcData2);//ANSI-->Unicode 6 wchar_t* pcTemp4 = (wchar_t*)objA2W; 7 printf("ATL ANSI to Unicode: %d \n", (wcslen(pcTemp4) + 1) * sizeof (WCHAR));//8
二. 常见的字符串符号
名称 | ANSI | UNICODE |
TCHAR | char | wchar_t |
LPSTR | char* | |
LPCSTR | const char* | |
LPTSTR | char* | wchar_t* |
LPCTSTR | const char* | const wchar_t* |
三. 常用字符串函数(通用函数,ANSI,UINCODE都可使用)
- 工程项目字符集:测试的工程字符集设置为:Unicode 请注意!!!!
- _tcslen:求字符串的字符长度,即字符串的字符个数,并且不含结束符'\0';而sizeof得到的是字符串大小 ,即在内存上所占的空间大小 :字符串大小 = 字符个数(包含结束符‘\0’) * 单个字符所占的字节数.注:sizeof字符指针得到的是指针大小,所以字符串大小一般使用_tcslen函数。
#ifdef _UNICODE #define _tcslen wcslen #else #define _tcslen strlen #endif// !UNICODE
1 #include "stdafx.h" 2 #include "windows.h" 3 4 int _tmain(int argc, _TCHAR* argv[]) 5 { 6 LPCTSTR lpStr = _T("abcdedfg"); 7 TCHAR acData[] = _T("abcdefg"); 8 9 _tprintf(_T("pcData: %d\n"), sizeof(lpStr)); //4 10 _tprintf(_T("lpstr size: %d \n"), sizeof(_T("abcdedfg"))); //18 11 _tprintf(_T("lpstr len: %d \n"), (_tcslen(lpStr) + 1) * sizeof (TCHAR));//18 12 _tprintf(_T("acData buf len: strlen: %d sizeof: %d \n"), (_tcslen(acData) + 1) * sizeof (TCHAR), sizeof (acData));//16 16 13 14 return 0; 15 }
- _tcscpy_s:拷贝字符串,注:第二个参数numberOfElements为元素个数即字符个数,不是字符串长度,后面含此参数的函数均是此含义,不再进行说明!对此有异议者,可以用Unicode字符集进行测试!
- _tcsncpy_s:拷贝指定长度的字符串,注:printf是顺序打印,windows是小端存储,unicode两字节表示一个汉字,所以打印出错,可以setlocale本地化处理后,正常打印出汉字。
#ifdef _UNICODE #define _tcscpy wcscpy_s #else #define _tcslen strcpy_s #endif// !UNICODE
1 #include "stdafx.h" 2 #include "windows.h" 3 #include <locale.h> 4 5 int _tmain(int argc, _TCHAR* argv[]) 6 { 7 TCHAR acBuf[100] = {0}; 8 LPCTSTR lpStr = _T("我们还是我们"); 9 setlocale(LC_ALL, "chs");//本地化处理,否则显示不了汉字 10 _tcscpy_s(acBuf, sizeof(acBuf) / sizeof (acBuf[0]), lpStr); 11 _tprintf(_T("acBuf info: %s\n"), acBuf);//我们还是我们 12 13 return 0; 14 }
#ifdef _UNICODE #define _tcsncpy wcsncpy_s #else #define _tcsnlen strncpy_s #endif// !UNICODE
1 #include "stdafx.h" 2 #include "windows.h" 3 #include <locale.h> 4 5 int _tmain(int argc, _TCHAR* argv[]) 6 { 7 TCHAR acBuf[100] = {0}; 8 LPCTSTR lpStr = _T("我们还是我们"); 9 setlocale(LC_ALL, "chs");//本地化处理,否则显示不了汉字 10 _tcsncpy_s(acBuf, lpStr, 4); 11 _tprintf(_T("acBuf info: %s\n"), acBuf);//我们还是 12 13 return 0; 14 }
- _tcscat_s:字符串拼接。注:第二个参数:这里表示用来存放拼接字符串的缓冲区大小,源字符串会在目标字符串的"\0"处进行拼接。
- _stprintf_s: 将多个变参按一定的格式保存在指定的buf里。 注:这里最后的参数:const char *format [,argument] ...
#ifdef _UNICODE #define _tcscat_s wcscat_s #else #define _tcscat_s strcat_s #endif// !UNICODE
1 #include "stdafx.h" 2 #include "windows.h" 3 4 int _tmain(int argc, _TCHAR* argv[]) 5 { 6 TCHAR acStr2[100] = _T("abcd"); 7 LPTSTR lpStr3 = _T("efgk"); 8 _tcscat_s(acStr2, 10, lpStr3); 9 _tprintf(_T("char cat: %s \n"), acStr2);//adcdefgk 10 11 return 0; 12 }
#ifdef _UNICODE #define _stprinf_s swprintf_s #else #define _stprintf_s sprintf_s #endif// !UNICODE
1 #include "stdafx.h" 2 #include "windows.h" 3 #include <locale.h> 4 5 int _tmain(int argc, _TCHAR* argv[]) 6 { 7 TCHAR acBuf[100] = {0}; 8 LPCTSTR lpStr1 = _T("我们AA"); 9 LPCTSTR lpStr2 = _T("BB你们"); 10 setlocale(LC_ALL, "chs");//本地化处理,否则显示不了汉字 11 //将字符串打印到缓冲区 12 _stprintf_s(acBuf, _T("%s"), lpStr1); 13 _tprintf_s(_T("acBuf info: %s\n"), acBuf);//我们AA 14 15 //实现拼接_tcscat_s的效果 16 memset(acBuf, sizeof(acBuf), 0); 17 _stprintf_s(acBuf, _T("%s%s"), lpStr1, lpStr2); 18 _tprintf(_T("acBuf info: %s\n"), acBuf);//我们AABB你们 19 20 return 0; 21 }
- _tcscmp: 字符串比较。注:返回值相同等于0,差值大于0则返回1, 差值小于0则返回-1
- _tcsncmp:比较两个字符串的前几个字符,返回值相同等于0,差值大于0返回正数,差值小于0返回负数。
#ifdef _UNICODE #define _tcscmp wcscmp #else #define _tcscmp strcmp #endif// !UNICODE
1 #include "stdafx.h" 2 #include "windows.h" 3 4 int _tmain(int argc, _TCHAR* argv[]) 5 { 6 LPCTSTR lpStr1 = _T("abcdef"); 7 LPCTSTR lpStr2 = _T("abcdef"); 8 LPCTSTR lpStr3 = _T("abcde"); 9 10 _tprintf(_T("lpStr1 = lpStr2: %d\n"), _tcscmp(lpStr1, lpStr2));//0 11 _tprintf(_T("lpStr2 > lpStr3: %d\n"), _tcscmp(lpStr2, lpStr3));//1 12 _tprintf(_T("lpStr3 < lpStr2: %d\n"), _tcscmp(lpStr3, lpStr2));//-1 13 14 return 0; 15 }
#ifdef _UNICODE #define _tcsncmp wcsncmp #else #define _tcslen strncmp #endif// !UNICODE
1 #include "stdafx.h" 2 #include "windows.h" 3 4 int _tmain(int argc, _TCHAR* argv[]) 5 { 6 LPCTSTR lpStr1 = _T("abcdef"); 7 LPCTSTR lpStr2 = _T("abcdef"); 8 LPCTSTR lpStr3 = _T("abcde"); 9 10 _tprintf(_T("ncmp 5 lpStr2 = lpStr3: %d\n"), _tcsncmp(lpStr2, lpStr3, 5));//0 11 _tprintf(_T("ncmp 6 lpStr2 = lpStr3: %d\n"), _tcsncmp(lpStr2, lpStr3, 6));//102 12 _tprintf(_T("ncmp 6 lpStr3 = lpStr2: %d\n"), _tcsncmp(lpStr3, lpStr2, 6));//-102 13 14 return 0; 15 }
- _tcslwr_s:将大写转换成小写。注:当变量为字符数组时,第二个参数由函数自己去计算,不需要去填充。当为其他变量时,则需要用户自己填充, 但会报错,好像只能为“TCHAR acStr[]”服务。
- _tcsupr_s:将小写转换成大写。注意事项与“_tcslwr_s“相似。
#ifdef _UNICODE #define _tcslwr_s wcslwr_s #else #define _tcslwr_s strlwr_s #endif// !UNICODE
1 #include "stdafx.h" 2 #include "windows.h" 3 4 int _tmain(int argc, _TCHAR* argv[]) 5 { 6 //只能使用该变量类型 7 TCHAR acStr[]= _T("ABCdeFG"); 8 _tcslwr_s(acStr); 9 _tprintf(_T("acStr: %s \n"), acStr);//abcdefg 10 11 LPTSTR lpStr = _T("ABCDEE"); 12 //_tcslwr_s(lpStr); //这样会报错,但是如果自己填充参数则会报错。 13 14 return 0; 15 }
#ifdef _UNICODE #define _tcsupr_s wcsupr_s #else #define _tcsupr_s strupr_s #endif// !UNICODE
1 #include "stdafx.h" 2 #include "windows.h" 3 4 int _tmain(int argc, _TCHAR* argv[]) 5 { 6 //只能使用该变量类型 7 TCHAR acStr[]= _T("abcdEFG"); 8 _tcsupr_s(acStr); 9 _tprintf(_T("acStr: %s \n"), acStr);//ABCDEFG 10 11 LPTSTR lpStr = _T("ABCDEE"); 12 //_tcsupr_s(lpStr); //这样会报错,但是如果自己填充参数则会报错。 13 14 return 0; 15 }
- _tsplitpath_s: 路径分割:注:可以分割出文件所在磁盘,目录,文件名及文件后缀名。
#ifdef _UNICODE #define _tsplitpath_s wsplitpath_s #else #define _tsplitpath_s splitpath_s #endif// !UNICODE
1 #include "stdafx.h" 2 #include "windows.h" 3 4 int _tmain(int argc, _TCHAR* argv[]) 5 { 6 LPCTSTR lpFilePath = _T("C:\\windows\\software\\a.text"); 7 TCHAR acDrive[MAX_PATH] = { 0 }; 8 TCHAR acDirectory[MAX_PATH] = { 0 }; 9 TCHAR acFileName[MAX_PATH] = { 0 }; 10 TCHAR acExtName[_MAX_EXT] = { 0 }; 11 _tsplitpath_s(lpFilePath, acDrive, MAX_PATH, acDirectory, MAX_PATH, acFileName, MAX_PATH, acExtName, _MAX_EXT); 12 _tprintf(_T("drive: %s directory: %s filename: %s extname: %s \n"), acDrive, acDirectory, acFileName, acExtName); 13 //drive: c: directory: \windows\software\ filename: a extname: .text 14 15 return 0; 16 }
四. string的一些用法
-
起源:string其实就是STL,但是又早于STL(STL的六大组件如下图,详细信息请参照《effective STL》),这就导致string的很多接口设计时没法参考标准,这就使得string的很多接口设计地有些冗余,如果你的项目里需要使用string大量地操作字符串,这里有别人封装过的代码。
- 创建方式:注:ANSI里std::string,而在Unicode工程里std::wstring,这里没有"_t"来自适应工程字符集。
1 #include "stdafx.h" 2 #include "windows.h" 3 #include <string> 4 #include <iostream> 5 using namespace std; 6 7 int _tmain(int argc, _TCHAR* argv[]) 8 { 9 10 string strData; 11 12 string strData1("adcd"); 13 cout << strData1 << endl; //abcd 14 15 string strData2 = "abcdefg"; 16 cout << strData2 << endl; //abcdefg 17 18 //拷贝构造 19 string strData3(strData1); 20 cout << strData3 << endl; // abcd 21 string strData4(strData2, 1, 6); // bcdefg 22 cout << strData4 << endl; 23 24 //拼接 25 string strData5 = strData1 + strData2; 26 cout << strData5 << endl; // abcdabcdefg 27 28 //+= 29 strData3 += "AAAAA"; 30 cout << strData3 << endl; // abcdAAAAA 31 32 return 0; 33 }
- 遍历方法:正向迭代器,反向迭代器,at()方法,STL for方法。注:如果没有特殊环境的要求,建议使用STL for方法。
1 #include "stdafx.h" 2 #include "windows.h" 3 #include <string> 4 #include <iostream> 5 using namespace std; 6 7 int _tmain(int argc, _TCHAR* argv[]) 8 { 9 string strData = "abcdefg"; 10 11 //正向迭代器 12 for (string::iterator iter = strData.begin(); iter != strData.end(); iter++) 13 { 14 cout << *iter << " "; // a b c d e f g 15 } 16 cout << endl; 17 18 //反向迭代器 19 for (string::reverse_iterator iter = strData.rbegin(); iter != strData.rend(); iter++) 20 { 21 cout << *iter << " "; // g f e d c b a 22 } 23 cout << endl; 24 25 //at() 26 for (size_t uiCount = 0; uiCount < strData.size(); uiCount++)\ 27 { 28 cout << strData.at(uiCount) << " "; // a b c d e f g 29 } 30 cout << endl; 31 32 //STL for特性 33 for (auto atInfo : strData) //如果要对内容进行修改,auto & 34 { 35 cout << atInfo << " "; // a b c d e f g 36 } 37 cout << endl; 38 39 return 0; 40 }
- 容量
1. size() : 字符串有效字符串的个数,不包含'\0'。
1 #include "stdafx.h" 2 #include "windows.h" 3 #include <string> 4 #include <iostream> 5 using namespace std; 6 7 int _tmain(int argc, _TCHAR* argv[]) 8 { 9 string strData = "abcdefg"; 10 11 cout << strData.size() << endl; //7 12 13 return 0; 14 }
2. resize():重新调整容器的大小。
1 #include "stdafx.h" 2 #include "windows.h" 3 #include <string> 4 #include <iostream> 5 using namespace std; 6 7 int _tmain(int argc, _TCHAR* argv[]) 8 { 9 string strData = "abcdefg"; 10 11 //当扩展容量小于size()时,直接截取 12 strData.resize(4); 13 cout << strData << endl; // abcd 14 15 //当扩展容量大于size()时,在后面添加'\0' 16 strData.resize(10); 17 cout << strData << endl; // abcd 18 19 //当扩展容量大于size()时,在后面添加'A' 20 strData.resize(15, 'A'); 21 cout << strData << endl; // abcd AAAAA 22 23 return 0; 24 }
3. reserve():用来提前申请空间的,有些情况下使用reserve可以很好地避免频繁扩容。注:在某些地方有大用,具体请对照:为什么要用string.reserve()。
1 #include "stdafx.h" 2 #include "windows.h" 3 #include <string> 4 #include <iostream> 5 using namespace std; 6 7 int _tmain(int argc, _TCHAR* argv[]) 8 { 9 string strData = "abcdefg"; 10 strData.reserve(50); 11 strData += "DDDDD"; 12 cout << strData << endl;//abcdefgDDDDD 13 14 return 0; 15 }
4. capacity():用来计算当前字符串的容量,最多能够存放的有效字符的个数,不常用。
5. empty():用于判断字符串是否为空,为空返回1,不为空返回0。
1 #include "stdafx.h" 2 #include "windows.h" 3 #include <string> 4 #include <iostream> 5 using namespace std; 6 7 int _tmain(int argc, _TCHAR* argv[]) 8 { 9 string strData = ""; 10 cout << strData.empty() << endl; // 1 11 12 string strData1 = "abcdefg"; 13 cout << strData1.empty() << endl;// 0 14 15 return 0; 16 }
6. clear():用来清空字符串,size()大小为0.
1 #include "stdafx.h" 2 #include "windows.h" 3 #include <string> 4 #include <iostream> 5 using namespace std; 6 7 int _tmain(int argc, _TCHAR* argv[]) 8 { 9 string strData = "abcdefg"; 10 cout << strData.size() << endl;// 7 11 12 strData.clear(); 13 cout << strData << endl; // 14 cout << strData.size() << endl;// 0 15 16 return 0; 17 }
- 操作
1. push_back():用于从尾部插入单个字符.
1 #include "stdafx.h" 2 #include "windows.h" 3 #include <string> 4 #include <iostream> 5 using namespace std; 6 7 int _tmain(int argc, _TCHAR* argv[]) 8 { 9 string strData = "abcdefg"; 10 11 strData.push_back('A'); 12 cout << strData << endl; //abcdefgA 13 14 strData.push_back('B'); 15 cout << strData << endl; //abcdefgAB 16 17 return 0; 18 }
2. append():用于从尾部插入单个或多个字符,也可以直接插入字符串
1 #include "stdafx.h" 2 #include "windows.h" 3 #include <string> 4 #include <iostream> 5 using namespace std; 6 7 int _tmain(int argc, _TCHAR* argv[]) 8 { 9 string strData = "abcdefg"; 10 string strData1 = "AABBCC"; 11 12 //从尾部插入字符串 13 strData.append("D"); 14 cout << strData << endl; // abcdefgD 15 strData.append(strData1); 16 cout << strData << endl; // abcdefgDAABBCC 17 18 //从尾部插入3个'Q' 19 strData.append(3, 'Q'); 20 cout << strData << endl; // abcdefgDAABBCCQQQ 21 22 //从strData1的下标2的位置开始复制4个字母插入strData 23 strData.append(strData1, 2, 4); // abcdefgDAABBCCQQQBBCC 24 cout << strData << endl; 25 26 return 0; 27 }
3. assign():其本质就是赋值。
1 #include "stdafx.h" 2 #include "windows.h" 3 #include <string> 4 #include <iostream> 5 using namespace std; 6 7 int _tmain(int argc, _TCHAR* argv[]) 8 { 9 string strData = "abcdefg"; 10 string strData1 = "AABBCC"; 11 12 //常量赋值 13 strData.assign("DD"); 14 cout << strData << endl; // DD 15 16 //常量赋值:赋值N个字符 17 strData.assign(4, 'Q'); 18 cout << strData << endl; // QQQQ 19 20 //常量赋值:将字符串的前N个字符串复制到strData; 21 strData.assign("ABCDEF", 4); 22 cout << strData << endl; // ABCD 23 24 //子字符串赋值 25 strData.assign(strData1); 26 cout << strData << endl; // AABBCC 27 28 //子字符串赋值:把strData1下标2开始的4个字符复制到strData; 29 strData.assign(strData1, 1, 4); 30 cout << strData << endl; // ABBC 31 32 return 0; 33 }
4. insert():在源字符串的某个位置插入字符或字符串。
1 #include "stdafx.h" 2 #include "windows.h" 3 #include <string> 4 #include <iostream> 5 using namespace std; 6 7 int _tmain(int argc, _TCHAR* argv[]) 8 { 9 string strData = "abcdefg"; 10 string strData1 = "AABBCC"; 11 12 //插入字符 13 strData.insert(2, 3, 'Q'); //QQQ 14 cout << strData << endl; //abQQQcdefg 15 strData.insert(strData.begin(), 'E'); //E 16 cout << strData << endl; //EabQQQcdefg 17 strData.insert(strData.begin(), 2, 'R');//RR 18 cout << strData << endl; //RREabQQQcdefg 19 20 //插入字符 21 strData.insert(3, "hello"); //hello 22 cout << strData << endl; //RREhelloabQQQcdefg 23 strData.insert(5, "word", 2); //wo 24 cout << strData << endl; //RREhewolloabQQQcdefg 25 strData.insert(2, strData1, 1, 4); //ABBC 26 cout << strData << endl; //RRABBCEhewolloabQQQcdefg 27 28 return 0; 29 }
5. erase():删除指定位置的一个或若干字符串。
1 #include "stdafx.h" 2 #include "windows.h" 3 #include <string> 4 #include <iostream> 5 using namespace std; 6 7 int _tmain(int argc, _TCHAR* argv[]) 8 { 9 string strData = "abcdefg"; 10 11 strData.erase(5); 12 cout << strData << endl; // abcde 13 14 strData.erase(0, 2); 15 cout << strData << endl; // cde 16 17 strData.erase(); 18 cout << strData << endl; // 19 20 return 0; 21 }
6. replace():用新的字符或字符串代替源字符某个位置开始的字符或字符串。
1 #include "stdafx.h" 2 #include "windows.h" 3 #include <string> 4 #include <iostream> 5 using namespace std; 6 7 int _tmain(int argc, _TCHAR* argv[]) 8 { 9 string strData = "abcd"; 10 11 //插入字符 12 strData.replace(2, 4, 2, '*'); 13 cout << strData << endl; // ab** 14 15 //插入字符串 16 strData.replace(2, 4, "===="); 17 cout << strData << endl; // ab==== 18 strData.replace(2, 4, "&&%%", 4); 19 cout << strData << endl; // ab&&%% 20 strData.replace(2, 4, "--**^^@@", 3, 5); 21 cout << strData << endl; // ab*^^@@ 22 23 return 0; 24 }
7. 查找:find, rfind, find_first_of, find_first_not_of , find_last_of, find_last_not_of。
1 #include "stdafx.h" 2 #include "windows.h" 3 #include <string> 4 #include <iostream> 5 using namespace std; 6 7 int _tmain(int argc, _TCHAR* argv[]) 8 { 9 size_t uiPos; 10 string strData = "abcdegfgc"; 11 string strDst = "cg"; 12 13 //find: 找到返回源字符串的下标,没有找到则返回-1; 14 uiPos = strData.find('c'); 15 cout << uiPos << endl; // 2 16 uiPos = strData.find('v'); 17 cout << uiPos << endl; // -1 18 uiPos = strData.find("cde", 1); 19 cout << uiPos << endl; // 2 20 21 //rfind: 从右向左遍历,返回的还是源字符的下标 22 uiPos = strData.rfind('c'); 23 cout << uiPos << endl; //8 24 uiPos = strData.rfind("abc", -2); 25 cout << uiPos << endl; //0 26 27 //find_first_of:从左向右查找指定字符串中的所有字符在源字符串中的位置 28 for (size_t uiPos = strData.find_first_of(strDst); uiPos != string::npos; uiPos = strData.find_first_of(strDst, uiPos + 1)) 29 { 30 strData.at(uiPos) = '='; 31 } 32 cout << strData << endl; //ab=de=f== 33 34 //find_last_of : 从右向左查找指定字符串中的所有字符在源字符串中的位置 35 //find_first_not_of : 从左向右查找指定字符串中所有字符与源字符串中的字符不相同的字符位置 36 //find_last_of : 从右向左查找指定字符串中所有字符与源字符串中的字符不相同的字符位置 37 38 return 0; 39 }
8. substr():用于获取子串,返回获取到的子串。
1 #include "stdafx.h" 2 #include "windows.h" 3 #include <string> 4 #include <iostream> 5 using namespace std; 6 7 int _tmain(int argc, _TCHAR* argv[]) 8 { 9 string strData = "abcdefg"; 10 11 string strData1 = strData.substr(); 12 cout << strData1 << endl; // abcdefg 13 string strData2 = strData.substr(1); 14 cout << strData2 << endl; // bcdefg 15 string strData3 = strData.substr(1, 5); 16 cout << strData3 << endl; // bcdef 17 18 return 0; 19 }
五. CString的一些用法
- 特性:CString相对于string类封装的就完善了很多了,但它是MFC的类库并不是C++的标准类库,所以CString只能在MFC环境下使用,这也是它的局限性。CString是通用性字符串构建会自适应ANSI和Unicode字符集,CString(typedef CStringT> CString)为Visual C++中最常用的字符串类,与string类不同并不具备STL特性,继承自CSimpleStringT类,主要应用在MFC和ATL编程中。
- 构建:与string类差不多。注:由于工程是建立在MFC工程里,下面的代码展示只能截取在某个函数里,打印信息则放在Edit类里,代码如下:
1 void CCharTestDlg::PrintEdit(CString strInfo) 2 { 3 strInfo += "\r\n"; 4 size_t ulTextLen = m_objEditOut.GetWindowTextLength(); 5 m_objEditOut.SetSel(ulTextLen, ulTextLen, TRUE); 6 m_objEditOut.ReplaceSel(strInfo); 7 strInfo.Empty(); 8 }
1 void CCharTestDlg::Init() 2 { 3 CString strData = _T("abcd"); 4 PrintEdit(strData); // abcd 5 6 //拷贝构造 7 CString strData1(strData); 8 PrintEdit(strData1); // abcd 9 10 // 11 CString strData2(_T("cdefg"), 3); 12 PrintEdit(strData2); // cde 13 14 CString strData3('A', 4); 15 PrintEdit(strData3); // AAAA 16 17 //拼接 18 CString strData4 = strData + strData1; 19 PrintEdit(strData4); //abcdabcd 20 21 //+= 22 strData4 += _T("BBB"); 23 PrintEdit(strData4); //abcdabcdBBBB 24 25 //加载字符串资源 26 CString strData5; 27 strData5.LoadString(IDS_ABOUTBOX); 28 PrintEdit(strData5); // 关于CharTest(&A)... 29 }
- 遍历:CString类不具备STL特性,对比string类来说,遍历方法比较单一,"[]"与"GetAt()"。
1 void CCharTestDlg::Init() 2 { 3 CString strData = _T("abcd"); 4 5 //[] 6 CString strData1; 7 for (int iNum = 0; iNum < strData.GetLength(); iNum++) 8 { 9 strData1 += strData[iNum]; 10 strData1 += _T(" "); 11 } 12 PrintEdit(strData1); // a b c d 13 14 //GetAt() 15 CString strData2; 16 for (int iNum = 0; iNum < strData.GetLength(); iNum++) 17 { 18 strData2 += strData.GetAt(iNum); 19 strData2 += _T(" "); 20 } 21 PrintEdit(strData2); 22 }
- 容量:需要注意GetBuffer与ReleaseBuffer的使用。
1. GetLength():获取字符串大小,不包含'\0', 类似strlen.
1 void CCharTestDlg::Init() 2 { 3 CString strData; 4 CString strData1 = _T("abcdefg"); 5 6 //GetLength(): 获取字符串大小,不包含'\0',类似strlen 7 strData.Format(_T("str len: %d"), strData1.GetLength()); 8 PrintEdit(strData); // 7 9 }
2. Empty():清空字符串内容。IsEmpty():判断字符串是否为空,如果为空则返回1,不为空则返回0。
1 void CCharTestDlg::Init() 2 { 3 CString strData; 4 CString strData1 = _T("abcdefg"); 5 6 strData.Format(_T("str is empty: %d"), strData1.IsEmpty()); 7 PrintEdit(strData); // str is empty: 0 8 strData1.Empty(); 9 strData.Format(_T("str is empty: %d"), strData1.IsEmpty()); 10 PrintEdit(strData); // str is empty: 1 11 }
3. GetBuff():获取CString类的内部buffer指针,并锁定buffer的长度n,当n为0时,buffer的长度为当前字符串的长度。这是个比较容易出问题的函数,当GetBuffer后相应的CString方法均不可靠.eg,下面例子中GetBuffer中获取的长度仍旧是原来长度。
ReleaseBuffer():更新buffer长度.注:GetLength为实际字符串长度,并不是buffer长度。并解除buffer的长度锁定(+=,insert可变长),CString方法恢复可用。
(1) 两者的关系:当GetBuffer之后,需要继续对该CString进行操作时,必须ReleaseBuffer。
1 void CCharTestDlg::Init() 2 { 3 CString strData; 4 CString strData1 = _T("abcdefg"); 5 //GetLength获取的是字符串长度,并非CString类内部Buffer的长度,请注意区别 6 strData.Format(_T("str len: %d"), strData1.GetLength()); 7 PrintEdit(strData); // str len: 7 8 9 //GetBuffer:获取CString类的内部buffer指针,并锁定buffer的长度 10 LPTSTR lpBuf = strData1.GetBuffer(10); 11 //_tcscpy_s(lpBuf, 10, _T("eeeeeeeeeeeeee"));//超出设置的buffer长度后,直接报错 12 _tcscpy_s(lpBuf, 10, _T("eeeeeeeee")); //9个e 13 14 //当GetBuffer后,相应的CString方法均不可靠,eg,下面打印的数据长度还是之前的长度7,并不是扩展长度10 15 PrintEdit(strData1); //eeeeeee 7 16 strData.Format(_T("str len: %d"), strData1.GetLength()); 17 PrintEdit(strData); // str len: 7 18 19 //ReleaseBuffer:更新buffer长度,并解除buffer的长度锁定(+=,insert可变长),CString方法恢复可用。 20 strData1.ReleaseBuffer(); 21 PrintEdit(strData1); //eeeeeeeee 9 22 strData.Format(_T("str len: %d"), strData1.GetLength()); 23 PrintEdit(strData); //str len: 9 24 }
(2) 为何会用到GetBuffer:其实在MFC工程里我们基本都是使用CString类来操作字符串,但是我们知道CString类是MFC封装的类库,并不适用于C++的标准类库的函数参数接口,这就导致我们必须将CString类对象转换成char*或wchar_t*指针,而 GetBuffer返回的就是内部buffer指针(LPTSTR)。
- 获取:获取源字符串中的单个字符或子字符串。GetA, Letf, Right, Mid,用例如下:
1 void CCharTestDlg::Init() 2 { 3 CString strData; 4 CString strData1 = _T("abcdefg"); 5 6 //获取单个字符 7 strData.Format(_T("get char: %c"), strData1.GetAt(4)); 8 PrintEdit(strData); // get char: e 9 10 //left: 从左向右获取源字符串到下标N的子字符串 11 strData.Format(_T("left: %s"), strData1.Left(3)); 12 PrintEdit(strData); // left: abc 13 14 //right: 从右向左获取源字符串到下标N的子字符串 15 strData.Format(_T("right: %s"), strData1.Right(3)); 16 PrintEdit(strData); // right: efg 17 18 //mid: 从源字符串的获取下标M到下标N的子字符串 19 strData.Format(_T("mid: %s"), strData1.Mid(1)); 20 PrintEdit(strData); // mid: bcdefg 21 strData.Format(_T("mid: %s"), strData1.Mid(3, 7)); 22 PrintEdit(strData); // defg 23 }
- 查找:find查找字符或字符串,返回源字符串的下标,否则返回-1。ReserveFind,FindOneOf,SpanExcluding,SpanIncluding。
1 void CCharTestDlg::Init() 2 { 3 CString strData; 4 CString strData1 = _T("abcdefg"); 5 CString strData2 = _T("cd"); 6 CString strData3 = _T("DDD"); 7 8 //查找字符: find 9 strData.Format(_T("find: %d"), strData1.Find('d')); 10 PrintEdit(strData); //find: 3 11 strData.Format(_T("find: %d"), strData1.Find('d', 5)); 12 PrintEdit(strData); //find: -1 13 14 //从右向左查找字符: ReserveFind 15 strData.Format(_T("reverse: %d"), strData1.ReverseFind('d')); 16 PrintEdit(strData); 17 18 //查找子字符串: Find 19 strData.Format(_T("find: %d"), strData1.Find(strData2)); 20 PrintEdit(strData); // find: 2 21 strData.Format(_T("find: %d"), strData1.Find(strData3, 2)); 22 PrintEdit(strData); // find: -1 23 24 //查找目标字符串中任意一个字符在源目标的位置:FindOneOf 25 strData.Format(_T("FindOneOf: %d"), strData1.FindOneOf(_T("fcg"))); // c 26 PrintEdit(strData); // FindOneOf: 2 27 28 //查找目标字符串与源字符串任意匹配的第一个字符之前的子串: SpanExcluding 29 strData.Format(_T("SpanExcluding: %s"), strData1.SpanExcluding(_T("fcg"))); // c 30 PrintEdit(strData); // SpanExcluding: ab 31 32 //查找目标字符串与源字符串任意字符不匹配的字符,并返回第一个不匹配字符之前的子串: SpanIncluding 33 strData.Format(_T("SpanIncluding: %s"), strData1.SpanIncluding(_T("abdc"))); 34 PrintEdit(strData); // SpanIncluding: abcd 35 }
- 大小写转换:MakeLower转小写,MakeUpper转大写,MakeReverse反转。
1 void CCharTestDlg::Init() 2 { 3 CString strData; 4 CString strData1 = _T("AbCdeFg"); 5 6 //MakeLower: 将字母全部转换成小写 7 strData.Format(_T("MakeLower: %s"), strData1.MakeLower()); 8 PrintEdit(strData); // abcdefg 9 10 //MakeUpper: 将字母全部转换成大写 11 strData.Format(_T("MakeUpper: %s"), strData1.MakeUpper()); 12 PrintEdit(strData); // ABCDEFG 13 14 //MakeReverse: 调换字符串顺序 15 strData.Format(_T("MakeReverse: %s"), strData1.MakeReverse()); 16 PrintEdit(strData); // GFEDCBA 17 }
- 插入和删除:SetAt,Insert,Remove,Delete。
1 void CCharTestDlg::Init() 2 { 3 CString strData; 4 CString strData1 = _T("AbCdeFg"); 5 6 //SetAt: 在源字符串的下标N处插入字符 7 strData1.SetAt(3, 'Q'); 8 PrintEdit(strData1); // AbCQeFg 9 10 //Insert: 返回插入后的对象长度 11 strData.Format(_T("Insert: %d"), strData1.Insert(3, 'W')); 12 PrintEdit(strData); // Insert: 8 13 PrintEdit(strData1);// AbCWQeFg 14 strData.Format(_T("Insert: %d"), strData1.Insert(6, _T("YYY"))); 15 PrintEdit(strData); // Insert: 11 16 PrintEdit(strData1);// AbCWQeYYYFg 17 18 //Remove:删除源字符串中指定的字符 19 strData.Format(_T("Remove: %d"), strData1.Remove('Y')); 20 PrintEdit(strData); // Remove: 3 21 PrintEdit(strData1);// AbCWQeFg 22 23 //Delete:删除源字符串从下标N开始的M个字符 24 strData.Format(_T("Delete: %d"), strData1.Delete(2, 5)); 25 PrintEdit(strData); // Delete: 3 26 PrintEdit(strData1);// Abg 27 }
- 替换:Replace返回替换字符或字符串的个数。
1 void CCharTestDlg::Init() 2 { 3 CString strData; 4 CString strData1 = _T("abcdebgabc"); 5 6 //Replace: 替换源字符串所有指定的字符 7 strData.Format(_T("Replace: %d"), strData1.Replace('b', 'p')); 8 PrintEdit(strData); // Replace: 3 9 PrintEdit(strData1); // apcdepgapc 10 11 //替换字符串 12 strData.Format(_T("Repalce: %d"), strData1.Replace(_T("apc"), _T("XYZ"))); 13 PrintEdit(strData); // Repalce: 2 14 PrintEdit(strData1); // XYZdepgXYZ 15 }
- 比较:Compare,CompareNoCase。
1 void CCharTestDlg::Init() 2 { 3 CString strData; 4 CString strData1 = _T("abcdefg"); 5 CString strData2 = _T("abcd"); 6 CString strData3 = _T("abcd"); 7 CString strData4 = _T("ABCD"); 8 9 //Compare: 注意返回值 10 strData.Format(_T("compare: %d"), strData1.Compare(strData2)); 11 PrintEdit(strData); // compare: 1 12 strData.Format(_T("compare: %d"), strData2.Compare(strData3)); 13 PrintEdit(strData); // compare: 0 14 strData.Format(_T("copare: %d"), strData2.Compare(strData1)); 15 PrintEdit(strData); // compare: -1 16 17 //CompareNoCase: 不区分大小写比较 18 strData.Format(_T("compare: %d"), strData3.Compare(strData4)); 19 PrintEdit(strData); // compare: 1 20 strData.Format(_T("compareNoCase: %d"), strData3.CompareNoCase(strData4)); 21 PrintEdit(strData); // compareNoCase: 0 22 }
- 格式化:Format方法与print参数类似,上面几乎每个例子里都有用到,这里就不用例子说明。
六. TCHAR*,_tstring,CString的转换
- 场景:CString只限定在MFC里使用,但封装性能好;string 只要符合C++标准可以跨平台使用,移植性好,相比CString封装性差。
- 通用性处理:这里先自定义_tstring:std::string ANSI 和 std::wstring UNICODE,方便作通用性转换。
#ifdef _UNICODE #define _tstring std::wstring #else #define _tstring std::string #endif// !UNICODE
- 转换:
1. CString与TCHAR* 转换
1 //CString-->TCHAR* 2 CString strData1 = _T("abcdef"); 3 TCHAR* pcTemp5 = strData1.GetBuffer(0); 4 5 //TCHAR*-->CString 6 TCHAR* pcData12 = _T("defgki"); 7 CString strTemp6(pcData12);
2. _tstring与TCHAR* 转换
1 //_tstring-->TCHAR* 2 _tstring strData3 = _T("defgki"); 3 const TCHAR* pcTemp8 = strData3.c_str(); 4 5 //TCHAR*-->_tstring 6 const TCHAR* pcData14 = _T("defgki"); 7 _tstring strData5(pcData14);
3. _tstring与 CString 转换
1 //CString-->_tstring 2 CString strData6 = _T("abcdef"); 3 _tstring strTemp9 = strData6.GetBuffer(); 4 5 //_tstring-->CString 6 _tstring strData7 = _T("abcdef"); 7 CString strTemp10 = strData7.c_str();