一、字符数组、字符串和C++string类区别
在C/C++中,字符串和字符数组通常会有以下几种表示
char a[] = {'a','b','c','d','e','f'}; //字符数组
char b[]="abcdef"; //字符串
char *c = (char*)"abcdef"; //字符串
string d = b; //C++string类
//string类可接受char * 和char[]的直接赋值
//但是反过来就不可以
cout<<"sizeof(a): "<<sizeof(a)<<endl;
cout<<"sizeof(b): "<<sizeof(b)<<endl;
cout<<"sizeof(c): "<<sizeof(c)<<endl;
cout<<"sizeof(d): "<<sizeof(d)<<endl;
// cout<<a<<endl; //输出这个会有一堆乱码
char a1[] = {'a','b','c','d','e','f','\0'};
cout<<a1<<endl; //正常输出
cout<<"length of b: "<<strlen(b)<<endl;
cout<<"length of c: "<<strlen(c)<<endl;
cout<<"length of d: "<<d.length()<<endl;
/*
sizeof(a): 6
sizeof(b): 7
sizeof(c): 4
sizeof(d): 4
abcdef
length of b: 6
length of c: 6
length of d: 6
*/
说明:
1.上面的打印结果字符串b比字符数组a大1,是因为字符串默认结尾有一个'\0'字符,标志结束
2.我们看到c和d的大小都是4, 说明c和d都是一个指针
3.当我们直接输出字符串数组是,会出现乱码,且程序无法继续向下运行,是因为,C/C++中字符数组边界是没有约束的,cout时会越界输出乱码。
4.当我们往字符数组后面加一个'\0'时,cout的时候可以识别到结束,能正常输出
二、C/C++字符串和C++string类的操作不同
字符串可以看成是对字符数组的封装,同样,string类是C++中特有的对字符数组的封装,提供了一些易于操作的函数:
1.用于C字符串的函数:
size_t strlen(char const * str) 求字符串长度
int strcmp(char const * str1, char const * str2) 比较字符串
char * strcat(char *dest, char const *src) 拼接两个字符串
char * strcpy(char * dest, char const * src) 复制字符串
char * strtok(char * str, char const * delimiter) 分割字符串
char * strchr(char * const str, int const c) 查找字符串中的字符
char * strncat(char * dest, char const * src, size_t count) 拼接src字符串中的前n个
char * strncpy(char * dest, char const * src, size_t count) 与src字符串中的前n个复制
char str1[50] = "give me ";
char str2[50] = "a cup";
cout<<strlen(str1)<<endl;
cout<<strlen(str2)<<endl;
/*
输出:
8
5
*/
strcat(str1, str2);
cout<<str1<<endl;
strncat(str1, " of coffee a", 10);
cout<<str1<<endl;
/*
give me a cup
give me a cup of coffee
*/
strcpy(str2, str1);
cout<<str2<<endl;
strncpy(str1, str2, 15);
cout<<str1<<endl;
/*
give me a cup of coffee
give me a cup of coffee
*/
cout<<"compare result = "<<strcmp(str1, str2)<<endl;
//compare result = 0
char * res = strchr(str1, 'e');
cout<<*res<<endl;
cout<<*(res+2)<<endl;
//e
//m
// 获取第一个子字符串
char * token = strtok(str1, " ");
//继续获取其他的子字符串
while( token != NULL ) {
printf( "%s ", token );
token = strtok(NULL, " ");
}
//give me a cup of coffee
说明:
当我们使用strcat, strcpy的时候一定要注意dest字符串的大小一定要大于拼接后或复制后字符串的大小,否则可能造成内存冲突
另外,C++11标准后推出strcat_s()方法,_s的意思就是safe,安全模式,他首先会检查缓存区的大小是否大于结果的大小,以免对内存的其他数据产生影响。而且在Visual Studio2017中上述函数不被支持,可以使用strcat_s()。
int strcat_s(char *restrict dest, rsize_t destsz, const char *restrict src);
其中,destsz一般填sizeof(dest), 即告诉编译器dest的大小,若拼接后结果大于该数值,则产生错误,不会对其他数据产生影响。
同理,也有strcpy_s, strtok_s等。
2.string类的函数
很遗憾。C++标准库里面没有字符分割函数split
(1)字符串长度
i. str.length()
(2)字符串拼接
i. str = str1+ str2 直接相加,由于string类重载了+运算符,所以可以直接相加,跟python类似
ii. str1.append(const char * str2)
iii. str1.append(const char * str2, int start, int num)
start代表开始的下标,num代表append的字符数
(3)字符串比较
i. str1.compare(str2)
ii. str1==str2 或者使用 >, <,>=, <=, !=, 返回True或者False
(4)字符串删除某段
i. str.erase(int start, int num)
ii. str.erase(str.begin()+i, str.begin()+j)
后者跟vector的一样:vect.erase(vect.begin()+i, vect.begin()+j)
(5)截取子字符串
substring = str.substr(int start, int num)
(6)插入字符串
i. str1.insert(int start, const char * str2)
ii. str1.insert(int start, const char * str2, int str2_start, int num)
(7)替换字符串
i. str1.replace(int str1_start, int num1, const char * str2, int str2_start, int num2)
将str1中从str1_start开始的num1个字符替换为str2中从str2_start2开始的num2个字符
(8)查找字符串
str1.find(const char * str2, int str1_start) ->返回下标 , 找不到的话返回 -1
(9)copy函数
很有趣的是这个copy函数跟strcpy不太一样,它是将string类的内容拷贝到字符串中
str1.copy(char * str2, int num,, int str1_start)
这里的str2不能替换成string类的对象,这是跟上面8个函数所不同的, 上面8个char * 可以替换成string类对象。
(10)string类转换成普通C字符串
str1.c_str()
string str1 = "give me";
string str2 = " a cup";
str1.append(str2);
cout<<str1<<endl;
//give me a cup
str1.replace(0,1, "Give", 0,1);
cout<<str1<<endl;
//Give me a cup
str1.insert(0, "abc", 1, 2);
cout<<str1<<endl;
//bcGive me a cup
str1.erase(1,1);
cout<<str1<<endl;
//bGive me a cup
int index = str1.find("ve", 0);
cout<<"index="<<index<<endl;
//index=3
const char* str4;
str4 = str1.c_str();
string a = str4;
cout<<a<<endl;
//bGive me a cup
//拷贝,将string对象的内容拷贝到普通C字符串中
char str3[10] = "defg";
str1.copy(str3, 7, 1);
cout<<str3<<endl;
//Give me
string类里面的设计思想不会使用int start , int end,这样的取子字符串的方式,而是int start , int num,这个常常会让人搞混。
3.字符串和int类型,float类型等互相转换
#include<sstream>
//const char *类型转换成int类型 ,a-to-int -> atoi
int a;
a = std::atoi("11");
cout<<a<<endl;
//const char *类型转换成float类型 ,a-to-float -> atof
float b;
b = std::atof("11.12");
cout<<b<<endl;
//int类型转换成string类型
string c;
ostringstream ostr;
ostr << "I am a string:"<< a ;
c = ostr.str();
cout<<c<<endl;
/*输出:
11
11.12
I am a string:11
*/
//char *类型转换成double类型
char str[30] = "2030300 This is test";
char *ptr;
long ret;
ret = strtol(str, &ptr, 10); //10代表十进制
printf("数字(无符号长整数)是 %ld\n", ret);
printf("字符串部分是 |%s|", ptr);
/*输出:
数字(无符号长整数)是 2030300
字符串部分是 | This is test|
*/