C++ string类I/O用法详解
C++ string 类输入输出基础
标准输入流读取 string
在 C++ 中,最常见的从标准输入读取 string
的方式是使用 cin
。例如:
#include <iostream>
#include <string>
int main() {
std::string str;
std::cout << "请输入一个字符串: ";
std::cin >> str;
std::cout << "你输入的字符串是: " << str << std::endl;
return 0;
}
在上述代码中,cin >> str
从标准输入读取一个字符串并存储到 str
中。这里需要注意的是,cin
是以空白字符(空格、制表符、换行符等)作为分隔符的。也就是说,如果输入 hello world
,cin
只会读取到 hello
,world
会留在输入缓冲区中。
读取整行字符串
如果我们希望读取包含空白字符的整行字符串,可以使用 std::getline
函数。getline
函数有两种常见的重载形式:
std::getline(std::istream& is, std::string& str)
:从输入流is
中读取一行数据到str
中,默认以换行符\n
作为行结束标志。std::getline(std::istream& is, std::string& str, char delim)
:从输入流is
中读取数据到str
中,直到遇到指定的分隔符delim
。
示例代码如下:
#include <iostream>
#include <string>
int main() {
std::string line;
std::cout << "请输入一行字符串: ";
std::getline(std::cin, line);
std::cout << "你输入的整行字符串是: " << line << std::endl;
std::string part;
std::cout << "请输入以逗号分隔的部分字符串: ";
std::getline(std::cin, part, ',');
std::cout << "你输入的部分字符串是: " << part << std::endl;
return 0;
}
在第一个 getline
调用中,它会读取整行输入,包括空格。而第二个 getline
调用中,当遇到逗号时就会停止读取。
格式化输入输出
使用 printf 和 scanf 家族函数处理 string
虽然 C++ 有自己的输入输出流库,但由于历史原因,C 语言的 printf
和 scanf
家族函数仍然可以在 C++ 程序中使用。不过,这些函数并没有原生支持 std::string
类型。要使用它们,我们需要将 std::string
转换为 C 风格的字符串(const char*
)。
对于输出,printf
函数使用 %s
格式说明符来打印 C 风格字符串。例如:
#include <iostream>
#include <string>
#include <cstdio>
int main() {
std::string str = "Hello, printf!";
printf("%s\n", str.c_str());
return 0;
}
这里使用了 std::string
的 c_str
成员函数,它返回一个指向以空字符结尾的 C 风格字符串的指针。
对于输入,scanf
函数可以读取 C 风格字符串到一个字符数组中,然后再将其转换为 std::string
。示例如下:
#include <iostream>
#include <string>
#include <cstdio>
int main() {
char buffer[100];
std::cout << "请输入一个字符串: ";
scanf("%s", buffer);
std::string str(buffer);
std::cout << "你输入的字符串是: " << str << std::endl;
return 0;
}
需要注意的是,scanf
存在缓冲区溢出的风险,如果输入的字符串长度超过 buffer
的大小,就会导致未定义行为。
使用字符串流进行格式化输入输出
std::stringstream
类模板在 <sstream>
头文件中定义,它允许我们像操作标准输入输出流一样操作字符串。这在进行格式化输入输出时非常有用。
字符串流输出
#include <iostream>
#include <sstream>
#include <string>
int main() {
int num = 42;
double dbl = 3.14159;
std::string str;
std::stringstream ss;
ss << "数字: " << num << ", 浮点数: " << dbl;
str = ss.str();
std::cout << str << std::endl;
return 0;
}
在上述代码中,stringstream
对象 ss
首先像 cout
一样将数据格式化输出到自身,然后通过 str
成员函数获取最终的字符串。
字符串流输入
#include <iostream>
#include <sstream>
#include <string>
int main() {
std::string input = "10 20.5";
int intValue;
double doubleValue;
std::stringstream ss(input);
ss >> intValue >> doubleValue;
std::cout << "整数: " << intValue << ", 浮点数: " << doubleValue << std::endl;
return 0;
}
这里 stringstream
对象 ss
以 input
字符串为输入源,像 cin
一样从字符串中按顺序提取数据并转换为相应的类型。
向文件中读写 string
写入文件
要将 std::string
写入文件,我们使用 <fstream>
头文件中的 std::ofstream
类。示例如下:
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::string content = "这是要写入文件的内容。";
std::ofstream file("example.txt");
if (file.is_open()) {
file << content << std::endl;
file.close();
std::cout << "内容已成功写入文件。" << std::endl;
} else {
std::cout << "无法打开文件。" << std::endl;
}
return 0;
}
在上述代码中,首先创建了一个 ofstream
对象并尝试打开名为 example.txt
的文件。如果文件成功打开,就将 content
字符串写入文件,并在末尾添加换行符。最后关闭文件。
从文件读取
从文件读取 std::string
同样使用 <fstream>
头文件,这次使用 std::ifstream
类。
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::string line;
std::ifstream file("example.txt");
if (file.is_open()) {
while (std::getline(file, line)) {
std::cout << "从文件读取的内容: " << line << std::endl;
}
file.close();
} else {
std::cout << "无法打开文件。" << std::endl;
}
return 0;
}
这里使用 ifstream
打开文件,如果成功打开,通过 getline
逐行读取文件内容并输出到控制台。
内存管理与 string 输入输出
string 在输入输出中的内存分配
当我们使用 cin
、getline
等函数读取字符串到 std::string
对象时,std::string
会根据输入的内容动态分配内存。例如,当使用 getline
读取一个很长的字符串时,std::string
会分配足够的内存来存储整个字符串。
#include <iostream>
#include <string>
int main() {
std::string longStr;
std::cout << "请输入一个很长的字符串: ";
std::getline(std::cin, longStr);
std::cout << "字符串长度: " << longStr.size() << ", 容量: " << longStr.capacity() << std::endl;
return 0;
}
在这个例子中,size
成员函数返回字符串的实际长度,capacity
成员函数返回当前分配的内存容量。当输入的字符串长度超过当前容量时,std::string
会重新分配内存,通常会以一定的策略增加容量,例如翻倍。
避免不必要的内存分配
在某些情况下,我们可以提前知道要读取的字符串的大致长度,这时可以使用 reserve
成员函数预先分配足够的内存,以避免多次重新分配内存。
#include <iostream>
#include <string>
int main() {
std::string str;
str.reserve(100); // 预先分配 100 个字符的空间
std::cout << "请输入字符串: ";
std::cin >> str;
std::cout << "字符串长度: " << str.size() << ", 容量: " << str.capacity() << std::endl;
return 0;
}
这样,如果输入的字符串长度不超过 100,就不会发生额外的内存重新分配。
高级输入输出场景
自定义输入输出操作符
我们可以为自定义类型重载输入输出操作符,以便与 std::string
进行交互。例如,假设有一个简单的 Point
类:
#include <iostream>
#include <string>
#include <sstream>
class Point {
public:
int x;
int y;
// 重载输出操作符
friend std::ostream& operator<<(std::ostream& os, const Point& p) {
os << "(" << p.x << ", " << p.y << ")";
return os;
}
// 重载输入操作符
friend std::istream& operator>>(std::istream& is, Point& p) {
std::string temp;
std::getline(is, temp, ')');
std::stringstream ss(temp.substr(temp.find('(') + 1));
ss >> p.x >> p.y;
return is;
}
};
int main() {
Point p;
std::cout << "请输入点的坐标 (格式: (x, y)): ";
std::cin >> p;
std::cout << "输入的点: " << p << std::endl;
return 0;
}
在上述代码中,我们重载了 <<
和 >>
操作符,使得 Point
类型可以方便地进行输入输出,并且在输入输出过程中与 std::string
进行了有效的转换。
处理国际化字符串输入输出
在处理国际化字符串时,std::wstring
用于宽字符字符串,而 <codecvt>
头文件提供了在不同字符编码之间转换的工具。例如,将 std::string
(多字节字符)转换为 std::wstring
(宽字符)以便处理 Unicode 字符。
#include <iostream>
#include <string>
#include <codecvt>
#include <locale>
int main() {
std::string mbString = "你好";
std::wstring_convert<std::codecvt<wchar_t, char, std::mbstate_t>> converter;
std::wstring wideString = converter.from_bytes(mbString);
std::wcout.imbue(std::locale("en_US.UTF - 8"));
std::wcout << L"宽字符字符串: " << wideString << std::endl;
return 0;
}
这个例子展示了如何将多字节字符串转换为宽字符字符串,并在控制台输出。在实际应用中,这种转换在处理不同编码的文件输入输出以及与国际化 API 交互时非常有用。
性能优化在 string 输入输出中的应用
- 减少拷贝:在输入输出操作中,尽量减少
std::string
的拷贝。例如,使用std::move
语义来避免不必要的深拷贝。
#include <iostream>
#include <string>
void processString(std::string&& str) {
std::cout << "处理字符串: " << str << std::endl;
}
int main() {
std::string str = "测试字符串";
processString(std::move(str));
return 0;
}
在上述代码中,std::move
将 str
的所有权转移给 processString
函数,避免了一次拷贝。
- 批量操作:如果需要进行多次输入输出操作,可以考虑批量处理以减少系统调用开销。例如,在文件写入时,可以先将多个字符串拼接成一个大字符串,然后一次性写入文件。
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ofstream file("batch.txt");
std::string batchStr;
std::string str1 = "第一行";
std::string str2 = "第二行";
std::string str3 = "第三行";
batchStr = str1 + "\n" + str2 + "\n" + str3 + "\n";
file << batchStr;
file.close();
return 0;
}
这种方式减少了文件写入的次数,提高了性能。
通过以上详细的讲解和丰富的代码示例,相信你对 C++ 中 string
类的输入输出用法有了深入的理解,无论是日常编程还是处理复杂的项目需求,都能更加得心应手地运用这些知识。