std::string_view无终止符无法用sscanf,求POSIX替代方案
POSIX标准下处理std::string_view格式化输入的替代方案
由于std::string_view不保证以空字符结尾,直接使用sscanf会触发未定义行为。微软CRT的_snscanf_s支持传入缓冲区长度规避该问题,POSIX标准里有两种可靠的替代方案:
1. 转换为临时空终止字符串(简单直观)
把std::string_view转换成std::string,利用std::string自动添加空终止符的特性,再传给sscanf:
TIMESTAMP_STRUCT StringToTimestamp(std::string_view dTime, bool dayfirst) noexcept { TIMESTAMP_STRUCT t{}; const std::string temp_str(dTime); int res; if (dayfirst) res = sscanf(temp_str.c_str(), "%2hu.%2hu.%4hd %2hu:%2hu:%2hu", &t.day, &t.month, &t.year, &t.hour, &t.minute, &t.second); else res = sscanf(temp_str.c_str(), "%4hd-%2hu-%2hu %2hu:%2hu:%2hu", &t.year, &t.month, &t.day, &t.hour, &t.minute, &t.second); if (res == 6) return t; if (!dTime.empty() && dTime != "0") Common::Log::Instance()->TraceFatal("StringToTimestamp failed"); return {}; }
该方案实现简单,无额外依赖;缺点是会产生一次内存拷贝,对于短时间戳字符串而言,性能影响可忽略。
2. 用fmemopen包装内存为文件流(无内存拷贝)
POSIX.1-2008标准定义的fmemopen可直接将string_view指向的内存区域包装成只读文件流,再用fscanf读取内容,完全避免内存拷贝:
#include <cstdio> TIMESTAMP_STRUCT StringToTimestamp(std::string_view dTime, bool dayfirst) noexcept { TIMESTAMP_STRUCT t{}; // fmemopen要求非const指针,但我们仅做只读操作,const_cast是安全的 FILE* stream = fmemopen(const_cast<char*>(dTime.data()), dTime.size(), "r"); if (!stream) { if (!dTime.empty() && dTime != "0") Common::Log::Instance()->TraceFatal("StringToTimestamp failed: fmemopen failed"); return {}; } int res; if (dayfirst) res = fscanf(stream, "%2hu.%2hu.%4hd %2hu:%2hu:%2hu", &t.day, &t.month, &t.year, &t.hour, &t.minute, &t.second); else res = fscanf(stream, "%4hd-%2hu-%2hu %2hu:%2hu:%2hu", &t.year, &t.month, &t.day, &t.hour, &t.minute, &t.second); fclose(stream); if (res == 6) return t; if (!dTime.empty() && dTime != "0") Common::Log::Instance()->TraceFatal("StringToTimestamp failed"); return {}; }
此方案适合性能敏感场景,但需确保目标系统支持POSIX.1-2008及以上标准。
不推荐的方案:直接用sscanf+strnlen
部分场景下有人会先通过strnlen检查string_view是否包含空字符,若没有则直接用sscanf。但该方式存在风险:若string_view中间意外包含空字符,会导致sscanf提前终止读取,引发逻辑错误,因此不建议使用。
内容的提问来源于stack exchange,提问作者OwnageIsMagic
相关产品推荐
相关产品推荐

