Je dois formater std::string
avec sprintf
et l'envoyer en flux de fichiers. Comment puis-je faire ceci?
454
Je dois formater std::string
avec sprintf
et l'envoyer en flux de fichiers. Comment puis-je faire ceci?
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 ++.std::format
été ajouté à C ++ 20 BTW: stackoverflow.com/a/57286312/895245 Awesome!C++20
hier et j'ai vu queC++20
copiéboost
(pour la millionième fois maintenant) en ajoutant lestd::format
à laC++20
spécification! J'étais très très content! Presque tous les fichiers C ++ que j'ai écrits au cours des 9 dernières années ont été utilisésboost::format
. ajouter une sortie de style printf officielle aux flux en C ++ ira un long chemin IMO pour tout C ++.Réponses:
Vous ne pouvez pas le faire directement, car vous n'avez pas accès en écriture au tampon sous-jacent (jusqu'à C ++ 11; voir le commentaire de Dietrich Epp ). Vous devrez le faire d'abord dans une chaîne c, puis le copier dans une chaîne std :::
Mais je ne sais pas pourquoi vous n'utiliseriez pas simplement un flux de chaînes? Je suppose que vous avez des raisons spécifiques de ne pas simplement faire ceci:
la source
char buf[100];
fait cette solution peu robuste. Mais l'idée essentielle est là.asprintf
, qui alloue une nouvelle chaîne avec suffisamment d'espace pour contenir le résultat. Copiez ensuite cela sur unstd::string
si vous le souhaitez, et rappelez-vous defree
l'original. En outre, il est possible de mettre cela dans une macro afin que tout bon compilateur aide à valider le format pour vous - vous ne voulez pas mettre undouble
où un%s
est attenduLe 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 :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.L'extrait de code ci-dessus est sous licence CC0 1.0 .
Explication ligne par ligne:
Objectif: écrire dans a
char*
en utilisantstd::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 :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.
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.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!Après cela, nous pouvons bien sûr simplement utiliser
snprintf
comme prévu et écrire la chaîne formatée dans lechar[]
.Enfin, nous créons et retournons un nouveau à
std::string
partir de cela, en veillant à omettre le terminateur nul à la fin.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, sansstd::
). 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
: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.
la source
return string(&buf[0], size);
quelque chose de similaire. Deuxièmement, si vous deviez renvoyer une chaîne c comme celle-ci, cela provoquerait un comportement indéfini car le vecteur qui contient les valeurs vers lesquelles vous pointez sera invalidé au retour. Troisièmement, lorsque j'ai commencé à apprendre le C ++, la norme ne définissait pas dans quel ordre les éléments devaient être stockés dans unstd::vector
, donc accéder à son stockage via un pointeur était un comportement indéfini. Maintenant, cela fonctionnerait, mais je ne vois aucun avantage à le faire de cette façon.std::string
sera construit à partir du vecteur implicitement converti ( initialisation de la copie ), qui est ensuite renvoyé sous forme de copie, comme le suggère la signature de la fonction. De plus, les éléments d'unstd::vector
sont, et ont toujours été destinés à être stockés de manière contiguë . Mais je comprends votre point de vue que cela pourrait ne présenter aucun avantage.return string(buf.get(), buf.get() + size);
devrait êtrereturn string(buf.get(), buf.get() + size - 1);
sinon vous obtenez une chaîne avec un caractère nul à la fin. J'ai trouvé que c'était le cas sur gcc 4.9.Solution C ++ 11 qui utilise en
vsnprintf()
interne:Une approche plus sûre et plus efficace (je l'ai testée, et c'est plus rapide):
La
fmt_str
valeur est passée par valeur pour se conformer aux exigences deva_start
.REMARQUE: La version "plus sûre" et "plus rapide" ne fonctionne pas sur certains systèmes. Par conséquent, les deux sont toujours répertoriés. En outre, "plus rapide" dépend entièrement de l'étape de préallocation correcte, sinon le ralentit
strcpy
.la source
size
à chaque itération, quand vous pouvez l'obtenir par le premier appel devsnprintf()
.boost::format()
fournit les fonctionnalités que vous souhaitez:À partir du synopsis des bibliothèques de formats Boost:
la source
C ++ 20 inclura
std::format
ce qui ressemblesprintf
en termes d'API mais est entièrement sûr pour le type, fonctionne avec les types définis par l'utilisateur et utilise la syntaxe de chaîne de format de type Python. Voici comment vous pourrez le formaterstd::string
et l'écrire dans un flux:ou
Alternativement, vous pouvez utiliser la bibliothèque {fmt} pour formater une chaîne et l'écrire dans
stdout
ou un flux de fichiers en une seule fois:En ce qui concerne la
sprintf
plupart des autres réponses ici, malheureusement, elles utilisent des varargs et sont intrinsèquement dangereuses à moins que vous n'utilisiez quelque chose comme l'format
attribut de GCC qui ne fonctionne qu'avec des chaînes de format littérales. Vous pouvez voir pourquoi ces fonctions ne sont pas sûres dans l'exemple suivant:où
string_format
est une implémentation de la réponse d'Erik Aronesty. Ce code se compile, mais il se bloquera très probablement lorsque vous essayez de l'exécuter:Avertissement : je suis l'auteur de {fmt} et C ++ 20
std::format
.la source
error: 'fmt' has not been declared
fmt
implémentation similaire a été ajoutée à C ++ 20! stackoverflow.com/a/57286312/895245 fmt réclame actuellement le support pour cela. Super travail!Si vous ne voulez qu'une syntaxe de type printf (sans appeler printf vous-même), jetez un œil au format Boost .
la source
J'ai écrit le mien en utilisant vsnprintf pour qu'il renvoie une chaîne au lieu d'avoir à créer mon propre tampon.
Vous pouvez donc l'utiliser comme
la source
vsnprintf
directement dans la chaîne.std::unique_ptr<char[]> buffer (new char[size]);
Afin de formater
std::string
de manière «sprintf», appelezsnprintf
(argumentsnullptr
et0
) pour obtenir la longueur de tampon nécessaire. Écrivez votre fonction en utilisant un modèle variadic C ++ 11 comme ceci:Compilez avec le support C ++ 11, par exemple dans GCC:
g++ -std=c++11
Usage:
la source
char buf[length + 1];
place dechar* buf = new char[length + 1];
?char[]
etchar*
avec new, c'est que dans le premier cas buf serait alloué sur la pile. C'est correct pour les petits tampons, mais comme nous ne pouvons pas garantir la taille de la chaîne résultante, il est légèrement préférable de l'utilisernew
. Par exemple, sur ma machinestring_sprintf("value: %020000000d",5)
, imprimez un nombre scandaleux de zéros non significatifs avant le numéro 5, lesnew char[length + 1]
std::move
c'est superflu .[edit: 20/05/25] mieux encore ...:
Dans l'en-tête:
La
PRINTSTRING(r)
fonction est de répondre à l'interface graphique ou au terminal ou à tout besoin de sortie spécial en utilisant#ifdef _some_flag_
, la valeur par défaut est:[edit '17 / 8/31] Ajout d'une version de modèle variadic 'vtspf (..)':
qui est en fait une version délimitée par des virgules (à la place) des
<<
opérateurs parfois gênants, utilisés comme ceci:[modifier] Adapté pour utiliser la technique dans la réponse d'Erik Aronesty (ci-dessus):
[réponse précédente]
Une réponse très tardive, mais pour ceux qui, comme moi, aiment la méthode «sprintf»: j'ai écrit et j'utilise les fonctions suivantes. Si vous l'aimez, vous pouvez étendre les options% pour mieux correspondre à celles du sprintf; ceux qui s'y trouvent actuellement sont suffisants pour mes besoins. Vous utilisez stringf () et stringfappend () comme vous le feriez pour sprintf. N'oubliez pas que les paramètres de ... doivent être de type POD.
la source
fmt
comme référence cela fonctionne très bien. (Mais je suppose que les gens voudront jouer avec des jouets, alors ...) S'il y a d'autres supposés «bugs», pourriez-vous s'il vous plaît nous expliquer?Voici comment Google le fait:
StringPrintf
(Licence BSD)et Facebook le fait de manière assez similaire:
StringPrintf
(Licence Apache)Les deux offrent également un moyen pratique
StringAppendF
.la source
Mes deux cents sur cette question très populaire.
Pour citer la page de
printf
manuel des fonctions similaires :En d'autres termes, une implémentation saine de C ++ 11 devrait être la suivante:
Ça marche plutôt bien :)
Les modèles Variadic sont pris en charge uniquement en C ++ 11. La réponse de pixelpoint montre une technique similaire utilisant des styles de programmation plus anciens.
C'est bizarre que C ++ n'ait pas une telle chose hors de la boîte. Ils ont récemment ajouté
to_string()
, ce qui, à mon avis, est un grand pas en avant. Je me demande s'ils ajouteront un.format
opérateur à lastd::string
fin ...Éditer
Comme l'a souligné alexk7, A
+1
est nécessaire sur la valeur de retour destd::snprintf
, car nous devons avoir de l'espace pour l'\0
octet. Intuitivement, sur la plupart des architectures manquantes+1
, l'required
entier sera partiellement remplacé par a0
. Cela se produira après l'évaluation derequired
comme paramètre réel pourstd::snprintf
, donc l'effet ne devrait pas être visible.Ce problème pourrait cependant changer, par exemple avec l'optimisation du compilateur: que faire si le compilateur décide d'utiliser un registre pour la
required
variable? C'est le genre d'erreurs qui entraînent parfois des problèmes de sécurité.la source
bytes
tampon, probablement sur l'required
entier (qui, heureusement à ce moment-là, est déjà évalué).nullptr
comme argument de tampon, éliminant lachar b;
ligne dans votre code. ( Source )Utilisation de C99 snprintf et C ++ 11
la source
Testé, réponse de qualité de production
Cette réponse traite le cas général avec des techniques conformes aux normes. La même approche est donnée en exemple sur CppReference.com en bas de leur page. Contrairement à leur exemple, ce code correspond aux exigences de la question et est testé sur le terrain dans les applications de robotique et de satellite. Il a également amélioré les commentaires. La qualité de la conception est discutée plus loin ci-dessous.
Efficacité linéaire prévisible
Deux passes sont nécessaires pour une fonction réutilisable sécurisée, fiable et prévisible selon les spécifications de la question. Les présomptions sur la distribution des tailles de vargs dans une fonction réutilisable sont un mauvais style de programmation et doivent être évitées. Dans ce cas, des représentations de longueur variable arbitrairement grandes de vargs est un facteur clé dans le choix de l'algorithme.
Réessayer en cas de dépassement est exponentiellement inefficace, ce qui est une autre raison discutée lorsque le comité des normes C ++ 11 a discuté de la proposition ci-dessus de fournir un essai à vide lorsque le tampon d'écriture est nul.
Dans la mise en œuvre prête pour la production ci-dessus, le premier cycle est un tel cycle à sec pour déterminer la taille d'allocation. Aucune allocation n'a lieu. L'analyse des directives printf et la lecture des variables ont été rendues extrêmement efficaces au cours des décennies. Le code réutilisable doit être prévisible, même si une petite inefficacité pour les cas triviaux doit être sacrifiée.
Sécurité et fiabilité
Andrew Koenig a déclaré à un petit groupe d'entre nous après sa conférence lors d'un événement à Cambridge, "Les fonctions utilisateur ne devraient pas s'appuyer sur l'exploitation d'un échec pour des fonctionnalités exceptionnelles." Comme d'habitude, sa sagesse a été confirmée dans le dossier depuis. Les problèmes de bogues de sécurité fixes et fermés indiquent souvent des tentatives de piratage dans la description du trou exploité avant la correction.
Ceci est mentionné dans la proposition de révision des normes formelles pour la fonction de tampon nul dans Alternative to sprintf, proposition de révision C9X , document ISO CEI WG14 N645 / X3J11 96-008 . Une chaîne arbitrairement longue insérée par directive d'impression, "% s", dans les limites de la disponibilité de la mémoire dynamique, ne fait pas exception et ne doit pas être exploitée pour produire une "fonctionnalité non exceptionnelle".
Considérez la proposition à côté de l'exemple de code donné au bas de la page C ++ Reference.org liée au premier paragraphe de cette réponse.
De plus, le test des cas d'échec est rarement aussi robuste que les cas de réussite.
Portabilité
Tous les principaux fournisseurs de systèmes d'exploitation fournissent des compilateurs qui prennent entièrement en charge std :: vsnprintf dans le cadre des normes c ++ 11. Les hôtes exécutant des produits de fournisseurs qui ne maintiennent plus de distributions doivent être fournis avec g ++ ou clang ++ pour de nombreuses raisons.
Utilisation de la pile
L'utilisation de la pile lors du 1er appel à std :: vsnprintf sera inférieure ou égale à celle du 2e, et elle sera libérée avant le début du 2e appel. Si le premier appel dépasse la disponibilité de la pile, std :: fprintf échouera également.
la source
C ++ 20
std::format
C'est arrivé! La fonctionnalité est décrite sur: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0645r9.html et utilise une
.format()
syntaxe de type Python .J'espère que l'utilisation sera comme:
Je vais essayer quand le support arrivera à GCC, GCC 9.1.0
g++-9 -std=c++2a
ne le supporte toujours pas.L'API ajoutera un nouvel en-
std::format
tête:La
fmt
bibliothèque existante prétend l'implémenter si vous avez besoin du polyfill: https://github.com/fmtlib/fmtet a été mentionné précédemment à: std :: string formating like sprintf
la source
Sur la base de la réponse fournie par Erik Aronesty:
Cela évite d'avoir à rejeter
const
le résultat.c_str()
qui figurait dans la réponse d'origine.la source
la source
_vscprintf
. Je pense que vous devriez développer cette réponse.string n'a pas ce dont vous avez besoin, mais std :: stringstream en a. Utilisez un flux de chaînes pour créer la chaîne, puis extrayez la chaîne. Voici une liste complète des choses que vous pouvez faire. Par exemple:
vous donnera 10 décimales de précision lors de l'impression d'un double ou d'un flottant.
la source
Vous pouvez essayer ceci:
la source
Si vous êtes sur un système qui a asprintf (3) , vous pouvez facilement envelopper:
la source
format
, car elle indique à gcc de vérifier les types d'arguments et de donner un avertissement décent avec -Wall:std::string format(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
va_end
. "si va_end n'est pas appelé avant qu'une fonction qui appelle va_start ou va_copy ne revienne, le comportement n'est pas défini." - docsthrow std::bad_alloc();
, car je n'utilise pas d'exceptions C ++ dans ma base de code, et pour les gens qui le font, ils peuvent facilement l'ajouter en fonction sur le commentaire source et votre commentaire ici.C'est le code que j'utilise pour le faire dans mon programme ... Ce n'est pas compliqué, mais ça fait l'affaire ... Remarque, vous devrez ajuster votre taille le cas échéant. MAX_BUFFER pour moi est 1024.
la source
vsnprintf
directement dans la chaîne.Repris l'idée de la réponse de Dacav et pixelpoint . J'ai joué un peu et j'ai obtenu ceci:
Avec sain d' esprit pratique de programmation , je crois que le code devrait être assez, mais je suis toujours ouvert à des alternatives plus sûres qui sont encore assez simple et ne nécessitant pas de C ++ 11.
Et voici une autre version qui utilise un tampon initial pour empêcher un deuxième appel
vsnprintf()
lorsque le tampon initial est déjà suffisant.(Il s'avère que cette version est juste similaire à la réponse de Piti Ongmongkolkul , seulement qu'elle n'utilise pas
new
etdelete[]
, et spécifie également une taille lors de la créationstd::string
.L'idée ici de ne pas utiliser
new
etdelete[]
est d'impliquer l'utilisation de la pile sur le tas car elle n'a pas besoin d'appeler les fonctions d'allocation et de désallocation, mais si elle n'est pas correctement utilisée, il pourrait être dangereux de tamponner les débordements dans certains (peut-être anciens, ou peut-être simplement vulnérables). Si c'est un problème, je vous suggère fortement d'utilisernew
et à ladelete[]
place. Notez que la seule préoccupation ici concerne les allocations commevsnprintf()
on l'appelle déjà avec des limites, donc spécifier une limite basée sur la taille allouée sur le deuxième tampon les empêcherait également.)la source
J'utilise habituellement ceci:
Inconvénient: tous les systèmes ne prennent pas en charge vasprint
la source
Ci-dessous une version légèrement modifiée de la réponse @iFreilicht, mise à jour en C ++ 14 (utilisation de la
make_unique
fonction au lieu de la déclaration brute) et prise en charge desstd::string
arguments (basée sur l' article de Kenny Kerr )Production:
N'hésitez pas à fusionner cette réponse avec l'original si vous le souhaitez.
la source
La bibliothèque Poco Foundation a une fonction de format très pratique, qui prend en charge std :: string à la fois dans la chaîne de format et dans les valeurs:
la source
Vous pouvez formater la sortie C ++ dans cout à l'aide du fichier d'en-tête iomanip. Assurez-vous d'inclure le fichier d'en-tête iomanip avant d'utiliser l'une des fonctions d'assistance comme setprecision, setfill, etc.
Voici un extrait de code que j'ai utilisé dans le passé pour imprimer le temps d'attente moyen dans le vecteur, que j'ai "accumulé".
Voici une brève description de la façon dont nous pouvons formater les flux C ++. http://www.cprogramming.com/tutorial/iomanip.html
la source
Il peut y avoir des problèmes si le tampon n'est pas assez grand pour imprimer la chaîne. Vous devez déterminer la longueur de la chaîne formatée avant d'y imprimer un message formaté. Je fais mon propre assistant (testé sur Windows et Linux GCC ), et vous pouvez essayer de l'utiliser.
String.cpp: http://pastebin.com/DnfvzyKP
String.h: http://pastebin.com/7U6iCUMa
String.cpp:
String.h:
la source
vsnprintf((char *)dst.data(), dst.size() + 1, format, ap);
- Est-il sûr de supposer que le tampon de la chaîne a de la place pour un caractère nul final? Existe-t-il des implémentations qui n'allouent pas de taille + 1 caractères. Serait-il plus sûr de le fairedst.resize(length+1); vsnprintf((char *)dst.data(), dst.size(), format, ap); dst.resize(length);
data
etc_str
sont des synonymes.la source
Solution très très simple.
la source
Je réalise que cela a été répondu plusieurs fois, mais c'est plus concis:
exemple:
Voir aussi http://rextester.com/NJB14150
la source
MISE À JOUR 1 : ajout de
fmt::format
testsJ'ai mené ma propre enquête sur les méthodes introduites ici et j'ai obtenu des résultats diamétralement opposés par rapport à ceux mentionnés ici.
J'ai utilisé 4 fonctions sur 4 méthodes:
vsnprintf
+std::unique_ptr
vsnprintf
+std::string
std::ostringstream
+std::tuple
+utility::for_each
fmt::format
fonction de lafmt
bibliothèquePour le backend de test, le
googletest
a utilisé.L'
for_each
implémentation est prise à partir d'ici: itérer sur tupleLes tests:
Le
UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR
.unsued.hpp :
used.cpp :
RÉSULTATS :
Comme vous pouvez le voir, l'implémentation via le
vsnprintf
+std::string
est égale àfmt::format
, mais plus rapide que via levsnprintf
+std::unique_ptr
, qui est plus rapide que via lestd::ostringstream
.Les tests compilés
Visual Studio 2015 Update 3
et exécutés àWindows 7 x64 / Intel Core i7-4820K CPU @ 3.70GHz / 16GB
.la source