Modernes C ++ macht dies super einfach.
C ++ 20
C ++ 20 wird eingeführt std::format
, mit dem Sie genau das tun können. Es werden Ersatzfelder verwendet, die denen in Python ähneln :
#include <iostream>
#include <format>
int main() {
std::cout << std::format("Hello {}!\n", "world");
}
Lesen Sie die vollständige Dokumentation ! Es ist eine enorme Verbesserung der Lebensqualität.
C ++ 11
Mit C ++ 11 s std::snprintf
, wurde dies bereits eine ziemlich einfache und sichere Aufgabe.
#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
}
Das obige Code-Snippet ist unter CC0 1.0 lizenziert .
Zeile für Zeile Erklärung:
Ziel: Schreibe einechar*
durchVerwendung std::snprintf
und dann konvertierendas zu einstd::string
.
Zuerst bestimmen wir die gewünschte Länge des char-Arrays unter Verwendung einer speziellen Bedingung in snprintf
. Von cppreference.com :
Rückgabewert
[...] Wenn die resultierende Zeichenfolge aufgrund des Grenzwerts für buf_size abgeschnitten wird, gibt die Funktion die Gesamtzahl der Zeichen (ohne das abschließende Nullbyte) zurück, die geschrieben worden wären, wenn der Grenzwert nicht festgelegt worden wäre.
Dies bedeutet, dass die gewünschte Größe die Anzahl der Zeichen plus eins ist , sodass der Nullterminator hinter allen anderen Zeichen steht und vom Zeichenfolgenkonstruktor wieder abgeschnitten werden kann. Dieses Problem wurde von @ alexk7 in den Kommentaren erklärt.
size_t size = snprintf( nullptr, 0, format.c_str(), args ... ) + 1;
snprintf
gibt eine negative Zahl zurück, wenn ein Fehler aufgetreten ist. Daher prüfen wir, ob die Formatierung wie gewünscht funktioniert hat. Wenn Sie dies nicht tun, kann dies zu stillen Fehlern oder zur Zuweisung eines großen Puffers führen, wie @ead in den Kommentaren hervorhebt.
if( size <= 0 ){ throw std::runtime_error( "Error during formatting." ); }
Als nächstes weisen wir ein neues Zeichenarray zu und weisen es einem zu std::unique_ptr
. Dies wird im Allgemeinen empfohlen, da Sie es nicht delete
erneut manuell ausführen müssen.
Beachten Sie, dass dies keine sichere Methode zum Zuweisen eines unique_ptr
mit benutzerdefinierten Typen ist, da Sie den Speicher nicht freigeben können, wenn der Konstruktor eine Ausnahme auslöst!
std::unique_ptr<char[]> buf( new char[ size ] );
Danach können wir natürlich nur snprintf
für den vorgesehenen Verwendungszweck verwenden und den formatierten String in das schreiben char[]
.
snprintf( buf.get(), size, format.c_str(), args ... );
Schließlich erstellen wir ein neues und geben es zurück, wobei wir std::string
sicherstellen, dass der Null-Terminator am Ende weggelassen wird.
return std::string( buf.get(), buf.get() + size - 1 );
Sie können ein Beispiel in Aktion sehen hier .
Wenn Sie auch std::string
in der Argumentliste verwenden möchten, werfen Sie einen Blick auf diesen Kern .
Zusätzliche Informationen für Visual Studio- Benutzer:
Wie in erklärt diese Antwort , umbenannt Microsoft std::snprintf
auf _snprintf
(ja, ohne std::
). MS setzt es weiter als veraltet und empfiehlt, es _snprintf_s
stattdessen zu verwenden , _snprintf_s
akzeptiert jedoch nicht, dass der Puffer Null oder kleiner als die formatierte Ausgabe ist, und berechnet in diesem Fall nicht die Ausgabelänge. Um die Verfallswarnungen während der Kompilierung zu entfernen, können Sie die folgende Zeile oben in die Datei einfügen, die Folgendes enthält _snprintf
:
#pragma warning(disable : 4996)
Abschließende Gedanken
Viele Antworten auf diese Frage wurden vor C ++ 11 geschrieben und verwenden feste Pufferlängen oder Variablen. Wenn Sie nicht mit alten Versionen von C ++ feststecken, würde ich die Verwendung dieser Lösungen nicht empfehlen. Im Idealfall gehen Sie den C ++ 20-Weg.
Da die C ++ 11-Lösung in dieser Antwort Vorlagen verwendet, kann sie viel Code generieren, wenn sie häufig verwendet wird. Sofern Sie nicht für eine Umgebung mit sehr begrenztem Speicherplatz für Binärdateien entwickeln, ist dies kein Problem und stellt in Bezug auf Klarheit und Sicherheit immer noch eine enorme Verbesserung gegenüber den anderen Lösungen dar.
Wenn die Raumeffizienz sehr wichtig ist, können diese beiden Lösungen mit vargs und vsnprintf nützlich sein.
Verwenden Sie keine Lösungen mit festen Pufferlängen, da dies nur zu Problemen führt.
boost::format
(wie Kennytms Lösung hier verwendet ).boost::format
unterstützt auch bereits C ++ - Stream-Operatoren! Beispiel :cout << format("helloworld. a=%s, b=%s, c=%s") % 123 % 123.123 % "this is a test" << endl;
.boost::format
hat die wenigsten Codezeilen ... wird von Experten begutachtet und lässt sich gut in C ++ - Streams integrieren.