C++ - Resumo dos métodos de formatação de string std::string

1 C++ std::string formatação de string

Em Python, podemos formatar strings convenientemente usando o seguinte código

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

Não apenas Python, strings também podem ser bem formatadas em outras linguagens de alto nível.

Este artigo resumirá os métodos de formatação de string em C++, incluindo:

  • Como formatar string em linguagem C
  • Como formatar strings em versões anteriores ao C++20
  • Função de biblioteca padrão de formatação de string C++ 20 std::format

1.1 Formatação de string em linguagem C

Na linguagem C podemos usar

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

Faça formatação de string, por ex.

#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++ usa std::stringstream para formatação de string

Em C++, a biblioteca padrão C++ não fornecia uma função de formatação de string padrão para a classe de string std::string antes do C++ 20. Só podemos juntar strings usando fluxos de string, std::stringstreamcomo

#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;
}

Esse tipo de código é realmente fedorento e longo, especialmente quando há muitos parâmetros de formatação.

1.3 Ferramenta de formatação de string de arquivo de cabeçalho único C++ de código aberto

Esta seção apresenta ferramentas de código aberto multiplataforma simples e fáceis de usar para formatação de strings antes do C++20.

Formato 1.3.1

Endereço Github: https://github.com/arajar/format

Esta é uma ferramenta de formatação de string padrão C++ 11 std::string com apenas um único arquivo de cabeçalho. Ela possui apenas um Format.harquivo. O código do arquivo de cabeçalho é o seguinte

#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();
	}
}

Instruções

#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 desmontagem

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

Esta também é uma ferramenta de formatação de string com apenas um único arquivo de cabeçalho. Ao usá-la, precisamos apenas incluir o sformat.harquivo de cabeçalho. O conteúdo do código do arquivo de cabeçalho é o seguinte

#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))

Pelo código acima, pessoalmente acho que o código implementado não é tão conciso quanto o formato na seção 1.1.1, e os parâmetros máximos de formato suportados são 24, o que também é o defeito desta ferramenta.

Instruções

#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 Função de formatação de string C++ personalizada

Podemos usar modelos variados + std::snprintfdefinir uma função de formatação de string

// 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); 
}

Instruções

#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 Função de biblioteca padrão de string C++ 20 std::format

Incapaz de suportar as reclamações das massas, o padrão C++20 finalmente introduziu a função de formatação de strings da biblioteca padrão std::format, que suporta formatação std::stringampla de caracteres std::wstring.

protótipo de função

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);

Uso de função

#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;
}

O código acima só pode ser compilado e passado em um compilador que suporte o padrão C++20, mas devo lamentar que o C++ sofra com a formatação de strings há muito tempo! ! !

Bem-vindo ao meu site pessoal: https://www.stubbornhuang.com/

Acho que você gosta

Origin blog.csdn.net/HW140701/article/details/127897839
Recomendado
Clasificación