如何用C++ date库解析秒与亚秒间无小数点的字符串?
问题
使用Howard Hinnant开发的date库时,遇到解析HH:mm:ssSSS格式时间字符串的问题:可以正常解析带分隔符的HH:mm:ss,SSS格式,但无法处理秒与亚秒之间无分隔符的HH:mm:ssSSS格式。不想通过修改字符串适配,询问是否可以通过date::parse()和std::numpunct实现解析。
附相关代码(已修正拼写及语法错误):
class PunctuationFacet : public std::numpunct<char> { public: PunctuationFacet(char numericPunctuation, size_t referenceCount = 0) : std::numpunct<char>(referenceCount) , _numericPunctuation(numericPunctuation) {} protected: char do_decimal_point() const { return _numericPunctuation; } private: char _numericPunctuation; }; std::optional<uint64_t> parse(std::string_view value, char numericPunctuation) { date::sys_time<std::chrono::milliseconds> timepoint; std::stringstream ss; ss.imbue(std::locale(ss.getloc(), new PunctuationFacet(numericPunctuation))); ss << value; ss >> date::parse("%H:%M:%S", timepoint); if (ss.fail()) [[unlikely]] { return std::nullopt; } return timepoint.time_since_epoch().count(); } int main(int argumentCount, char **arguments) { auto timestampDoesWork = parse("14:20:51,123", ','); auto timestampDoesNotWork = parse("14:20:51123", 0); }
解决方案
仅通过std::numpunct无法实现HH:mm:ssSSS格式的解析,核心原因是:
std::numpunct的作用仅为指定小数点符号,但HH:mm:ssSSS中秒与毫秒之间没有任何分隔符,不存在可替换的小数点位置。date::parse的%S格式要求秒和亚秒部分必须有分隔符(小数点或numpunct指定的符号),否则会把51123整体当作秒数解析,超出合法范围导致失败。
要在不修改原字符串的前提下解析该格式,可以利用date库的格式化字符串扩展:
直接解析无分隔符格式:使用
%H:%M:%S%3f格式字符串,其中%3f表示读取3位数字作为毫秒数,date::parse会自动拆分秒部分(如51)和后续3位(如123)。
示例代码:std::optional<uint64_t> parse_no_sep(std::string_view value) { date::sys_time<std::chrono::milliseconds> timepoint; std::istringstream ss(std::string(value)); if (!(ss >> date::parse("%H:%M:%S%3f", timepoint))) return std::nullopt; return timepoint.time_since_epoch().count(); }调用
parse_no_sep("14:20:51123")即可得到正确结果。同时兼容两种格式:先尝试无分隔符格式,失败后再切换到带分隔符的解析逻辑:
std::optional<uint64_t> parse_both(std::string_view value) { date::sys_time<std::chrono::milliseconds> timepoint; std::istringstream ss(std::string(value)); // 先尝试无分隔符格式 if (ss >> date::parse("%H:%M:%S%3f", timepoint)) return timepoint.time_since_epoch().count(); // 重置流,尝试带分隔符格式 ss.clear(); ss.seekg(0); ss.imbue(std::locale(ss.getloc(), new PunctuationFacet(','))); if (ss >> date::parse("%H:%M:%S", timepoint)) return timepoint.time_since_epoch().count(); return std::nullopt; }此函数可同时处理
14:20:51,123和14:20:51123两种格式。
内容的提问来源于stack exchange,提问作者DeepBlue
相关产品推荐
相关产品推荐

