好的,给你讲讲 C++ 中 std::istringstream
和 std::ostringstream
的用法,它们是 <sstream>
头文件里的字符串流类,方便我们在内存中的字符串和流操作之间转换,类似对字符串进行输入输出流操作。
1. std::istringstream
(输入字符串流)
- 作用:把字符串当作输入流,方便逐词读取、格式化解析字符串。
- 常用场景:解析字符串中的数据,比如用空格分割单词,读取数字等。
示例
#include <iostream>
#include <sstream>
#include <string>
int main() {
std::string str = "123 45.67 hello";
std::istringstream iss(str);
int i;
double d;
std::string s;
iss >> i >> d >> s;
std::cout << "整数: " << i << std::endl;
std::cout << "浮点数: " << d << std::endl;
std::cout << "字符串: " << s << std::endl;
return 0;
}
输出:
整数: 123
浮点数: 45.67
字符串: hello
2. std::ostringstream
(输出字符串流)
- 作用:把数据格式化输出到字符串中,方便拼接字符串、格式化内容。
- 常用场景:拼接字符串或将各种类型转换成字符串。
示例
#include <iostream>
#include <sstream>
#include <string>
int main() {
std::ostringstream oss;
int i = 42;
double d = 3.14;
std::string s = "world";
oss << "Hello " << s << ", number: " << i << ", pi = " << d;
std::string result = oss.str();
std::cout << result << std::endl;
return 0;
}
输出:
Hello world, number: 42, pi = 3.14
3. 其他相关操作
iss.str()
可以获取当前输入流对应的字符串。oss.str()
可以获取当前输出流构建的字符串。- 你也可以用
iss.str(new_string)
来重置输入流的字符串内容。
好的,这里给你写一个综合示例,演示如何用 std::istringstream
解析一行文本(比如从用户输入的一行多数据),然后用 std::ostringstream
拼接格式化字符串输出。
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
int main() {
std::cout << "请输入一行数字,空格分隔:" << std::endl;
std::string line;
std::getline(std::cin, line);
// 用 istringstream 解析输入行
std::istringstream iss(line);
std::vector<int> numbers;
int num;
while (iss >> num) {
numbers.push_back(num);
}
// 计算数字和
int sum = 0;
for (int n : numbers) sum += n;
// 用 ostringstream 拼接输出字符串
std::ostringstream oss;
oss << "你输入的数字有 " << numbers.size() << " 个,分别是:";
for (size_t i = 0; i < numbers.size(); ++i) {
oss << numbers[i];
if (i != numbers.size() - 1) oss << ", ";
}
oss << "。它们的和是 " << sum << "。";
std::cout << oss.str() << std::endl;
return 0;
}
演示说明:
- 用户输入一行数字,例如:
10 20 30 40
- 程序用
istringstream
解析数字到vector<int>
。 - 用
ostringstream
拼接一个格式化的字符串结果输出。
好,给你写个更复杂的示例,演示如何用 std::istringstream
解析一行混合数据(整数、小数、字符串),并用 std::ostringstream
格式化输出,带小数点精度控制。
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <iomanip> // for std::setprecision
struct DataItem {
int id;
double value;
std::string name;
};
int main() {
std::cout << "请输入多组数据,格式为:整数 小数 字符串,空格分隔,每组一行,输入空行结束:" << std::endl;
std::vector<DataItem> items;
std::string line;
while (true) {
std::getline(std::cin, line);
if (line.empty()) break; // 空行结束
std::istringstream iss(line);
DataItem item;
if (iss >> item.id >> item.value >> item.name) {
items.push_back(item);
} else {
std::cerr << "输入格式错误,跳过该行。" << std::endl;
}
}
std::ostringstream oss;
oss << "你输入了 " << items.size() << " 组数据:" << std::endl;
for (const auto& item : items) {
oss << "ID=" << item.id
<< ", 值=" << std::fixed << std::setprecision(2) << item.value
<< ", 名称=\"" << item.name << "\"" << std::endl;
}
std::cout << oss.str();
return 0;
}
使用示例:
输入:
1 3.14159 apple
2 2.71828 banana
3 1.41421 cherry
输出:
你输入了 3 组数据:
ID=1, 值=3.14, 名称="apple"
ID=2, 值=2.72, 名称="banana"
ID=3, 值=1.41, 名称="cherry"
好,这里给你写一个示例,演示如何用 std::ostringstream
做宽度、对齐和填充字符的格式化输出。
#include <iostream>
#include <sstream>
#include <iomanip>
#include <string>
int main() {
std::ostringstream oss;
int id = 42;
double value = 3.14159;
std::string name = "example";
// 设置宽度为10,右对齐,空格填充(默认)
oss << "ID: " << std::setw(10) << id << "\n";
// 设置宽度为10,左对齐,空格填充
oss << "Value: " << std::left << std::setw(10) << std::fixed << std::setprecision(2) << value << "\n";
// 设置宽度为15,右对齐,用'-'填充
oss << "Name: " << std::right << std::setw(15) << std::setfill('-') << name << "\n";
std::cout << oss.str();
return 0;
}
运行结果:
ID: 42
Value: 3.14
Name: -------example
重点解释:
std::setw(n)
设置字段宽度为 n。std::left
和std::right
控制左对齐或右对齐。std::setfill(c)
设置填充字符,默认是空格。std::fixed
和std::setprecision(n)
控制浮点数小数位数。
明白了!这里帮你写一个基于之前代码的多线程版 Winsock TCP 客户端,包含:
- 独立线程做连接和自动重连
- 主线程处理数据收发
- 线程安全退出机制
- 错误日志写文件
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <thread>
#include <atomic>
#include <chrono>
#include <fstream>
#include <mutex>
#pragma comment(lib, "ws2_32.lib")
std::atomic<bool> running{ true };
std::mutex log_mutex;
void log_error(const std::string& msg) {
std::lock_guard<std::mutex> lock(log_mutex);
std::ofstream log("client_error.log", std::ios::app);
log << msg << std::endl;
std::cerr << msg << std::endl;
}
bool connect_to_server(SOCKET& sock, const sockaddr_in& serverAddr) {
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET) {
log_error("Socket 创建失败,错误码:" + std::to_string(WSAGetLastError()));
return false;
}
if (connect(sock, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
int err = WSAGetLastError();
log_error("连接失败,错误码:" + std::to_string(err));
closesocket(sock);
sock = INVALID_SOCKET;
return false;
}
std::cout << "连接服务器成功!" << std::endl;
return true;
}
void connection_manager(SOCKET& sock, const sockaddr_in& serverAddr) {
const int max_retries = 5;
int retry_count = 0;
while (running) {
if (sock == INVALID_SOCKET) {
if (!connect_to_server(sock, serverAddr)) {
retry_count++;
if (retry_count > max_retries) {
log_error("重连超过最大次数,停止连接管理线程");
running = false;
break;
}
log_error("等待3秒后重连...");
std::this_thread::sleep_for(std::chrono::seconds(3));
continue;
}
retry_count = 0;
}
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
void data_handler(SOCKET& sock) {
while (running) {
if (sock == INVALID_SOCKET) {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
continue;
}
// 发送示例
const char* msg = "Hello server";
int ret = send(sock, msg, (int)strlen(msg), 0);
if (ret == SOCKET_ERROR) {
int err = WSAGetLastError();
log_error("发送失败,错误码:" + std::to_string(err));
if (err == 10053 || err == 10054) {
log_error("连接异常断开,准备重连");
closesocket(sock);
sock = INVALID_SOCKET;
continue;
} else {
log_error("未知错误,停止程序");
running = false;
break;
}
}
// 接收示例
char buf[512];
ret = recv(sock, buf, sizeof(buf) - 1, 0);
if (ret == 0) {
log_error("服务器关闭连接,准备重连");
closesocket(sock);
sock = INVALID_SOCKET;
continue;
}
else if (ret == SOCKET_ERROR) {
int err = WSAGetLastError();
log_error("接收失败,错误码:" + std::to_string(err));
if (err == 10053 || err == 10054) {
log_error("连接异常断开,准备重连");
closesocket(sock);
sock = INVALID_SOCKET;
continue;
} else {
log_error("未知错误,停止程序");
running = false;
break;
}
}
else {
buf[ret] = '\0';
std::cout << "收到数据: " << buf << std::endl;
}
std::this_thread::sleep_for(std::chrono::seconds(5));
}
}
int main() {
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
log_error("WSAStartup失败");
return 1;
}
sockaddr_in serverAddr{};
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(80);
inet_pton(AF_INET, "192.168.1.100", &serverAddr.sin_addr);
SOCKET sock = INVALID_SOCKET;
std::thread conn_thread(connection_manager, std::ref(sock), std::ref(serverAddr));
std::thread data_thread(data_handler, std::ref(sock));
std::cout << "按回车退出程序..." << std::endl;
std::cin.get();
running = false;
conn_thread.join();
data_thread.join();
if (sock != INVALID_SOCKET) {
closesocket(sock);
}
WSACleanup();
return 0;
}
说明:
connection_manager
线程负责连接和重连,最多重连 5 次失败后停止程序。data_handler
线程负责数据收发,检测错误 10053 和 10054 后关闭连接,等待重连。- 采用
std::atomic<bool>
控制线程退出。 - 所有错误日志写入
client_error.log
,同时输出到控制台。 - 主线程等待用户回车后,安全退出所有线程和清理资源。
发表回复