C++ - std::string字符串格式化方法总结

news/2024/11/29 19:48:53/

文章目录

  • 1 C++ std::string字符串格式化
    • 1.1 C语言中的字符串格式化
    • 1.2 C++使用std::stringstream进行字符串格式化
    • 1.3 开源的C++单个头文件的字符串格式化工具
      • 1.3.1 format
      • 1.3.2 sformat
    • 1.4 自定义的C++字符串格式化函数
    • 1.5 C++20的字符串标准库函数std::format

1 C++ std::string字符串格式化

在Python中,我们可以使用以下代码方便的格式化字符串

if __name__ == '__main__':format_str = "There are {} fools in the world".format(10)print(format_str)

不仅是Python,在其他高级语言中同样也可以很好地对字符串进行格式化。

本文将对C++中字符串格式化方法进行总结,包括:

  • C语言中如何进行字符串格式化
  • C++20之前的版本如何进行字符串格式化
  • C++20的字符串格式化标准库函数std::format

1.1 C语言中的字符串格式化

在C语言中,我们可以使用

int sprintf(char* buffer, const char* format, ... );     //不推荐使用 
int snprintf(char* buffer, std::size_t buf_size, const char* format, ... );

进行字符串格式化,例如

#include <iostream>int main()
{char format_str[64] = { 0 };snprintf(format_str, sizeof(format_str) - 1, "There are %d fools in the world", 10);std::cout << format_str << std::endl;
}

1.2 C++使用std::stringstream进行字符串格式化

在C++中,C++标准库在C++20之前并没有给std::string字符串类提供一个标准的字符串格式化函数,我们只能通过使用std::stringstream字符串流来拼凑字符串,比如

#include <iostream>
#include <sstream>int main()
{std::stringstream ss;ss << "There are ";ss << 10;ss << " fools in the world";std::cout << ss.str() << std::endl;return 0;
}

这种代码真的是又臭又长,特别是当格式化参数很多时。

1.3 开源的C++单个头文件的字符串格式化工具

本小节介绍简单好用跨平台的C++20之前的字符串格式化开源工具。

1.3.1 format

Github地址:https://github.com/arajar/format

这是一个只有单个头文件的C++11标准的std::string字符串格式化工具,其只有一个Format.h文件,头文件代码如下

#pragma once#include <string>
#include <vector>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <iomanip>namespace util
{class ArgBase{public:ArgBase() {}virtual ~ArgBase() {}virtual void Format(std::ostringstream &ss, const std::string& fmt) = 0;};template <class T>class Arg : public ArgBase{public:Arg(T arg) : m_arg(arg) {}virtual ~Arg(){}virtual void Format(std::ostringstream &ss, const std::string& fmt){ss << m_arg;}private:T m_arg;};class ArgArray : public std::vector < ArgBase* >{public:ArgArray() {}~ArgArray(){std::for_each(begin(), end(), [](ArgBase* p){ delete p; });}};static void FormatItem(std::ostringstream& ss, const std::string& item, const ArgArray& args){int index = 0;int alignment = 0;std::string fmt;char* endptr = nullptr;index = strtol(&item[0], &endptr, 10);if (index < 0 || index >= args.size()){return;}if (*endptr == ','){alignment = strtol(endptr + 1, &endptr, 10);if (alignment > 0){ss << std::right << std::setw(alignment);}else if (alignment < 0){ss << std::left << std::setw(-alignment);}}if (*endptr == ':'){fmt = endptr + 1;}args[index]->Format(ss, fmt);return;}template <class T>static void Transfer(ArgArray& argArray, T t){argArray.push_back(new Arg<T>(t));}template <class T, typename... Args>static void Transfer(ArgArray& argArray, T t, Args&&... args){Transfer(argArray, t);Transfer(argArray, args...);}template <typename... Args>std::string Format(const std::string& format, Args&&... args){if (sizeof...(args) == 0){return format;}ArgArray argArray;Transfer(argArray, args...);size_t start = 0;size_t pos = 0;std::ostringstream ss;while (true){pos = format.find('{', start);if (pos == std::string::npos){ss << format.substr(start);break;}ss << format.substr(start, pos - start);if (format[pos + 1] == '{'){ss << '{';start = pos + 2;continue;}start = pos + 1;pos = format.find('}', start);if (pos == std::string::npos){ss << format.substr(start - 1);break;}FormatItem(ss, format.substr(start, pos - start), argArray);start = pos + 1;}return ss.str();}
}

