C++提供了一个更好的string类,并且作为标准库的一部分提供了这个字符串的实现。在C++中,std::string是一个类(实际上是basic_string模板类的一个实例),这个类支持<cstring>中提供的大部分功能,还能自动管理内存分配。string类在std名称空间的<string>头文件中定义。
1. C风格的字符串的优势和劣势
优势是:1)底层使用了基本的字符类型和数组结构
2)量级小,如果用的好,只会占用所需的内存
3)可以按照操作原始内存的方式操作和复制字符串
劣势是:1)很难找到bug在哪,信不信由你
2)没有利用C++面向对象的特性
3)程序员需要明白底层的表示方式,有很多是你不愿意去明白的
2.使用string类
要明白string是一个类,不过你也可以把它看做是基本类型,像int,实际上它不是基本类型。它用起来很简单,看例子就知道了:
string A("12");
string B("34");
string C;
C=A+B;
如果使用C风格就比较麻烦了:
char* str1="12";
char* str2="34";
char* str3=new char[strlen(str1)+strlen(str2)+1];
strcpy(str3,str1);
strcat(str3,str2);
C风格字符串的另一个问题是不能通过==运算符进行比较。假设有一下两个字符串:
char* a="12";
char b[]="12";
if(a==b){.......};
if的判断语句始终返回false,因为它比较的是指针的值,而不是字符串的内容。注意C数组和指针是相关的。可以把C数组看成是指向数组中第一个元素的指针。要比较C字符串,需要这样写代码:if(strcmp(a,b)==0){......}; 此外C字符串也无法通过<,>,<=,>=进行比较,因此都需要strcmp()根据字符的字典顺序返回-1、0和1的值进行判断。但是在C++的string,这些符号都被重载了,随便用,这些运算符可以操作真正的字符串字符。单独的字符可以通过运算符operator[]访问。说到底就是一句话,好使。
看代码:
string myString="Hello";
myString +=",there";
string myOtherString=myString;
if(myString == myOtherString)
{
myOtherString[0]='H';
}
cout<<myString<<endl;
cout<<myOtherString<<endl;
输出是:hello,there
Hello,there
在这个例子中,要注意即使字符串被分配和调整大小,也不会出现内存泄漏的情况。所有这些string对象都创建为堆栈变量。尽管string类肯定需要完成大量分配内存和调整大小的操作,但是string的析构函数会在string对象离开作用域时清理内存。
可以运用string的c_str()方法获得一个表示C风格字符串的const字符指针。不过一旦string执行了任何内存重分配或string对象被销毁了,这个返回的const指针就失效了。
因此永远不要返回在基于堆栈的string上调用c_str()的结果。
源代码中的字符串字面量通常解释为const char*。使用用户定义的标准字面量"s"可以把字符串字面量解释为std::string,例如
auto string1="Hello world";
auto string2="Hello world"s;
3. 数值转换
std名称空间包含很多辅助函数,以便完成数值和字符串之间的转换。下面的函数可以用于将数值转换为字符串:
string to_string(int val);(括号里面放各种基本类型,例如double,long等)
例如:long double d=3.14L;
string to_string(d);
通过下面这组在std名称空间中定义的函数可以将string转换为数值。在这些函数圆形中,str表示要转换的string,idx是一个指针,这个指针会接受第一个未转换的字符的索引,base表示转换过程中使用的进制。idx可以是空指针,如果是空指针则被忽略。如果不能执行任何转换,函数会抛出invalid_argument异常,如果转换的值超出了返回类型的范围,则抛出out_of_range异常。
int stoi(const string& str,size_t* idx=0,int base=10);
long stol(const string& str,size_t* idx=0,int base=10);
unsigned long stoul(const sting& str, size_t* idx=0,int base=10);
//就是说string to double的缩写 stod,第一个参数是待转换的string,第二个参数是
将接受第一个未转换的字符的索引,base表示几进制。
4.原始字符串字面量
原始字符串字面量是可以横跨多行代码的字符串字面量,不需要转义嵌入的双引号,像\t和\n这种转义序列不按照转义序列的方式处理,而是按照普通的文本的方式进行处理。
例子:
string str="Hello, "World!""; //报错,应该都看得懂
必须使用转义字符:
string str="Hello, \"World\"";
但是对于原始字符串字面量来说:
string str = R"(Hello "World"!)";
原始字符串字面量可以跨越多行,会忽略转义符号,把它当做普通的字符处理。
string str="Line 1
Line 2 with \t"; //错的
使用原始字符串字面量时就可以:
string str=R"(Line 1
Line 2 with \t)";
会输出:Line 1
Line 2 with \t
但是如果原始字符里面包含)"时,这个方法就不好使了,这时候就得使用扩展的原始字符串字面量语法。
R"d-char-sequence(r-char-sequence)d-char-sequence)";
r-char-sequenece是原始的字符串,d-char-sequence是可选的分隔符序列。原始字符串首尾的分隔符序列应该一致。分隔符序列最多只能有16个字符。应该选择未出现在原始字符串字面量中的序列作为分割符序列。
string str=R"-(The characters )" are embedded in this string)-";