Il moderno C ++ lo rende super semplice.
C ++ 20
C ++ 20 introduce std::format
, che ti permette di fare esattamente questo. Utilizza campi di sostituzione simili a quelli in Python :
#include <iostream>
#include <format>
int main() {
std::cout << std::format("Hello {}!\n", "world");
}
Controlla la documentazione completa ! È un enorme miglioramento della qualità della vita.
C ++ 11
Con C ++ 11 s std::snprintf
, questo è già diventato un compito abbastanza facile e sicuro.
#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
}
Lo snippet di codice sopra riportato è concesso in licenza in CC0 1.0 .
Spiegazione riga per riga:
Obiettivo: scrivere in achar*
utilizzando std::snprintf
e quindi convertirlo in astd::string
.
Innanzitutto, determiniamo la lunghezza desiderata dell'array char usando una condizione speciale in snprintf
. Da cppreference.com :
Valore di ritorno
[...] Se la stringa risultante viene troncata a causa del limite buf_size, la funzione restituisce il numero totale di caratteri (escluso il byte null terminante) che sarebbero stati scritti se il limite non fosse stato imposto.
Ciò significa che la dimensione desiderata è il numero di caratteri più uno , in modo che il terminatore null siederà dopo tutti gli altri caratteri e che possa essere nuovamente tagliato dal costruttore di stringhe. Questo problema è stato spiegato da @ alexk7 nei commenti.
size_t size = snprintf( nullptr, 0, format.c_str(), args ... ) + 1;
snprintf
restituirà un numero negativo se si è verificato un errore, quindi controlliamo se la formattazione ha funzionato come desiderato. Non farlo potrebbe portare a errori silenziosi o all'allocazione di un enorme buffer, come sottolineato da @ead nei commenti.
if( size <= 0 ){ throw std::runtime_error( "Error during formatting." ); }
Successivamente, assegniamo un nuovo array di caratteri e lo assegniamo a std::unique_ptr
. Questo è generalmente consigliato, in quanto non sarà necessario manualmente di delete
nuovo.
Si noti che questo non è un modo sicuro per allocare un unique_ptr
con tipi definiti dall'utente in quanto non è possibile deallocare la memoria se il costruttore genera un'eccezione!
std::unique_ptr<char[]> buf( new char[ size ] );
Dopodiché, possiamo ovviamente usare solo snprintf
per l'uso previsto e scrivere la stringa formattata su char[]
.
snprintf( buf.get(), size, format.c_str(), args ... );
Infine, ne creiamo e ne restituiamo uno nuovo std::string
, assicurandoci di omettere il null-terminator alla fine.
return std::string( buf.get(), buf.get() + size - 1 );
Puoi vedere un esempio in azione qui .
Se vuoi usare anche std::string
nella lista degli argomenti, dai un'occhiata a questo riassunto .
Ulteriori informazioni per gli utenti di Visual Studio :
Come spiegato in questa risposta , Microsoft è stata rinominata std::snprintf
in _snprintf
(sì, senza std::
). MS lo imposta ulteriormente come obsoleto e consiglia di utilizzare _snprintf_s
invece, tuttavia _snprintf_s
non accetterà che il buffer sia zero o più piccolo dell'output formattato e non calcolerà la lunghezza degli output se ciò si verifica. Quindi, al fine di eliminare gli avvisi di deprecazione durante la compilazione, è possibile inserire la seguente riga nella parte superiore del file che contiene l'uso di _snprintf
:
#pragma warning(disable : 4996)
Pensieri finali
Molte risposte a questa domanda sono state scritte prima del tempo di C ++ 11 e usano lunghezze di buffer fisse o Varg. A meno che tu non sia bloccato con le vecchie versioni di C ++, non consiglierei di usare quelle soluzioni. Idealmente, vai in C ++ 20.
Poiché la soluzione C ++ 11 in questa risposta utilizza modelli, può generare un bel po 'di codice se viene usata molto. Tuttavia, a meno che non si stia sviluppando per un ambiente con uno spazio molto limitato per i file binari, questo non sarà un problema ed è comunque un grande miglioramento rispetto alle altre soluzioni in termini di chiarezza e sicurezza.
Se l'efficienza dello spazio è estremamente importante, queste due soluzioni con vargs e vsnprintf possono essere utili.
NON UTILIZZARE alcuna soluzione con buffer fissi, che richiede solo problemi.
boost::format
(come la soluzione di kennytm usa qui ).boost::format
supporta già anche gli operatori di stream C ++! Esempio:cout << format("helloworld. a=%s, b=%s, c=%s") % 123 % 123.123 % "this is a test" << endl;
.boost::format
ha il minor numero di righe di codice ... è peer-reviewed e si integra perfettamente con i flussi C ++.