使用方法

#include <iostream>
#include "Format.h"int main()
{std::string format_str = util::Format("There are {0} fools in the world",10);std::cout << format_str << std::endl;return 0;
}

1.3.2 sformat

Github:https://github.com/mmc1993/sformat

这同样是一个只有单个头文件的字符串格式化工具,在使用时我们只需要包含sformat.h头文件,头文件的代码内容如下

#pragma once#include <tuple>
#include <string>
#include <algorithm>template <class T>
inline void ToString(std::string & ret, T && val)
{ret.append(std::to_string(std::forward<T>(val)));
}inline void ToString(std::string & ret, const std::string & val)
{ret.append(val);
}inline void ToString(std::string & ret, const char * val)
{ret.append(val);
}template <int N>
struct SFormatN {static std::string Format(const char * fmt){static_assert(false, "");}
};template <>
struct SFormatN<0> {template <class ...ARGS>static std::string Format(const char * fmt, const std::tuple<ARGS...> &){return fmt;}
};template <class ...ARGS>
std::string SFormat(const char * fmt, const ARGS &...args)
{const auto tuple = std::forward_as_tuple(args...);return SFormatN<sizeof...(args)>::Format(fmt, tuple);
}#define FMT_N(idx)	case idx: ToString(ret, std::get<idx>(args)); break;#define FMT_PARSE(N, ...)															\
template <>																			\
struct SFormatN<N> {																\template <class... ARGS>														\static std::string Format(const char * fmt, const std::tuple<ARGS...> & args)	\{	std::string ret;															\while (*fmt != '\0') { auto idx = -1;										\if (*fmt == '{') { idx = 0; ++fmt;										\while (*fmt >= '0' && *fmt <= '9')									\{ idx *= 10; idx += (int)(*fmt++ - '0'); }						\if (*fmt != '}') idx = -1; else ++fmt;								\}																		\switch (idx) { __VA_ARGS__ default: ret.append(1, *fmt++); break; }		\}																			\return ret;																	\}																				\
};FMT_PARSE(1, FMT_N(0))
FMT_PARSE(2, FMT_N(0) FMT_N(1))
FMT_PARSE(3, FMT_N(0) FMT_N(1) FMT_N(2))
FMT_PARSE(4, FMT_N(0) FMT_N(1) FMT_N(2) FMT_N(3))
FMT_PARSE(5, FMT_N(0) FMT_N(1) FMT_N(2) FMT_N(3) FMT_N(4))
FMT_PARSE(6, FMT_N(0) FMT_N(1) FMT_N(2) FMT_N(3) FMT_N(4) FMT_N(5))
FMT_PARSE(7, FMT_N(0) FMT_N(1) FMT_N(2) FMT_N(3) FMT_N(4) FMT_N(5) FMT_N(6))
FMT_PARSE(8, FMT_N(0) FMT_N(1) FMT_N(2) FMT_N(3) FMT_N(4) FMT_N(5) FMT_N(6) FMT_N(7))
FMT_PARSE(9, FMT_N(0) FMT_N(1) FMT_N(2) FMT_N(3) FMT_N(4) FMT_N(5) FMT_N(6) FMT_N(7) FMT_N(8))
FMT_PARSE(10, FMT_N(0) FMT_N(1) FMT_N(2) FMT_N(3) FMT_N(4) FMT_N(5) FMT_N(6) FMT_N(7) FMT_N(8) FMT_N(9))
FMT_PARSE(11, FMT_N(0) FMT_N(1) FMT_N(2) FMT_N(3) FMT_N(4) FMT_N(5) FMT_N(6) FMT_N(7) FMT_N(8) FMT_N(9) FMT_N(10))
FMT_PARSE(12, FMT_N(0) FMT_N(1) FMT_N(2) FMT_N(3) FMT_N(4) FMT_N(5) FMT_N(6) FMT_N(7) FMT_N(8) FMT_N(9) FMT_N(10) FMT_N(11))
FMT_PARSE(13, FMT_N(0) FMT_N(1) FMT_N(2) FMT_N(3) FMT_N(4) FMT_N(5) FMT_N(6) FMT_N(7) FMT_N(8) FMT_N(9) FMT_N(10) FMT_N(11) FMT_N(12))
FMT_PARSE(14, FMT_N(0) FMT_N(1) FMT_N(2) FMT_N(3) FMT_N(4) FMT_N(5) FMT_N(6) FMT_N(7) FMT_N(8) FMT_N(9) FMT_N(10) FMT_N(11) FMT_N(12) FMT_N(13))
FMT_PARSE(15, FMT_N(0) FMT_N(1) FMT_N(2) FMT_N(3) FMT_N(4) FMT_N(5) FMT_N(6) FMT_N(7) FMT_N(8) FMT_N(9) FMT_N(10) FMT_N(11) FMT_N(12) FMT_N(13) FMT_N(14))
FMT_PARSE(16, FMT_N(0) FMT_N(1) FMT_N(2) FMT_N(3) FMT_N(4) FMT_N(5) FMT_N(6) FMT_N(7) FMT_N(8) FMT_N(9) FMT_N(10) FMT_N(11) FMT_N(12) FMT_N(13) FMT_N(14) FMT_N(15))
FMT_PARSE(17, FMT_N(0) FMT_N(1) FMT_N(2) FMT_N(3) FMT_N(4) FMT_N(5) FMT_N(6) FMT_N(7) FMT_N(8) FMT_N(9) FMT_N(10) FMT_N(11) FMT_N(12) FMT_N(13) FMT_N(14) FMT_N(15) FMT_N(16))
FMT_PARSE(18, FMT_N(0) FMT_N(1) FMT_N(2) FMT_N(3) FMT_N(4) FMT_N(5) FMT_N(6) FMT_N(7) FMT_N(8) FMT_N(9) FMT_N(10) FMT_N(11) FMT_N(12) FMT_N(13) FMT_N(14) FMT_N(15) FMT_N(16) FMT_N(17))
FMT_PARSE(19, FMT_N(0) FMT_N(1) FMT_N(2) FMT_N(3) FMT_N(4) FMT_N(5) FMT_N(6) FMT_N(7) FMT_N(8) FMT_N(9) FMT_N(10) FMT_N(11) FMT_N(12) FMT_N(13) FMT_N(14) FMT_N(15) FMT_N(16) FMT_N(17) FMT_N(18))
FMT_PARSE(20, FMT_N(0) FMT_N(1) FMT_N(2) FMT_N(3) FMT_N(4) FMT_N(5) FMT_N(6) FMT_N(7) FMT_N(8) FMT_N(9) FMT_N(10) FMT_N(11) FMT_N(12) FMT_N(13) FMT_N(14) FMT_N(15) FMT_N(16) FMT_N(17) FMT_N(18) FMT_N(19))
FMT_PARSE(21, FMT_N(0) FMT_N(1) FMT_N(2) FMT_N(3) FMT_N(4) FMT_N(5) FMT_N(6) FMT_N(7) FMT_N(8) FMT_N(9) FMT_N(10) FMT_N(11) FMT_N(12) FMT_N(13) FMT_N(14) FMT_N(15) FMT_N(16) FMT_N(17) FMT_N(18) FMT_N(19) FMT_N(20))
FMT_PARSE(22, FMT_N(0) FMT_N(1) FMT_N(2) FMT_N(3) FMT_N(4) FMT_N(5) FMT_N(6) FMT_N(7) FMT_N(8) FMT_N(9) FMT_N(10) FMT_N(11) FMT_N(12) FMT_N(13) FMT_N(14) FMT_N(15) FMT_N(16) FMT_N(17) FMT_N(18) FMT_N(19) FMT_N(20) FMT_N(21))
FMT_PARSE(23, FMT_N(0) FMT_N(1) FMT_N(2) FMT_N(3) FMT_N(4) FMT_N(5) FMT_N(6) FMT_N(7) FMT_N(8) FMT_N(9) FMT_N(10) FMT_N(11) FMT_N(12) FMT_N(13) FMT_N(14) FMT_N(15) FMT_N(16) FMT_N(17) FMT_N(18) FMT_N(19) FMT_N(20) FMT_N(21) FMT_N(22))
FMT_PARSE(24, FMT_N(0) FMT_N(1) FMT_N(2) FMT_N(3) FMT_N(4) FMT_N(5) FMT_N(6) FMT_N(7) FMT_N(8) FMT_N(9) FMT_N(10) FMT_N(11) FMT_N(12) FMT_N(13) FMT_N(14) FMT_N(15) FMT_N(16) FMT_N(17) FMT_N(18) FMT_N(19) FMT_N(20) FMT_N(21) FMT_N(22) FMT_N(23))

