std:: formatage des chaînes comme sprintf
je dois formater std::string
avec sprintf
et l'envoyer dans file stream. Comment puis-je faire cela?
30 réponses
vous ne pouvez pas le faire directement, parce que vous n'avez pas d'accès en écriture au tampon sous-jacent (jusqu'à C++11; voir le commentaire De Dietrich Epp 151940920" ). Vous devrez d'abord le faire dans un c-string, puis le copier dans un std:: string:
char buff[100];
snprintf(buff, sizeof(buff), "%s", "Hello");
std::string buffAsStdStr = buff;
mais pourquoi ne pas utiliser un stream? Je suppose que vous avez des raisons précises de ne pas simplement faire cela:
std::ostringstream stringStream;
stringStream << "Hello";
std::string copyOfStr = stringStream.str();
C++11 solution qui utilise vsnprintf()
en interne:
#include <stdarg.h> // For va_start, etc.
std::string string_format(const std::string fmt, ...) {
int size = ((int)fmt.size()) * 2 + 50; // Use a rubric appropriate for your code
std::string str;
va_list ap;
while (1) { // Maximum two passes on a POSIX system...
str.resize(size);
va_start(ap, fmt);
int n = vsnprintf((char *)str.data(), size, fmt.c_str(), ap);
va_end(ap);
if (n > -1 && n < size) { // Everything worked
str.resize(n);
return str;
}
if (n > -1) // Needed size returned
size = n + 1; // For null char
else
size *= 2; // Guess at a larger size (OS specific)
}
return str;
}
plus sûr et plus efficace (je l'ai testé, et c'est plus rapide) approche:
#include <stdarg.h> // For va_start, etc.
#include <memory> // For std::unique_ptr
std::string string_format(const std::string fmt_str, ...) {
int final_n, n = ((int)fmt_str.size()) * 2; /* Reserve two times as much as the length of the fmt_str */
std::unique_ptr<char[]> formatted;
va_list ap;
while(1) {
formatted.reset(new char[n]); /* Wrap the plain char array into the unique_ptr */
strcpy(&formatted[0], fmt_str.c_str());
va_start(ap, fmt_str);
final_n = vsnprintf(&formatted[0], n, fmt_str.c_str(), ap);
va_end(ap);
if (final_n < 0 || final_n >= n)
n += abs(final_n - n + 1);
else
break;
}
return std::string(formatted.get());
}
le fmt_str
est passé par la valeur pour se conformer aux exigences de va_start
.
NOTE: la version" plus sûre "et" plus rapide " ne fonctionne pas sur certains systèmes. Les deux sont donc toujours répertoriés. En outre, "plus rapide" dépend entièrement de l'étape de préallocation étant correct, sinon le strcpy
le rend plus lent.
utilisant C++11 std::snprintf
, cela devient une tâche facile et sûre. Je vois beaucoup de réponses à cette question qui ont apparemment été écrites avant L'époque du C++11 qui utilise des longueurs de tampon et des vargs fixes, ce que je ne recommanderais pas pour des raisons de sécurité, d'efficacité et de clarté.
#include <memory>
#include <iostream>
#include <string>
#include <cstdio>
using namespace std; //Don't if you're in a header-file
template<typename ... Args>
string string_format( const std::string& format, Args ... args )
{
size_t size = snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '"151900920"'
unique_ptr<char[]> buf( new char[ size ] );
snprintf( buf.get(), size, format.c_str(), args ... );
return string( buf.get(), buf.get() + size - 1 ); // We don't want the '"151900920"' inside
}
l'extrait de code ci-dessus est sous licence CC0 1.0 .
explication ligne par ligne:
" Aim: écrivez à un char*
en utilisant std::snprintf
et convertissez-le en un std::string
.
tout d'abord, nous déterminons la longueur désirée du tableau de char.
de cppreference.com :
valeur de retour
[...] Si la chaîne est tronquée en raison de buf_size limite, la fonction renvoie le nombre de caractères (non compris le fin de nul-byte) qui aurait été écrit, si la limite avait été pas imposées.
cela signifie que la taille désirée est le nombre de caractères plus un , de sorte que le terminateur nul sera assis après tous les autres caractères et qu'il peut être coupée par la chaîne de constructeur. Cette question a été expliquée par @alexk7 dans les commentaires.
ensuite, nous attribuons un nouveau tableau de caractères et l'assignons à un std::unique_ptr
. Ceci est généralement conseillé, car vous n'aurez pas à delete
manuellement à nouveau.
notez que ce n'est pas une façon sûre 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 lance une exception!
après cela, nous pouvons bien sûr simplement utiliser snprintf
pour son utilisation prévue et écrire la chaîne formatée au char[]
et ensuite créer et retourner un nouveau std::string
à partir de cela.
vous pouvez voir un exemple dans action ici .
Si vous voulez aussi utiliser std::string
dans la liste d'arguments, jetez un oeil à ce gist .
informations supplémentaires pour Visual Studio utilisateurs:
comme expliqué dans cette réponse , Microsoft renommé std::snprintf
à _snprintf
(oui, sans std::
). MS l'a ensuite défini comme déprécié et conseille d'utiliser _snprintf_s
à la place, cependant _snprintf_s
n'acceptera pas le tampon à zéro ou plus petit que le formaté la sortie et ne calculera pas la longueur des sorties si cela se produit.
Afin de se débarrasser de la dépréciation des avertissements 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)
boost::format()
fournit la fonctionnalité que vous voulez:
depuis le Boost format bibliothèques synopsis:
un objet format est construit à partir d'une chaîne de format, et est ensuite donné arguments par des appels répétés à l'opérateur%. Chacun de ces arguments est ensuite converti en chaînes, qui sont à leur tour combinées en une chaîne, selon le format-string.
#include <boost/format.hpp>
cout << boost::format("writing %1%, x=%2% : %3%-th try") % "toto" % 40.23 % 50;
// prints "writing toto, x=40.230 : 50-th try"
malheureusement, la plupart des réponses ici utilisent des varargs qui sont intrinsèquement dangereux sauf si vous utilisez quelque chose comme L'attribut format
de GCC qui ne fonctionne qu'avec des chaînes de format littéral. Vous pouvez voir pourquoi ces fonctions sont dangereuses sur l'exemple suivant:
std::string format_str = "%s";
string_format(format_str, format_str[0]);
où string_format
est une implémentation de la réponse D'Erik Aronesty. Ce code se compile, mais il va très probablement planter quand vous essayez de l'exécuter:
$ g++ -Wall -Wextra -pedantic test.cc
$ ./a.out
Segmentation fault: 11
il est possible d'implémenter un printf
et de l'étendre au format std::string
en utilisant des gabarits (variadiques). Cela a été fait dans la bibliothèque fmt , qui fournit une alternative sûre à sprintf
retour std::string
:
std::string format_str = "The answer is %d";
std::string result = fmt::sprintf(format_str, 42);
fmt garde trace des types d'arguments et si le type ne correspond pas aux spécifications de format, il n'y a pas de défaut de segmentation, juste une exception.
Avertissement : je suis l'auteur de cette bibliothèque.
si vous voulez seulement une syntaxe de type printf (sans appeler printf vous-même), regardez Boost Format .
j'ai écrit le mien en utilisant vsnprintf donc il retourne string au lieu d'avoir à créer mon propre buffer.
#include <string>
#include <cstdarg>
//missing string printf
//this is safe and convenient but not exactly efficient
inline std::string format(const char* fmt, ...){
int size = 512;
char* buffer = 0;
buffer = new char[size];
va_list vl;
va_start(vl, fmt);
int nsize = vsnprintf(buffer, size, fmt, vl);
if(size<=nsize){ //fail delete buffer and try again
delete[] buffer;
buffer = 0;
buffer = new char[nsize+1]; //+1 for /0
nsize = vsnprintf(buffer, size, fmt, vl);
}
std::string ret(buffer);
va_end(vl);
delete[] buffer;
return ret;
}
donc vous pouvez l'utiliser comme
std::string mystr = format("%s %d %10.5f", "omg", 1, 10.5);
[modifier " 17/8/31] Ajout d'un variadic basé sur un modèle de la version 'vtspf(..)
template<typename T> const std::string type_to_string(const T &v)
{
std::ostringstream ss;
ss << v;
return ss.str();
};
template<typename T> const T string_to_type(const std::string &str)
{
std::istringstream ss(str);
T ret;
ss >> ret;
return ret;
};
template<typename...P> void vtspf_priv(std::string &s) {}
template<typename H, typename...P> void vtspf_priv(std::string &s, H h, P...p)
{
s+=type_to_string(h);
vtspf_priv(s, p...);
}
template<typename...P> std::string temp_vtspf(P...p)
{
std::string s("");
vtspf_priv(s, p...);
return s;
}
qui est en fait une version délimitée par une virgule (au lieu de) des opérateurs parfois gênants <<
, utilisés comme ceci:
char chSpace=' ';
double pi=3.1415;
std::string sWorld="World", str_var;
str_var = vtspf("Hello", ',', chSpace, sWorld, ", pi=", pi);
[edit] adapté pour faire usage de la technique dans la réponse D'Erik Aronesty (ci-dessus):
#include <string>
#include <cstdarg>
#include <cstdio>
//=============================================================================
void spf(std::string &s, const std::string fmt, ...)
{
int n, size=100;
bool b=false;
va_list marker;
while (!b)
{
s.resize(size);
va_start(marker, fmt);
n = vsnprintf((char*)s.c_str(), size, fmt.c_str(), marker);
va_end(marker);
if ((n>0) && ((b=(n<size))==true)) s.resize(n); else size*=2;
}
}
//=============================================================================
void spfa(std::string &s, const std::string fmt, ...)
{
std::string ss;
int n, size=100;
bool b=false;
va_list marker;
while (!b)
{
ss.resize(size);
va_start(marker, fmt);
n = vsnprintf((char*)ss.c_str(), size, fmt.c_str(), marker);
va_end(marker);
if ((n>0) && ((b=(n<size))==true)) ss.resize(n); else size*=2;
}
s += ss;
}
[précédente réponse]
Une réponse très tardive, mais pour ceux qui, comme moi, font comme le chemin "sprintf": j'ai écrit et j'utilise les fonctions suivantes. Si vous le souhaitez, vous pouvez étendre les options %pour les adapter plus étroitement à celles de sprintf; celles-ci sont actuellement suffisantes pour mes besoins.
Vous utilisez stringf() et stringfappend () comme vous le feriez pour sprintf. Souvenez-vous que les paramètres pour ... doit être POD types.
//=============================================================================
void DoFormatting(std::string& sF, const char* sformat, va_list marker)
{
char *s, ch=0;
int n, i=0, m;
long l;
double d;
std::string sf = sformat;
std::stringstream ss;
m = sf.length();
while (i<m)
{
ch = sf.at(i);
if (ch == '%')
{
i++;
if (i<m)
{
ch = sf.at(i);
switch(ch)
{
case 's': { s = va_arg(marker, char*); ss << s; } break;
case 'c': { n = va_arg(marker, int); ss << (char)n; } break;
case 'd': { n = va_arg(marker, int); ss << (int)n; } break;
case 'l': { l = va_arg(marker, long); ss << (long)l; } break;
case 'f': { d = va_arg(marker, double); ss << (float)d; } break;
case 'e': { d = va_arg(marker, double); ss << (double)d; } break;
case 'X':
case 'x':
{
if (++i<m)
{
ss << std::hex << std::setiosflags (std::ios_base::showbase);
if (ch == 'X') ss << std::setiosflags (std::ios_base::uppercase);
char ch2 = sf.at(i);
if (ch2 == 'c') { n = va_arg(marker, int); ss << std::hex << (char)n; }
else if (ch2 == 'd') { n = va_arg(marker, int); ss << std::hex << (int)n; }
else if (ch2 == 'l') { l = va_arg(marker, long); ss << std::hex << (long)l; }
else ss << '%' << ch << ch2;
ss << std::resetiosflags (std::ios_base::showbase | std::ios_base::uppercase) << std::dec;
}
} break;
case '%': { ss << '%'; } break;
default:
{
ss << "%" << ch;
//i = m; //get out of loop
}
}
}
}
else ss << ch;
i++;
}
va_end(marker);
sF = ss.str();
}
//=============================================================================
void stringf(string& stgt,const char *sformat, ... )
{
va_list marker;
va_start(marker, sformat);
DoFormatting(stgt, sformat, marker);
}
//=============================================================================
void stringfappend(string& stgt,const char *sformat, ... )
{
string sF = "";
va_list marker;
va_start(marker, sformat);
DoFormatting(sF, sformat, marker);
stgt += sF;
}
pour formater std::string
de manière 'sprintf', appelez snprintf
(arguments nullptr
et 0
) pour obtenir la longueur de tampon nécessaire. Écrivez votre fonction en utilisant C++11 Modèle variadic comme ceci:
#include <cstdio>
#include <string>
#include <cassert>
template< typename... Args >
std::string string_sprintf( const char* format, Args... args ) {
int length = std::snprintf( nullptr, 0, format, args... );
assert( length >= 0 );
char* buf = new char[length + 1];
std::snprintf( buf, length + 1, format, args... );
std::string str( buf );
delete[] buf;
return std::move(str);
}
compiler avec le support C++11, par exemple dans GCC: g++ -std=c++11
Utilisation:
std::cout << string_sprintf("%g, %g\n", 1.23, 0.001);
voici comment google le fait: StringPrintf
(licence BSD)
et facebook le fait d'une manière assez similaire: StringPrintf
(Apache License)
Tous les deux disposent d'un pratique StringAppendF
aussi.
mes deux cents sur cette question très populaire.
pour citer la page de manuel de printf
- comme des fonctions :
lors d'un retour réussi, ces fonctions renvoient le nombre de caractères imprimés (à l'exclusion du octet nul utilisé pour terminer la sortie vers les chaînes).
les fonctions snprintf() et vsnprintf () n'écrivent pas plus que des octets de taille (incluant la terminaison null octet ('\0')). Si la sortie a été tronquée à cause de cette limite, alors la valeur de retour est le nombre de caractères (à l'exclusion du caractère null byte final) qui auraient été écrits dans la chaîne finale si suffisamment d'espace avait été disponible. Ainsi, une valeur de retour de taille ou plus signifie que la sortie a été tronquée.
en d'autres termes, une implémentation sane C++11 devrait être la suivante:
#include <string>
#include <cstdio>
template <typename... Ts>
std::string fmt (const std::string &fmt, Ts... vs)
{
char b;
size_t required = std::snprintf(&b, 0, fmt.c_str(), vs...) + 1;
// See comments: the +1 is necessary, while the first parameter
// can also be set to nullptr
char bytes[required];
std::snprintf(bytes, required, fmt.c_str(), vs...);
return std::string(bytes);
}
ça marche plutôt bien :)
Les modèles variadiquesne sont supportés qu'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 dans la boite. Ils ont récemment ajouté to_string()
, qui à mon avis est un grand pas en avant. Je me demande s'ils vont éventuellement ajouter un opérateur .format
au std::string
...
Edit
comme alexk7 l'a souligné, un +1
est nécessaire sur la valeur de retour de std::snprintf
, puisque nous avons besoin d'espace pour le byte " 151970920"
. Intuitivement, sur la plupart des architectures, l'absence du +1
provoque l'écrasement partiel de l'entier required
par un 0
. Cela se produira après l'évaluation de required
comme paramètre réel pour std::snprintf
, de sorte que l'effet ne devrait pas être visible.
ce problème pourrait toutefois changer, par exemple avec l'optimisation du compilateur: et si le compilateur décide d'utiliser un registre pour la variable required
? C'est le genre d'erreurs qui entraîne parfois des problèmes de sécurité.
template<typename... Args>
std::string string_format(const char* fmt, Args... args)
{
size_t size = snprintf(nullptr, 0, fmt, args...);
std::string buf;
buf.reserve(size + 1);
buf.resize(size);
snprintf(&buf[0], size + 1, fmt, args...);
return buf;
}
utilisant C99 snprintf et C++11
basé sur la réponse fournie par Erik Aronesty:
std::string string_format(const std::string &fmt, ...) {
std::vector<char> str(100,'"151900920"');
va_list ap;
while (1) {
va_start(ap, fmt);
auto n = vsnprintf(str.data(), str.size(), fmt.c_str(), ap);
va_end(ap);
if ((n > -1) && (size_t(n) < str.size())) {
return str.data();
}
if (n > -1)
str.resize( n + 1 );
else
str.resize( str.size() * 2);
}
return str.data();
}
cela évite la nécessité de rejeter const
du résultat de .c_str()
qui était dans la réponse originale.
inline void format(string& a_string, const char* fmt, ...)
{
va_list vl;
va_start(vl, fmt);
int size = _vscprintf( fmt, vl );
a_string.resize( ++size );
vsnprintf_s((char*)a_string.data(), size, _TRUNCATE, fmt, vl);
va_end(vl);
}
chaîne n'a pas ce que vous avez besoin, mais std::stringstream fait. Utilisez un stringstream pour créer la chaîne, puis extraire la chaîne. Ici est une liste de choses que vous pouvez faire. Par exemple:
cout.setprecision(10); //stringstream is a stream like cout
vous donnera 10 décimales de précision lors de l'impression d'un lit double ou float.
vous pourriez essayer ceci:
string str;
str.resize( _MAX_PATH );
sprintf( &str[0], "%s %s", "hello", "world" );
// optionals
// sprintf_s( &str[0], str.length(), "%s %s", "hello", "world" ); // Microsoft
// #include <stdio.h>
// snprintf( &str[0], str.length(), "%s %s", "hello", "world" ); // c++11
str.resize( strlen( str.data() ) + 1 );
C'est le code que j'utilise pour ce faire dans mon programme... Ce n'est rien de fantaisiste, mais ça fait l'affaire... Remarque: vous devrez ajuster votre taille au besoin. MAX_BUFFER pour moi est 1024.
std::string Format ( const char *fmt, ... )
{
char textString[MAX_BUFFER*5] = {'"151900920"'};
// -- Empty the buffer properly to ensure no leaks.
memset(textString, '"151900920"', sizeof(textString));
va_list args;
va_start ( args, fmt );
vsnprintf ( textString, MAX_BUFFER*5, fmt, args );
va_end ( args );
std::string retStr = textString;
return retStr;
}
a pris l'idée de Dacav et pixelpoint réponse . J'ai joué un peu et j'ai eu ça:
#include <cstdarg>
#include <cstdio>
#include <string>
std::string format(const char* fmt, ...)
{
va_list vl;
va_start(vl, fmt);
int size = vsnprintf(0, 0, fmt, vl) + sizeof('"151900920"');
va_end(vl);
char buffer[size];
va_start(vl, fmt);
size = vsnprintf(buffer, size, fmt, vl);
va_end(vl);
return std::string(buffer, size);
}
Avec sain d'esprit en programmation, je crois que le code devrait être suffisant, mais je suis toujours ouvert à des alternatives plus sûres qui sont encore assez simples et ne nécessitent pas le C++11.
et voici une autre version qui utilise un tampon initial pour empêcher le deuxième appel à vsnprintf()
quand le tampon initial est déjà suffisant.
std::string format(const char* fmt, ...)
{
va_list vl;
int size;
enum { INITIAL_BUFFER_SIZE = 512 };
{
char buffer[INITIAL_BUFFER_SIZE];
va_start(vl, fmt);
size = vsnprintf(buffer, INITIAL_BUFFER_SIZE, fmt, vl);
va_end(vl);
if (size < INITIAL_BUFFER_SIZE)
return std::string(buffer, size);
}
size += sizeof('"151910920"');
char buffer[size];
va_start(vl, fmt);
size = vsnprintf(buffer, size, fmt, vl);
va_end(vl);
return std::string(buffer, size);
}
(il s'avère que cette version est juste similaire à réponse de Piti Ongmongkolkul , seulement qu'il n'utilise pas new
et delete[]
, et spécifie également une taille lors de la création std::string
.
l'idée ici de ne pas utiliser new
et delete[]
est de implique l'utilisation de la pile sur le tas puisqu'il n'a pas besoin d'appeler les fonctions d'allocation et de désallocation, cependant s'il n'est pas correctement utilisé, il pourrait être dangereux de buffer les débordements dans certains systèmes (peut-être anciens, ou peut-être juste vulnérables). Si c'est un problème, je suggère fortement d'utiliser new
et delete[]
à la place. Notez que la seule préoccupation ici est au sujet des allocations car vsnprintf()
est déjà appelé avec des limites, donc en spécifiant une limite basée sur la taille attribuée sur la deuxième un tampon les empêcherait aussi.)
j'utilise habituellement ceci:
std::string myformat(const char *const fmt, ...)
{
char *buffer = NULL;
va_list ap;
va_start(ap, fmt);
(void)vasprintf(&buffer, fmt, ap);
va_end(ap);
std::string result = buffer;
free(buffer);
return result;
}
désavantage: tous les systèmes ne supportent pas vasprint
Si vous êtes sur un système qui a asprintf(3) , vous pouvez facilement l'envelopper:
#include <iostream>
#include <cstdarg>
#include <cstdio>
std::string format(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
std::string format(const char *fmt, ...)
{
std::string result;
va_list ap;
va_start(ap, fmt);
char *tmp = 0;
int res = vasprintf(&tmp, fmt, ap);
va_end(ap);
if (res != -1) {
result = tmp;
free(tmp);
} else {
// The vasprintf call failed, either do nothing and
// fall through (will return empty string) or
// throw an exception, if your code uses those
}
return result;
}
int main(int argc, char *argv[]) {
std::string username = "you";
std::cout << format("Hello %s! %d", username.c_str(), 123) << std::endl;
return 0;
}
solution très simple.
std::string strBuf;
strBuf.resize(256);
int iCharsPrinted = sprintf_s((char *)strPath.c_str(), strPath.size(), ...);
strBuf.resize(iCharsPrinted);
ci-dessous la version légèrement modifiée de la réponse @iFreilicht, mise à jour à C++14 (utilisation de la fonction make_unique
au lieu de la déclaration brute) et ajouté le support pour std::string
arguments (basé sur Kenny Kerr article )
#include <iostream>
#include <memory>
#include <string>
#include <cstdio>
template <typename T>
T process_arg(T value) noexcept
{
return value;
}
template <typename T>
T const * process_arg(std::basic_string<T> const & value) noexcept
{
return value.c_str();
}
template<typename ... Args>
std::string string_format(const std::string& format, Args const & ... args)
{
const auto fmt = format.c_str();
const size_t size = std::snprintf(nullptr, 0, fmt, process_arg(args) ...) + 1;
auto buf = std::make_unique<char[]>(size);
std::snprintf(buf.get(), size, fmt, process_arg(args) ...);
auto res = std::string(buf.get(), buf.get() + size - 1);
return res;
}
int main()
{
int i = 3;
float f = 5.f;
char* s0 = "hello";
std::string s1 = "world";
std::cout << string_format("i=%d, f=%f, s=%s %s", i, f, s0, s1) << "\n";
}
sortie:
i = 3, f = 5.000000, s = hello world
n'hésitez pas à fusionner cette réponse avec la réponse originale si vous le souhaitez.
Testé, Réponse De Qualité De Production
Cette réponse traite le cas général avec conforme aux normes techniques. La même approche est donnée en exemple sur CppReference.com près du bas de la page.
#include <string>
#include <cstdarg>
#include <vector>
// requires at least C++11
const std::string vformat(const char * const zcFormat, ...) {
// initialize use of the variable argument array
va_list vaArgs;
va_start(vaArgs, zcFormat);
// reliably acquire the size
// from a copy of the variable argument array
// and a functionally reliable call to mock the formatting
va_list vaArgsCopy;
va_copy(vaArgsCopy, vaArgs);
const int iLen = std::vsnprintf(NULL, 0, zcFormat, vaArgsCopy);
va_end(vaArgsCopy);
// return a formatted string without risking memory mismanagement
// and without assuming any compiler or platform specific behavior
std::vector<char> zc(iLen + 1);
std::vsnprintf(zc.data(), zc.size(), zcFormat, vaArgs);
va_end(vaArgs);
return std::string(zc.data(), iLen); }
#include <ctime>
#include <iostream>
#include <iomanip>
// demonstration of use
int main() {
std::time_t t = std::time(nullptr);
std::cerr
<< std::put_time(std::localtime(& t), "%D %T")
<< " [debug]: "
<< vformat("Int 1 is %d, Int 2 is %d, Int 3 is %d", 11, 22, 33)
<< std::endl;
return 0; }
une solution que j'ai préférée est de le faire avec sprintf directement dans le tampon std::string, après avoir rendu ledit tampon assez grand:
#include <string>
#include <iostream>
using namespace std;
string l_output;
l_output.resize(100);
for (int i = 0; i < 1000; ++i)
{
memset (&l_output[0], 0, 100);
sprintf (&l_output[0], "\r%i"151900920"", i);
cout << l_output;
cout.flush();
}
ainsi, créer la chaîne std::, la redimensionner, accéder directement à son buffer...
Fondation Poco bibliothèque a une fonction de format très pratique, qui soutient std:: chaîne dans la chaîne de format et les valeurs:
vous pouvez formater la sortie C++ dans cout en utilisant le fichier d'en-tête iomanip. Assurez-vous que vous incluez le fichier d'en-tête iomanip avant d'utiliser l'une des fonctions d'aide 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é".
#include<iomanip>
#include<iostream>
#include<vector>
#include<numeric>
...
cout<< "Average waiting times for tasks is " << setprecision(4) << accumulate(all(waitingTimes), 0)/double(waitingTimes.size()) ;
cout << " and " << Q.size() << " tasks remaining" << endl;
Voici une brève description de la façon dont nous pouvons formater les flux C++. http://www.cprogramming.com/tutorial/iomanip.html
Il peut y avoir des problèmes, si le tampon n'est pas assez grande 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 propre assistant à cela (testé sur Windows et Linux GCC ), et vous pouvez essayer de l'utiliser.
de la Chaîne.cpp: http://pastebin.com/DnfvzyKP
Chaîne.h: http://pastebin.com/7U6iCUMa
de la Chaîne.cpp:
#include <cstdio>
#include <cstdarg>
#include <cstring>
#include <string>
using ::std::string;
#pragma warning(disable : 4996)
#ifndef va_copy
#ifdef _MSC_VER
#define va_copy(dst, src) dst=src
#elif !(__cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__))
#define va_copy(dst, src) memcpy((void*)dst, (void*)src, sizeof(*src))
#endif
#endif
///
/// \breif Format message
/// \param dst String to store formatted message
/// \param format Format of message
/// \param ap Variable argument list
///
void toString(string &dst, const char *format, va_list ap) throw() {
int length;
va_list apStrLen;
va_copy(apStrLen, ap);
length = vsnprintf(NULL, 0, format, apStrLen);
va_end(apStrLen);
if (length > 0) {
dst.resize(length);
vsnprintf((char *)dst.data(), dst.size() + 1, format, ap);
} else {
dst = "Format error! format: ";
dst.append(format);
}
}
///
/// \breif Format message
/// \param dst String to store formatted message
/// \param format Format of message
/// \param ... Variable argument list
///
void toString(string &dst, const char *format, ...) throw() {
va_list ap;
va_start(ap, format);
toString(dst, format, ap);
va_end(ap);
}
///
/// \breif Format message
/// \param format Format of message
/// \param ... Variable argument list
///
string toString(const char *format, ...) throw() {
string dst;
va_list ap;
va_start(ap, format);
toString(dst, format, ap);
va_end(ap);
return dst;
}
///
/// \breif Format message
/// \param format Format of message
/// \param ap Variable argument list
///
string toString(const char *format, va_list ap) throw() {
string dst;
toString(dst, format, ap);
return dst;
}
int main() {
int a = 32;
const char * str = "This works!";
string test(toString("\nSome testing: a = %d, %s\n", a, str));
printf(test.c_str());
a = 0x7fffffff;
test = toString("\nMore testing: a = %d, %s\n", a, "This works too..");
printf(test.c_str());
a = 0x80000000;
toString(test, "\nMore testing: a = %d, %s\n", a, "This way is cheaper");
printf(test.c_str());
return 0;
}
de la Chaîne.h:
#pragma once
#include <cstdarg>
#include <string>
using ::std::string;
///
/// \breif Format message
/// \param dst String to store formatted message
/// \param format Format of message
/// \param ap Variable argument list
///
void toString(string &dst, const char *format, va_list ap) throw();
///
/// \breif Format message
/// \param dst String to store formatted message
/// \param format Format of message
/// \param ... Variable argument list
///
void toString(string &dst, const char *format, ...) throw();
///
/// \breif Format message
/// \param format Format of message
/// \param ... Variable argument list
///
string toString(const char *format, ...) throw();
///
/// \breif Format message
/// \param format Format of message
/// \param ap Variable argument list
///
string toString(const char *format, va_list ap) throw();
j'ai essayé, avec expressions régulières . Je l'ai implémenté pour ints et const strings comme exemple, mais vous pouvez ajouter n'importe quel autre type ( POD types mais avec des pointeurs vous pouvez imprimer n'importe quoi).
#include <assert.h>
#include <cstdarg>
#include <string>
#include <sstream>
#include <regex>
static std::string
formatArg(std::string argDescr, va_list args) {
std::stringstream ss;
if (argDescr == "i") {
int val = va_arg(args, int);
ss << val;
return ss.str();
}
if (argDescr == "s") {
const char *val = va_arg(args, const char*);
ss << val;
return ss.str();
}
assert(0); //Not implemented
}
std::string format(std::string fmt, ...) {
std::string result(fmt);
va_list args;
va_start(args, fmt);
std::regex e("\{([^\{\}]+)\}");
std::smatch m;
while (std::regex_search(fmt, m, e)) {
std::string formattedArg = formatArg(m[1].str(), args);
fmt.replace(m.position(), m.length(), formattedArg);
}
va_end(args);
return fmt;
}
voici un exemple d'utilisation:
std::string formatted = format("I am {s} and I have {i} cats", "bob", 3);
std::cout << formatted << std::endl;
sortie:
je suis bob et j'ai 3 chats
cela peut être essayé. simple. vraiment n'utilise pas les nuances de la classe string cependant.
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string>
#include <exception>
using namespace std;
//---------------------------------------------------------------------
class StringFormatter
{
public:
static string format(const char *format, ...);
};
string StringFormatter::format(const char *format, ...)
{
va_list argptr;
va_start(argptr, format);
char *ptr;
size_t size;
FILE *fp_mem = open_memstream(&ptr, &size);
assert(fp_mem);
vfprintf (fp_mem, format, argptr);
fclose (fp_mem);
va_end(argptr);
string ret = ptr;
free(ptr);
return ret;
}
//---------------------------------------------------------------------
int main(void)
{
string temp = StringFormatter::format("my age is %d", 100);
printf("%s\n", temp.c_str());
return 0;
}
_return.desc = (boost::format("fail to detect. cv_result = %d") % st_result).str();