跳到主要内容

C++ 字符串流

字符串流简介

字符串流(stringstream)是C++标准库中的一个强大组件,允许我们像操作输入输出流一样操作字符串。它提供了一种在内存中执行I/O操作的机制,使我们能够将字符串转换为其他数据类型,或者将其他数据类型转换为字符串。

字符串流定义在<sstream>头文件中,主要包含以下三个类:

  • istringstream: 输入字符串流,用于从字符串中读取数据
  • ostringstream: 输出字符串流,用于向字符串中写入数据
  • stringstream: 输入输出字符串流,兼具以上两种功能
提示

字符串流实际上是将字符串作为一个流,这样就可以使用标准的流操作符(<<>>)来进行数据操作。这为我们提供了一种类似于文件I/O的方式来处理字符串。

包含字符串流所需的头文件

使用字符串流需要包含<sstream>头文件:

cpp
#include <sstream>
#include <string>
#include <iostream>

基本用法

stringstream的创建与使用

创建一个stringstream对象很简单:

cpp
#include <sstream>
#include <string>
#include <iostream>

int main() {
// 创建一个空的字符串流
std::stringstream ss;

// 向字符串流中插入数据
ss << "Hello" << " " << "World" << " " << 2023;

// 从字符串流中获取字符串
std::string result = ss.str();

std::cout << "字符串流内容: " << result << std::endl;

return 0;
}

输出:

字符串流内容: Hello World 2023

使用stringstream进行类型转换

字符串流最常见的用途之一是在不同数据类型间进行转换:

数值转字符串

cpp
#include <sstream>
#include <string>
#include <iostream>

int main() {
int number = 42;
double pi = 3.14159;

std::stringstream ss;
ss << "整数: " << number << ", 浮点数: " << pi;

std::string result = ss.str();
std::cout << result << std::endl;

return 0;
}

输出:

整数: 42, 浮点数: 3.14159

字符串转数值

cpp
#include <sstream>
#include <string>
#include <iostream>

int main() {
std::string data = "123 456.789";
std::stringstream ss(data);

int integer;
double decimal;

ss >> integer >> decimal;

std::cout << "整数部分: " << integer << std::endl;
std::cout << "小数部分: " << decimal << std::endl;

return 0;
}

输出:

整数部分: 123
小数部分: 456.789

stringstream的高级操作

清空与重用字符串流

当我们想要重用同一个stringstream对象时,可以使用clear()方法清除错误状态,使用str("")方法清空内容:

cpp
#include <sstream>
#include <string>
#include <iostream>

int main() {
std::stringstream ss;

// 第一次使用
ss << "第一次使用";
std::cout << ss.str() << std::endl;

// 清空和重置
ss.str(""); // 清空内容
ss.clear(); // 清除状态标志

// 第二次使用
ss << "第二次使用";
std::cout << ss.str() << std::endl;

return 0;
}

输出:

第一次使用
第二次使用

格式控制

我们可以像使用cout一样,对stringstream应用格式控制:

cpp
#include <sstream>
#include <string>
#include <iostream>
#include <iomanip> // 格式控制需要这个头文件

int main() {
std::stringstream ss;
double value = 3.14159265359;

ss << "默认: " << value << std::endl;
ss << "精确到2位小数: " << std::fixed << std::setprecision(2) << value << std::endl;
ss << "科学计数法: " << std::scientific << value << std::endl;
ss << "十六进制: " << std::hex << std::uppercase << static_cast<int>(value) << std::endl;

std::cout << ss.str();

return 0;
}

输出:

默认: 3.14159
精确到2位小数: 3.14
科学计数法: 3.14e+00
十六进制: 3

istringstream和ostringstream

使用istringstream进行输入操作

istringstream专门用于从字符串中读取数据:

cpp
#include <sstream>
#include <string>
#include <iostream>
#include <vector>

int main() {
std::string input = "apple orange banana";
std::istringstream iss(input);

std::string fruit;
std::vector<std::string> fruits;

while (iss >> fruit) {
fruits.push_back(fruit);
}

std::cout << "水果清单:" << std::endl;
for (const auto& f : fruits) {
std::cout << "- " << f << std::endl;
}

return 0;
}

输出:

水果清单:
- apple
- orange
- banana

使用ostringstream进行输出操作

ostringstream专门用于向字符串中写入数据:

cpp
#include <sstream>
#include <string>
#include <iostream>

int main() {
std::ostringstream oss;

oss << "姓名: " << "张三" << std::endl;
oss << "年龄: " << 25 << std::endl;
oss << "职业: " << "软件工程师" << std::endl;

std::string profile = oss.str();
std::cout << "个人信息:\n" << profile;

return 0;
}

