Le C ++ moderne rend cela super simple.
C ++ 20
C ++ 20 introduit std::format
, ce qui vous permet de faire exactement cela. Il utilise des champs de remplacement similaires à ceux de python :
#include <iostream>
#include <format>
int main() {
std::cout << std::format("Hello {}!\n", "world");
}
Consultez la documentation complète ! C'est une énorme amélioration de la qualité de vie.
C ++ 11
Avec C ++ 11 s std::snprintf
, cela est déjà devenu une tâche assez facile et sûre.
#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
}
L'extrait de code ci-dessus est sous licence CC0 1.0 .
Explication ligne par ligne:
Objectif: écrire dans achar*
en utilisant std::snprintf
puis convertir cela en astd::string
.
Tout d'abord, nous déterminons la longueur souhaitée du tableau char en utilisant une condition spéciale dans snprintf
. De cppreference.com :
Valeur de retour
[...] Si la chaîne résultante est tronquée à cause de buf_size limit, la fonction retourne le nombre total de caractères (sans compter l'octet nul final) qui auraient été écrits si la limite n'avait pas été imposée.
Cela signifie que la taille souhaitée est le nombre de caractères plus un , de sorte que le terminateur nul se place après tous les autres caractères et qu'il peut être à nouveau coupé par le constructeur de chaîne. Ce problème a été expliqué par @ alexk7 dans les commentaires.
size_t size = snprintf( nullptr, 0, format.c_str(), args ... ) + 1;
snprintf
retournera un nombre négatif en cas d'erreur, nous vérifions donc si le formatage a fonctionné comme souhaité. Ne pas le faire pourrait entraîner des erreurs silencieuses ou l'allocation d'un énorme tampon, comme indiqué par @ead dans les commentaires.
if( size <= 0 ){ throw std::runtime_error( "Error during formatting." ); }
Ensuite, nous allouons un nouveau tableau de caractères et l'affectons à a std::unique_ptr
. Ceci est généralement conseillé, car vous n'aurez pas à delete
le refaire manuellement .
Notez que ce n'est pas un moyen sûr d'allouer un unique_ptr
avec des types définis par l'utilisateur car vous ne pouvez pas désallouer la mémoire si le constructeur lève une exception!
std::unique_ptr<char[]> buf( new char[ size ] );
Après cela, nous pouvons bien sûr simplement utiliser snprintf
comme prévu et écrire la chaîne formatée dans le char[]
.
snprintf( buf.get(), size, format.c_str(), args ... );
Enfin, nous créons et retournons un nouveau à std::string
partir de cela, en veillant à omettre le terminateur nul à la fin.
return std::string( buf.get(), buf.get() + size - 1 );
Vous pouvez voir un exemple en action ici .
Si vous souhaitez également utiliser std::string
dans la liste des arguments, jetez un œil à cet élément essentiel .
Informations supplémentaires pour les utilisateurs de Visual Studio :
Comme expliqué dans cette réponse , Microsoft renommé std::snprintf
à _snprintf
(oui, sans std::
). MS le définit en outre comme obsolète et conseille de l'utiliser à la _snprintf_s
place, mais _snprintf_s
n'acceptera pas que le tampon soit nul ou inférieur à la sortie formatée et ne calculera pas la longueur des sorties si cela se produit. Ainsi, afin de vous débarrasser des avertissements de dépréciation lors de la compilation, vous pouvez insérer la ligne suivante en haut du fichier qui contient l'utilisation de _snprintf
:
#pragma warning(disable : 4996)
Dernières pensées
Beaucoup de réponses à cette question ont été écrites avant l'époque de C ++ 11 et utilisent des longueurs de tampon fixes ou vargs. À moins que vous ne soyez bloqué avec les anciennes versions de C ++, je ne recommanderais pas d'utiliser ces solutions. Idéalement, suivez la voie C ++ 20.
Étant donné que la solution C ++ 11 dans cette réponse utilise des modèles, elle peut générer un peu de code si elle est beaucoup utilisée. Cependant, à moins que vous ne développiez pour un environnement avec un espace très limité pour les binaires, cela ne sera pas un problème et constitue toujours une amélioration considérable par rapport aux autres solutions, à la fois en termes de clarté et de sécurité.
Si l'efficacité de l'espace est super importante, ces deux solutions avec vargs et vsnprintf peuvent être utiles.
N'UTILISEZ PAS de solutions avec des longueurs de tampon fixes, c'est juste pour des problèmes.
boost::format
(comme la solution de kennytm l'utilise ici ).boost::format
prend également en charge les opérateurs de flux C ++! exemple:cout << format("helloworld. a=%s, b=%s, c=%s") % 123 % 123.123 % "this is a test" << endl;
.boost::format
a le moins de lignes de code ... est évalué par les pairs et s'intègre bien avec les flux C ++.