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?

338
demandé sur thecoshman 2010-02-26 17:15:54

30 réponses

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.

230
répondu Erik Aronesty 2018-05-26 14:57:22

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)
171
répondu iFreilicht 2018-01-04 21:51:32

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"
97
répondu kennytm 2017-12-10 12:01:46

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]);

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.

27
répondu vitaut 2018-02-01 20:24:07

si vous voulez seulement une syntaxe de type printf (sans appeler printf vous-même), regardez Boost Format .

18
répondu Timo Geusch 2010-02-26 14:20:23

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);
15
répondu Piti Ongmongkolkul 2012-12-20 14:06:18

[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;
}
14
répondu slashmais 2017-08-31 07:06:25

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);
12
répondu user2622016 2016-02-11 11:23:54

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.

10
répondu PW. 2013-11-08 08:05:22

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 variadiques

ne 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é.

10
répondu Dacav 2018-04-12 14:03:08
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

7
répondu emerge 2014-10-11 02:06:15

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.

6
répondu ChetS 2013-02-01 19:36:30
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);
}
6
répondu pixelpoint 2014-01-13 10:14:45

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.

4
répondu Hassan Syed 2010-02-26 14:27:40

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 );
4
répondu EddieV223 2013-01-24 06:02:53

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;
}
3
répondu Dave 2014-12-11 04:35:23

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.)

3
répondu konsolebox 2017-05-23 12:18:28

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

3
répondu Folkert van Heusden 2016-06-23 13:01:06

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;
}
3
répondu Thomas Perl 2017-08-28 08:51:35

solution très simple.

std::string strBuf;
strBuf.resize(256);
int iCharsPrinted = sprintf_s((char *)strPath.c_str(), strPath.size(), ...);
strBuf.resize(iCharsPrinted);
2
répondu Pasha 2016-03-25 10:38:15

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.

2
répondu Pawel Sledzikowski 2016-10-11 07:52:10

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; }
2
répondu Douglas Daseeco 2018-09-21 18:30:34

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...

1
répondu Xelous 2013-03-11 14:34:45

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:

1
répondu riot_starter 2014-05-29 20:12:36

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

1
répondu vinkris 2014-08-04 14:03:02

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();
1
répondu Valdemar_Rudolfovich 2014-12-11 04:42:17

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

1
répondu ElefEnt 2014-12-11 04:47:12

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;
}
1
répondu ksridhar 2014-12-14 05:28:47
_return.desc = (boost::format("fail to detect. cv_result = %d") % st_result).str();
1
répondu user5685202 2015-12-16 06:32:14