从上述代码上看,我个人认为其实现的代码没有1.1.1节format简洁,并且其最大可支持的格式化参数为24个,这也是这个工具的缺陷。

使用方法

#include <iostream>
#include "sformat.h"int main()
{std::string format_str = SFormat("There are {0} fools in the world",10);std::cout << format_str << std::endl;return 0;
}

1.4 自定义的C++字符串格式化函数

我们可以使用可变参数模板+std::snprintf定义一个字符串格式化函数

// std::string的字符串格式化函数
template<typename ... Args>
static std::string str_format(const std::string &format, Args ... args)
{auto size_buf = std::snprintf(nullptr, 0, format.c_str(), args ...) + 1; std::unique_ptr<char[]> buf(new(std::nothrow) char[size_buf]);if (!buf)return std::string("");std::snprintf(buf.get(), size_buf, format.c_str(), args ...);return std::string(buf.get(), buf.get() + size_buf - 1); 
}// std::wstring的字符串格式化函数
template<typename ... Args>
static std::wstring wstr_format(const std::wstring &format, Args ... args)
{auto size_buf = std::snprintf(nullptr, 0, format.c_str(), args ...) + 1; std::unique_ptr<char[]> buf(new(std::nothrow) char[size_buf]);if (!buf)return std::wstring("");std::snprintf(buf.get(), size_buf, format.c_str(), args ...);return std::wstring(buf.get(), buf.get() + size_buf - 1); 
}

