O C ++ moderno torna isso super simples.
C ++ 20
O C ++ 20 apresenta std::format
, o que permite fazer exatamente isso. Ele usa campos de substituição semelhantes aos do python :
#include <iostream>
#include <format>
int main() {
std::cout << std::format("Hello {}!\n", "world");
}
Confira a documentação completa ! É uma enorme melhoria na qualidade de vida.
C ++ 11
Com o C ++ 11 s std::snprintf
, isso já se tornou uma tarefa bastante fácil e segura.
#include <memory>
#include <string>
#include <stdexcept>
template<typename ... Args>
std::string string_format( const std::string& format, Args ... args )
{
size_t size = snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0'
if( size <= 0 ){ throw std::runtime_error( "Error during formatting." ); }
std::unique_ptr<char[]> buf( new char[ size ] );
snprintf( buf.get(), size, format.c_str(), args ... );
return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside
}
O trecho de código acima está licenciado sob CC0 1.0 .
Linha por linha explicação:
Objetivo: escreva para achar*
usando std::snprintf
e depois converta para astd::string
.
Em primeiro lugar, determina-se o comprimento desejado da matriz de char usando uma condição especial snprintf
. Em cppreference.com :
Valor de retorno
[...] Se a sequência resultante for truncada devido ao limite de buf_size, a função retornará o número total de caracteres (não incluindo o byte nulo final) que teria sido gravado, se o limite não fosse imposto.
Isso significa que o tamanho desejado é o número de caracteres mais um , para que o terminador nulo fique atrás de todos os outros caracteres e que possa ser cortado pelo construtor de cadeias novamente. Esse problema foi explicado por @ alexk7 nos comentários.
size_t size = snprintf( nullptr, 0, format.c_str(), args ... ) + 1;
snprintf
retornará um número negativo se ocorrer um erro; portanto, verificamos se a formatação funcionou como desejado. Não fazer isso pode levar a erros silenciosos ou à alocação de um buffer enorme, como apontado pelo @ead nos comentários.
if( size <= 0 ){ throw std::runtime_error( "Error during formatting." ); }
Em seguida, alocamos uma nova matriz de caracteres e atribuímos a a std::unique_ptr
. Isso geralmente é recomendado, pois você não precisará manualmente manualmente delete
novamente.
Observe que essa não é uma maneira segura de alocar um unique_ptr
com tipos definidos pelo usuário, pois você não pode desalocar a memória se o construtor lançar uma exceção!
std::unique_ptr<char[]> buf( new char[ size ] );
Depois disso, é claro que podemos apenas usar snprintf
para o uso pretendido e escrever a string formatada no arquivo char[]
.
snprintf( buf.get(), size, format.c_str(), args ... );
Por fim, criamos e retornamos um novo std::string
, certificando-se de omitir o terminador nulo no final.
return std::string( buf.get(), buf.get() + size - 1 );
Você pode ver um exemplo em ação aqui .
Se você também deseja usar std::string
na lista de argumentos, dê uma olhada nesta essência .
Informações adicionais para o Visual Studio usuários do :
Conforme explicado nesta resposta , a Microsoft renomeou std::snprintf
para _snprintf
(sim, sem std::
). A Microsoft ainda o define como obsoleta e recomenda o uso _snprintf_s
, mas _snprintf_s
não aceita que o buffer seja zero ou menor que a saída formatada e não calculará o comprimento das saídas, se isso ocorrer. Portanto, para se livrar dos avisos de descontinuação durante a compilação, você pode inserir a seguinte linha na parte superior do arquivo que contém o uso de _snprintf
:
#pragma warning(disable : 4996)
Pensamentos finais
Muitas respostas a essa pergunta foram escritas antes do tempo do C ++ 11 e usam comprimentos de buffer fixos ou vargs. A menos que você esteja preso às versões antigas do C ++, eu não recomendaria o uso dessas soluções. Idealmente, siga o caminho C ++ 20.
Como a solução C ++ 11 nesta resposta usa modelos, ela pode gerar um pouco de código se for usada muito. No entanto, a menos que você esteja desenvolvendo para um ambiente com espaço muito limitado para binários, isso não será um problema e ainda será uma grande melhoria em relação às outras soluções, tanto em clareza quanto em segurança.
Se a eficiência do espaço for super importante, essas duas soluções com vargs e vsnprintf podem ser úteis.
NÃO USE soluções com comprimentos de buffer fixos, isso está apenas pedindo problemas.
boost::format
(como a solução da kennytm usa aqui ).boost::format
já suporta operadores de fluxo C ++ também! exemplo:cout << format("helloworld. a=%s, b=%s, c=%s") % 123 % 123.123 % "this is a test" << endl;
.boost::format
tem o mínimo de linhas de código ... é revisado por pares e se integra perfeitamente aos fluxos de C ++.