输出:

个人信息:
姓名: 张三
年龄: 25
职业: 软件工程师

实际应用场景

场景一:CSV数据处理

字符串流在处理CSV(逗号分隔值)格式数据时非常有用:

cpp
#include <sstream>
#include <string>
#include <iostream>
#include <vector>

int main() {
std::string csvLine = "张三,25,北京,软件工程师";
std::istringstream iss(csvLine);
std::string token;
std::vector<std::string> data;

while (std::getline(iss, token, ',')) {
data.push_back(token);
}

std::cout << "姓名: " << data[0] << std::endl;
std::cout << "年龄: " << data[1] << std::endl;
std::cout << "城市: " << data[2] << std::endl;
std::cout << "职业: " << data[3] << std::endl;

return 0;
}

输出:

姓名: 张三
年龄: 25
城市: 北京
职业: 软件工程师

场景二:日志格式化

在开发日志系统时,字符串流可以很方便地格式化各种数据:

cpp
#include <sstream>
#include <string>
#include <iostream>
#include <ctime>
#include <iomanip>

std::string getCurrentTimeStamp() {
auto now = std::time(nullptr);
auto tm = *std::localtime(&now);
std::ostringstream oss;
oss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S");
return oss.str();
}

void logMessage(const std::string& level, const std::string& message) {
std::stringstream ss;
ss << "[" << getCurrentTimeStamp() << "] [" << level << "] " << message;
std::cout << ss.str() << std::endl;
}

int main() {
logMessage("INFO", "程序启动");
logMessage("WARNING", "磁盘空间不足");
logMessage("ERROR", "无法连接到数据库,错误代码: " + std::to_string(404));

return 0;
}

输出(时间戳会根据当前时间变化):

[2023-09-15 10:45:30] [INFO] 程序启动
[2023-09-15 10:45:30] [WARNING] 磁盘空间不足
[2023-09-15 10:45:30] [ERROR] 无法连接到数据库,错误代码: 404

场景三:字符串解析与验证

字符串流可以用于验证输入是否符合某种格式:

cpp
#include <sstream>
#include <string>
#include <iostream>

bool isValidInteger(const std::string& str) {
std::istringstream iss(str);
int num;
iss >> num;
// 检查是否成功读取了一个整数,并且已到达字符串末尾
return !iss.fail() && iss.eof();
}

int main() {
std::string test1 = "123";
std::string test2 = "123abc";
std::string test3 = "abc";
std::string test4 = "123.45";

std::cout << test1 << " 是有效整数? " << (isValidInteger(test1) ? "是" : "否") << std::endl;
std::cout << test2 << " 是有效整数? " << (isValidInteger(test2) ? "是" : "否") << std::endl;
std::cout << test3 << " 是有效整数? " << (isValidInteger(test3) ? "是" : "否") << std::endl;
std::cout << test4 << " 是有效整数? " << (isValidInteger(test4) ? "是" : "否") << std::endl;

return 0;
}

输出:

123 是有效整数? 是
123abc 是有效整数? 否
abc 是有效整数? 否
123.45 是有效整数? 否

字符串流的性能考虑

虽然字符串流非常方便,但在处理大量数据或性能敏感的应用中,我们需要注意以下几点:

  1. 字符串流会进行动态内存分配,可能比直接的字符串操作慢
  2. 频繁创建和销毁字符串流对象会增加开销
  3. 对于简单的数字转换,std::to_stringstd::stoi等函数可能更高效
警告

在性能关键的应用中,考虑重用字符串流对象而不是反复创建新的对象。

总结

字符串流是C++中处理字符串的强大工具,它将流操作的灵活性带入字符串处理,使我们能够:

  1. 轻松地在不同数据类型之间转换
  2. 使用熟悉的流操作符(<<>>)来处理字符串
  3. 应用格式控制来精确控制输出格式
  4. 解析复杂的文本数据

字符串流在实际开发中有广泛的应用,特别是在需要格式化、解析或验证字符串数据的场景中。掌握字符串流的使用将极大地提高你处理文本数据的能力。

练习

  1. 编写一个程序,使用字符串流将用户输入的一组数字(以空格分隔)转换为整数,并计算它们的平均值。
  2. 实现一个函数,使用字符串流将整数转换为其罗马数字表示(例如,58 -> "LVIII")。
  3. 创建一个简单的计算器程序,使用字符串流解析类似"5 + 3 * 2"的表达式并计算结果。
  4. 编写一个程序,使用字符串流格式化一个表格,确保各列对齐。

通过这些练习,你将能够更熟练地运用字符串流来解决实际问题,提升你的C++编程技能。