一、C-Type string
- 在C语言中,字符串表示为字符数组,最后面加
\0
strlen
获得字符串实际大小
sizeof
不要使用sizeof获取字符串大小
char* copyString(const char* str)
{
char* result { new char[strlen(str)] }; // BUG! Off by one!
strcpy(result, str);
return result;
}
char* copyString(const char* str)
{
FIGURE 2-1
Dynamic Strings ❘ 89
char* result { new char[strlen(str) + 1] }; //正确做法
strcpy(result, str);
return result;
}
char* appendStrings(const char* str1, const char* str2, const char* str3)
{
char* result { new char[strlen(str1) + strlen(str2) + strlen(str3) + 1] };
strcpy(result, str1);
strcat(result, str2);
strcat(result, str3);
return result;
}
char text1[] { "abcdef" };
size_t s1 { sizeof(text1) }; // is 7
size_t s2 { strlen(text1) }; // is 6
const char* text2 { "abcdef" };
//sizeof const char* 的大小
size_t s3 { sizeof(text2) }; // is platform-dependent 32位 4 64位 8
size_t s4 { strlen(text2) }; // is 6
二、 字符串常量(String Literals )
cout << "hello" << endl; //字符串常量 "hello" 存在只读内存中
//“array of n const char
//字符串常量赋值给字符指针时 不可修改
char* ptr { "hello" }; // Assign the string literal to a variable.
ptr[1] = 'a'; // Undefined behavior!
const char* ptr { "hello" }; // Assign the string literal to a variable.
ptr[1] = 'a'; // Error! Attempts to write to read-only memory
//字符串常量赋值给字符数组时 可修改
//编译器会常量字符串给字符数组
char arr[] { "hello" }; // Compiler takes care of creating appropriate sized
// character array arr.
arr[1] = 'a'; // The contents can be modified.
三、原始字符串字面值(raw string literal)
原始字符串简单来说,“原生的、不加处理的”,字符表示的就是自己(所见即所得),引号、斜杠无需 “\” 转义,比如常用的目录表示,引入原始字符串后,非常方便。
格式
R"(原始字符串)";
代码
#include <iostream>
#include<string>
int main()
{
//原始字符串字面值
std::string redist_path1 = "C:\Program Files (x86)\Microsoft.NET\RedistList";
std::string redist_path2 = "C:\\Program Files (x86)\\Microsoft.NET\\RedistList";
std::string redist_path3 = R"(C:\Program Files (x86)\Microsoft.NET\RedistList)";
std::string redist_path4 = R"(C:\\Program Files (x86)\\Microsoft.NET\\RedistList)";
std::cout << "redist_path1: " << redist_path1 << std::endl;
std::cout << "redist_path2: " << redist_path2 << std::endl;
std::cout << "redist_path3: " << redist_path3 << std::endl;
std::cout << "redist_path4: " << redist_path4 << std::endl;
std::string fozu = R"(
_ooOoo_
o8888888o
88" . "88
(| -_- |)
O\ = /O
____/`---'\____
. ' \\| |// `.
/ \\||| : |||// \
/ _||||| -:- |||||- \
| | \\\ - /// | |
| \_| ''\---/'' | |
\ .-\__ `-` ___/-. /
___`. .' /--.--\ `. . __
."" '< `.___\_<|>_/___.' >'"".
| | : `- \`.;`\ _ /`;.`/ - ` : | |
\ \ `-. \_ __\ /__ _/ .-` / /
======`-.____`-.___\_____/___.-`____.-'======
`=---='
)";
std::cout << fozu << std::endl;
return 0;
}
四、 The C++ std::string Class
1. C-style 字符串的优缺点
优点:
- 很简单,利用了底层的基本字符类型和数组结构
- 很轻,如果使用得当,只会占用所需的内存
- 它们是低级别的,所以您可以很容易地将它们作为原始内存进行操作和复制
- 如果你是一个C程序员,为什么要学习一些新东西?
缺点:
- 需要付出难以置信的努力来模拟一流的字符串数据类型
- 容易出现难以找到的内存错误
- 没有利用C++的面向对象特性
- 它们要求程序员了解其底层表示
2. 字符串比较
//c 字符串比较
const char* a = "hello"; //不可修改
char b[] = "hello"; //可以修改
//b[0] = 'a';
//比较的是字符指针
if (a == b)
{
std::cout << "a == b" << std::endl;
}
//C字符串比较
if (strcmp(a, b) == 0)
{
std::cout << "a == b" << std::endl;
}
//stl C++ 字符串
std::string a1("hello");
std::string b1("hello");
//这种方法的缺点:compate 返回值是 int 不容易记忆比较结果 使用起来很麻烦
auto result = a1.compare(b1);
if (result == 0)
{
std::cout << "a1 equal b1" << std::endl;
}else if (result < 0)
{
std::cout << "a1 less b1" << std::endl;
}else if (result > 0)
{
std::cout << "a1 greater b1" << std::endl;
}
//C++20 字符串比较
auto result2{ a1 <=> b1 };
if (std::is_eq(result2))
{
std::cout << "a1 equal b1" << std::endl;
}
else if (std::is_lt(result2))
{
std::cout << "a1 less b1" << std::endl;
}
else if (std::is_gt(result2))
{
std::cout << "a1 greater b1" << std::endl;
}
3. 获取字符串指针
auto pChar1 = a1.c_str();
auto pChar2 = a1.data();
std::cout << pChar1 << std::endl;
std::cout << pChar2 << std::endl;
4. find substr replace starts_with ends_with 用法
//find substr replace starts_with ends_with
std::string strHello{ "Hello!!" };
std::string strWorld{ "The World..." };
auto position{ strHello.find("!!") };
if (position != std::string::npos) {
// Found the "!!" substring, now replace it.
strHello.replace(position, 2, strWorld.substr(3, 6));
}
std::cout << strHello << std::endl;
bool bStart = strHello.starts_with("Hel");
std::cout << bStart << std::endl;
bool bEnd = strHello.ends_with("d");
std::cout << bEnd << std::endl;
5. 数值转换
5.1. 数值转字符串
long double d { 3.14L };
string s { to_string(d) };
5.2. 字符串转数值
- 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 string& str, size_t *idx=0, int base=10);
- long long stoll(const string& str, size_t *idx=0, int base=10);
- unsigned long long stoull(const string& str, size_t *idx=0, int base=10);
- float stof(const string& str, size_t *idx=0);
- double stod(const string& str, size_t *idx=0);
- long double stold(const string& str, size_t *idx=0);
string :将要转换的字符串
idx:第一个不能被转换的字符指针,可以为空(忽略返回值)
base:数值进制,默认十进制
const std::string toParse{ "123USD232" };
size_t index{ 0 };
int value{ stoi(toParse, &index) };
std::cout << std::format("字符串转数值:{}", value) << std::endl;
std::cout << std::format("第一个不能转换的字符:{}", toParse[index]) << std::endl;
五、The std::string_view Class
string_view提供字符串的只读视图(没有字符串拷贝,内部只包含一个指针和长度),并支持类似于字符串的接口
注意:每当函数需要只读字符串作为其参数之一时使用std::string_view而不是const string&或const char*
//string_view 没有字符串拷贝,内部只包含一个指针和长度 效率高
string_view extractExtension(string_view filename)
{
return filename.substr(filename.rfind('.'));
}
string filename { R"(c:\temp\my file.ext)" };
cout << format("C++ string: {}", extractExtension(filename)) << endl;
const char* cString { R"(c:\temp\my file.ext)" };
cout << format("C string: {}", extractExtension(cString)) << endl;
cout << format("Literal: {}", extractExtension(R"(c:\temp\my file.ext)")) << endl;
const char* raw{ "hfdskfd" };
size_t length{ 3 };
std::string_view stringView(raw, length);
std::cout << stringView << std::endl;
注意:不能使用string_view 直接构造 string
//handleExtension(extractExtension("my file.ext")); //error
handleExtension(extractExtension("my file.ext").data()); // data() method
handleExtension(std::string{ extractExtension("my file.ext") }); // explicit ctor
std::string str{ "Hello" };
std::string_view sv{ " world" };
//auto result{ str + sv }; //error
auto result1{ str + sv.data() };
std::string result22{ str };
result22.append(sv.data(), sv.size());
注意:不能使用string_view 作为函数返回值,有失效风险
注意:不能使用string_view存储一个临时string变量的view
string s { "Hello" };
string_view sv { s + " World!" }; //临时变量 World 在大括号之后会释放
cout << sv;