使用方法

#include <iostream>template<typename ... Args>
static std::string str_format(const std::string& format, Args ... args)
{auto size_buf = std::snprintf(nullptr, 0, format.c_str(), args ...) + 1;std::unique_ptr<char[]> buf(new(std::nothrow) char[size_buf]);if (!buf)return std::string("");std::snprintf(buf.get(), size_buf, format.c_str(), args ...);return std::string(buf.get(), buf.get() + size_buf - 1);
}int main()
{std::string format_str = str_format("There are %d fools in the world", 10);std::cout << format_str << std::endl;return 0;
}

1.5 C++20的字符串标准库函数std::format

经不住广大群众的吐槽,C++20标准终于推出了标准库的字符串格式化函数std::format,该函数既支持std::string也支持宽字符std::wstring的格式化。

函数原型

template<class... Args>
std::string format(std::string_view fmt, const Args&... args);template<class... Args>
std::wstring format(std::wstring_view fmt, const Args&... args);template<class... Args>
std::string format(const std::locale& loc, std::string_view fmt, const Args&... args);template<class... Args>
std::wstring format(const std::locale& loc, std::wstring_view fmt, const Args&... args);

函数使用

#include <iostream>
#include <format>int main()
{std::string format_str = std::format("There are {} fools in the world",10);std::cout << format_str << std::endl;return 0;
}

以上代码只能在支持C++20标准的编译器上编译通过,但是不得不感叹一句C++苦字符串格式化久矣!!!

欢迎访问我的个人站:https://www.stubbornhuang.com/


http://www.ppmy.cn/news/676269.html

相关文章

ultraEdit格式化代码

以C语言为例&#xff1a; 高级->配置工具&#xff1a; 命令行为&#xff1a; "D:\Program Files\UltraEdit\GNU\astyle.exe" --styleansi "%f" java代码的话把ansi改为java linux文件改为linux 其中前面为你的ultraEdit的安装目录中的GNU\astyle.ex…

Python格式化输出

Python格式化输出主要有三种方式&#xff0c;1、%-formatting&#xff1b;2、str.format();3、f-Strings %-formatting 字符串对象具有实用%运算符的内置操作&#xff0c;您可以使用它来格式化字符串。 以下是部分字符串格式符号&#xff1a; 符号说明%s字符串%c字符%i整数…

linux bios格式化磁盘,BIOS设置与磁盘分区、格式化.doc

项目2 BIOS设置与磁盘分区、格式化 教学目标 终极目标:能熟悉各种BIOS设置方法和步骤;能独立完成装机常用的BIOS设置;能熟练使用多种工具进行磁盘分区;能熟练使用工具进行磁盘的低级和高级格式化。 促成教学目标: 1. 能熟悉各种BIOS设置的方法和操作步骤 2. 能独立完成装机…

如何在windows下格式化linux,Linux、Windows/DO格式化怎么做?

格式化是什麽意思? 格式化(format)是指对磁盘或磁盘中的分区(partition)进行初始化的一种操作,这种操作通常会导致现有的磁盘或分区中所有的文件被清除。格式化通常分为低级格式化和高级格式化。如果没有特别指明,对硬盘的格式化通常是指高级格式化,而对软盘的格式化则通常…

java格式化到毫秒_java时间格式化到毫秒

(3)Java 日期时间及其格式化 可以用 System 类的静态方法 publ... java的时间处理续计算java时间)_计算机软件及应用_IT/计算机_专业资料。1. java 计算时间依靠 1970 年 1 月 1 日开始的毫秒数. 2. date 类的构造函数 date(...... 每个类方法返回一个以缺省格式化方式初始化的…

计算机中2种格式化,电脑的各种格式化有什么区别

高级格式化和低级格式化 格式化动作可分为高级格式化(high-level format)和低级格式化(low-level format)两种。软盘只有低级格式化(虽然看上去是高级格式化);而硬盘不仅有高级格式化,还有低级格式化的动作。低级格式化都是针对硬件的磁道为单位来工作,这个格式化动作是在硬…

格式化原理

有高级和低级格式化之分&#xff1a; 低级格式化&#xff1a;低级格式化就是将空白的磁盘划分出柱面和磁道&#xff0c;再将磁道划分为若干个扇区&#xff0c;每个扇区又划分出标识部分ID、间隔区GAP和数据区DATA等。可见&#xff0c;低级格式化是高级格式化之前的一件工作&…

计算机硬盘与格式化,电脑硬盘能格式化吗

多磁头技术:通过在同一碟片上增加多个磁头同时的读或写来为硬盘提速,或同时在多碟片同时利用磁头来读或写来为磁盘提速,多用于服务器和数据库中心。下面是学习啦小编带来的关于电脑硬盘能格式化吗的内容,欢迎阅读! 电脑硬盘能格式化吗? 可以格式化,如果是硬盘有逻辑坏道或…