std :: स्प्रिंट की तरह स्ट्रिंग स्वरूपण


454

मुझे इसके std::stringसाथ फॉर्मेट करना है sprintfऔर फाइल स्ट्रीम में भेजना है। मैं यह कैसे कर सकता हूँ?


6
लंबी कहानी लघु प्रयोग boost::format(जैसा कि kennytm का समाधान यहां उपयोग करता है )। boost::formatपहले से ही C ++ स्ट्रीम ऑपरेटर्स को भी सपोर्ट करता है! उदाहरण: cout << format("helloworld. a=%s, b=%s, c=%s") % 123 % 123.123 % "this is a test" << endl;boost::formatकोड की कम से कम पंक्तियाँ हैं ... सहकर्मी की समीक्षा की गई है और सी ++ धाराओं के साथ अच्छी तरह से एकीकृत करता है।
ट्रेवर बॉयड स्मिथ

@ ओकोनल - समुदाय के लिए (मैं अपने प्रतिनिधि के बारे में कम परवाह नहीं कर सकता) मेरा सुझाव है कि आप अपना चयन बदलें। पहले स्निपेट में वर्तमान में चयनित एक, एक मनमाना अधिकतम लंबाई के उपयोग में होने वाली प्रतीक्षा के लिए एक बग प्रस्तुत करता है। दूसरा स्निपेट स्प्रिंटफ जैसे वेरिएग का उपयोग करने की आपकी बताई गई इच्छा को पूरी तरह से नजरअंदाज कर देता है। मेरा सुझाव है कि आप केवल वही उत्तर चुनें जो साफ, सुरक्षित हो, केवल C ++ मानकों पर निर्भर हो, परीक्षण किया गया हो, और अच्छी तरह से टिप्पणी की गई हो। यह मेरा है प्रासंगिक नहीं है। यह वास्तव में सच है। Stackoverflow.com/questions/2342162/… देखें ।
डगलस डसेको

@TrevorBoydSmith a std::formatको C ++ 20 BTW में जोड़ा गया: stackoverflow.com/a/57286312/895245 विस्मयकारी!
सिरो सेंटिल्ली 郝海东 冠状 iro i 法轮功 '

1
@CiroSantilli मैंने C++20कल के बारे में एक लेख पढ़ा और मैंने देखा कि C++20कॉपी boost(मिलियन समय के लिए) std::formatको C++20कल्पना में जोड़कर ! मैं बहुत खुश था! लगभग हर C ++ फ़ाइल जो मैंने पिछले 9 वर्षों में लिखी है, का उपयोग किया है boost::format। C ++ में स्ट्रीम के लिए आधिकारिक प्रिंटफ स्टाइल आउटपुट जोड़ना C ++ के सभी के लिए एक लंबा रास्ता तय करेगा।
ट्रेवर बॉयड स्मिथ

जवाबों:


333

आप इसे सीधे नहीं कर सकते, क्योंकि आपके पास अंतर्निहित बफ़र तक पहुंच नहीं है (C ++ 11 तक, डिट्रिच एप की टिप्पणी देखें )। आपको इसे पहले सी-स्ट्रिंग में करना होगा, फिर इसे std :: string में कॉपी करना होगा:

  char buff[100];
  snprintf(buff, sizeof(buff), "%s", "Hello");
  std::string buffAsStdStr = buff;

लेकिन मुझे यकीन नहीं है कि आप सिर्फ एक स्ट्रिंग स्ट्रीम का उपयोग क्यों नहीं करेंगे? मैं मान रहा हूं कि आपके पास ऐसा न करने के लिए विशिष्ट कारण हैं:

  std::ostringstream stringStream;
  stringStream << "Hello";
  std::string copyOfStr = stringStream.str();

17
मैजिक कुकी char buf[100];इस घोल को बहुत मजबूत नहीं बनाती है। लेकिन आवश्यक विचार है।
जॉन डिब्लिंग

18
जॉन, धाराएँ धीमी नहीं हैं। एकमात्र कारण धाराएँ धीमी लग रही हैं कि डिफ़ॉल्ट रूप से iostreams C FILE आउटपुट के साथ सिंक्रोनाइज़ कर रहे हैं ताकि रुक-रुक कर कॉउट और प्रिंटआउट सही तरीके से आउटपुट हो। इस लिंक को अक्षम करना (cout.sync_with_stdio (झूठा) के लिए एक कॉल के साथ) c ++ की धाराएँ stdio को बेहतर बनाने का कारण बनती हैं, कम से कम MSVC10 के रूप में।
जिम्बो

72
प्रारूपों का उपयोग करने का कारण एक स्थानीयकरणकर्ता को वाक्य के व्याकरण को कठिन कोडिंग के बजाय विदेशी भाषाओं के लिए वाक्य की संरचना का पुनर्निर्माण करना है।
मार्टिग्न कोर्टको जुले

216
किसी कारण से, अन्य भाषाएं प्रिंटफ़-जैसे सिंटैक्स का उपयोग करती हैं: जावा, पायथन (नई सिंटैक्स अभी भी स्ट्रीम की तुलना में प्रिंटफ़ के करीब है)। केवल C ++ इस क्रिया को निर्दोष मनुष्यों पर घृणा उत्पन्न करता है।
quant_dev

9
इससे भी बेहतर, उपयोग करें asprintf, जो परिणाम धारण करने के लिए पर्याप्त जगह के साथ एक नया स्ट्रिंग आवंटित करता है। फिर कॉपी करें कि std::stringयदि आपको पसंद है, और freeमूल को याद रखें । इसके अलावा, इसे एक मैक्रो में रखना संभव है ताकि कोई भी अच्छा संकलक आपके लिए प्रारूप को मान्य करने में मदद करे - आप एक ऐसी doubleजगह नहीं रखना चाहते जहां एक %sउम्मीद है
हारून मैकडैड

287

आधुनिक C ++ इस सुपर को सरल बनाता है।

सी ++ 20

C ++ 20 परिचय std::formatदेता है, जो आपको वास्तव में ऐसा करने की अनुमति देता है। यह अजगर के समान ही प्रतिस्थापन क्षेत्रों का उपयोग करता है :

#include <iostream>
#include <format>

int main() {
    std::cout << std::format("Hello {}!\n", "world");
}

की जाँच करें पूर्ण प्रलेखन ! यह जीवन का एक बड़ा गुण है।


सी ++ 11

साथ सी ++ 11 रों std::snprintf, यह पहले से ही एक बहुत आसान और सुरक्षित कार्य बन गया।

#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
}

ऊपर कोड स्निपेट CC0 1.0 के तहत लाइसेंस प्राप्त है ।

लाइन द्वारा लाइन स्पष्टीकरण:

उद्देश्य:char* उपयोग करके लिखेंstd::snprintfऔर फिर उसे एक में परिवर्तित करेंstd::string

सबसे पहले, हम एक विशेष स्थिति का उपयोग करके चार सरणी की वांछित लंबाई निर्धारित करते हैं snprintf। से cppreference.com :

प्रतिलाभ की मात्रा

[...] यदि परिणामी स्ट्रिंग को buf_size सीमा के कारण छोटा कर दिया जाता है, तो फ़ंक्शन कुल वर्णों को वापस कर देता है (समाप्त करने योग्य नल-बाइट को शामिल नहीं करता है), जो कि सीमा लागू नहीं होने पर लिखा होता।

इसका मतलब यह है कि वांछित आकार वर्णों की संख्या प्लस एक है , जिससे कि नल-टर्मिनेटर अन्य सभी पात्रों के बाद बैठ जाएगा और यह फिर से स्ट्रिंग निर्माता द्वारा काट दिया जा सकता है। इस मुद्दे को टिप्पणियों में @ alexk7 द्वारा समझाया गया था।

size_t size = snprintf( nullptr, 0, format.c_str(), args ... ) + 1;

snprintfयदि कोई त्रुटि हुई है, तो एक ऋणात्मक संख्या लौटाएगा, इसलिए हम फिर से जाँच करेंगे कि क्या स्वरूपण वांछित के रूप में काम करता है। ऐसा न करने से मूक त्रुटियां हो सकती हैं या भारी बफर का आवंटन हो सकता है, जैसा कि टिप्पणियों में @ead द्वारा बताया गया है।

if( size <= 0 ){ throw std::runtime_error( "Error during formatting." ); }

अगला, हम एक नया वर्ण सरणी आवंटित करते हैं और इसे असाइन करते हैं std::unique_ptr। यह आमतौर पर सलाह दी जाती है, क्योंकि आपको deleteइसे फिर से मैन्युअल नहीं करना होगा ।

ध्यान दें कि यह unique_ptrउपयोगकर्ता-परिभाषित प्रकारों के साथ आवंटित करने का एक सुरक्षित तरीका नहीं है क्योंकि आप मेमोरी को नहीं दे सकते हैं यदि निर्माण एक अपवाद फेंकता है!

std::unique_ptr<char[]> buf( new char[ size ] );

उसके बाद, हम निश्चित रूप से बस snprintfइसके इच्छित उपयोग के लिए उपयोग कर सकते हैं और स्वरूपित स्ट्रिंग को लिख सकते हैं char[]

snprintf( buf.get(), size, format.c_str(), args ... );

अंत में, हम std::stringउस से एक नया बनाते हैं और वापस करते हैं, जिससे अंत में अशक्त-टर्मिनेटर को छोड़ना सुनिश्चित होता है।

return std::string( buf.get(), buf.get() + size - 1 );

आप यहां कार्रवाई में एक उदाहरण देख सकते हैं ।


यदि आप भी std::stringतर्क सूची में उपयोग करना चाहते हैं, तो इस जिस्ट पर एक नज़र डालें ।


विजुअल स्टूडियो उपयोगकर्ताओं के लिए अतिरिक्त जानकारी :

के रूप में में विस्तार से बताया इस जवाब , माइक्रोसॉफ्ट का नया नाम दिया std::snprintfकरने के लिए _snprintf(हाँ, बिना std::)। एमएस आगे इसे पदावनत के रूप में सेट करता है और _snprintf_sइसके बजाय उपयोग करने की सलाह देता है , हालांकि _snprintf_sबफर को स्वरूपित आउटपुट से शून्य या छोटा होने के लिए स्वीकार नहीं करेगा और ऐसा होने पर आउटपुट लंबाई की गणना नहीं करेगा। इसलिए संकलन के दौरान पदावनति चेतावनियों से छुटकारा पाने के लिए, आप फ़ाइल के शीर्ष पर निम्न पंक्ति सम्मिलित कर सकते हैं जिसमें निम्न का उपयोग होता है _snprintf:

#pragma warning(disable : 4996)

अंतिम विचार

इस प्रश्न के बहुत सारे उत्तर C ++ 11 के समय से पहले लिखे गए थे और निश्चित बफर लंबाई या वेरिएग का उपयोग करते थे। जब तक आप C ++ के पुराने संस्करणों के साथ फंस नहीं जाते, मैं उन समाधानों का उपयोग करने की अनुशंसा नहीं करता। आदर्श रूप से, C ++ 20 तरीका जाना।

क्योंकि इस उत्तर में C ++ 11 समाधान टेम्प्लेट का उपयोग करता है, यह बहुत अधिक कोड उत्पन्न कर सकता है यदि यह बहुत अधिक उपयोग किया जाता है। हालाँकि, जब तक आप बायनेरिज़ के लिए बहुत सीमित स्थान वाले वातावरण के लिए विकसित नहीं कर रहे हैं, यह एक समस्या नहीं होगी और यह अभी भी स्पष्टता और सुरक्षा दोनों में अन्य समाधानों पर एक बड़ा सुधार है।

यदि अंतरिक्ष दक्षता सुपर महत्वपूर्ण है, तो इन दो समाधानों के साथ vargs और vsnprintf उपयोगी हो सकते हैं। निश्चित बफर लंबाई के साथ किसी भी समाधान का उपयोग न करें , वह सिर्फ परेशानी के लिए पूछ रहा है।


2
कृपया विजुअल स्टूडियो उपयोगकर्ताओं के लिए अपने उत्तर पर जोर दें कि VS का संस्करण कम से कम 2013 होना चाहिए। इस लेख से आप देख सकते हैं कि यह केवल VS2013 संस्करण के साथ काम करता है: यदि बफर एक अशक्त सूचक है और गिनती शून्य है, तो लेन वापस आ गई है। आउटपुट को प्रारूपित करने के लिए आवश्यक वर्णों की गणना, समाप्ति नल सहित नहीं। एक ही तर्क और स्थानीय मापदंडों के साथ एक सफल कॉल करने के लिए, कम से कम लेन + 1 वर्णों वाले एक बफर को आवंटित करें।
चा

3
@ मुओइदीप कई कारण। सबसे पहले, यहाँ लक्ष्य एक std :: string को लौटाना है, c-string नहीं है, तो आप शायद इसका मतलब return string(&buf[0], size);या कुछ इसी तरह का है। दूसरे, यदि आप उस तरह एक सी-स्ट्रिंग वापस करने के लिए थे, तो यह अपरिभाषित व्यवहार का कारण होगा क्योंकि वेक्टर जो आपके द्वारा इंगित किए गए मानों को रिटर्न पर अमान्य कर देगा। तीसरा, जब मैंने C ++ सीखना शुरू किया, तो मानक इस बात को परिभाषित नहीं करता था कि किस क्रम में तत्वों को एक के अंदर संग्रहीत किया जाना था std::vector, इसलिए एक पॉइंटर के माध्यम से इसके भंडारण तक पहुंच को अपरिभाषित व्यवहार किया गया था। अब यह काम करेगा, लेकिन मुझे इसे इस तरह से करने में कोई लाभ नहीं है।
इरेलिच्ट

2
@iFreilicht एक नया रूपांतर std::stringरूप से परिवर्तित वेक्टर ( कॉपी इनिशियलाइज़ेशन ) से बनाया जाएगा, जिसे बाद में कॉपी के रूप में लौटाया जाता है, जैसा कि फ़ंक्शन सिग्नेचर बताता है। इसके अलावा, एक के तत्वों std::vectorरहे हैं, और हमेशा के लिए हो सकता है, संग्रहीत लिए ही थे समीपवर्ती । लेकिन मैं आपकी बात मानता हूं कि ऐसा करने में कोई लाभ नहीं हो सकता है।
मूओइपिप

4
मैं वास्तव में इस समाधान को पसंद करता हूं, हालांकि मुझे लगता है कि लाइन return string(buf.get(), buf.get() + size);होनी चाहिए return string(buf.get(), buf.get() + size - 1);अन्यथा आपको अंत में एक अशक्त चरित्र के साथ एक स्ट्रिंग मिलनी चाहिए । मुझे यह मामला 4.9 पर मिला।
फिल विलियम्स

3
एक एसटीडी पास करना: स्ट्रिंग टू% s एक संकलित त्रुटि का कारण बनता है ( त्रुटि: गैर-तुच्छ प्रकार की वस्तु को पास नहीं कर सकता 'std :: __ cxx11 :: basic_string <char>' varadic function के माध्यम से; कॉल रनटाइम के लिए गर्भपात कर देगा] -Wnon-pod -वारगर्स] ) क्लैंग 3.9.1 में, लेकिन सीएल 19 में यह ठीक संकलित करता है और इसके बजाय रनटाइम पर क्रैश होता है। किसी भी चेतावनी के झंडे को चालू कर सकता हूं जो कि संकलन के समय सीएल में भी है?
ज़िट्रैक्स

241

C ++ 11 समाधान जो vsnprintf()आंतरिक रूप से उपयोग करता है :

#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;
}

एक सुरक्षित और अधिक कुशल (मैंने इसे परीक्षण किया, और यह तेज़ है) दृष्टिकोण:

#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());
}

की fmt_strआवश्यकताओं के अनुरूप मूल्य द्वारा पारित किया जाता है va_start

नोट: "सुरक्षित" और "तेज" संस्करण कुछ सिस्टम पर काम नहीं करता है। इसलिए दोनों अभी भी सूचीबद्ध हैं। इसके अलावा, "तेज" पूरी तरह से उपदेशात्मक कदम के सही होने पर निर्भर करता है, अन्यथा strcpyयह इसे धीमा कर देता है।


3
धीमी गति से। 1 से आकार क्यों बढ़ा? और यह कवक कब लौटता है -1?
0xDEAD BEEF

27
आप str.c_str () ओवरराइट कर रहे हैं? क्या यह खतरनाक नहीं है?
क्वांटम

8
va_start एक संदर्भ तर्क के साथ MSVC में समस्याएँ हैं। यह चुपचाप विफल हो जाता है और यादृच्छिक स्मृति को इंगित करता है। वर्कअराउंड के रूप में, std :: string fmt के बजाय std :: string & fmt का उपयोग करें, या एक आवरण वस्तु लिखें।
स्टीव हनोव

6
I + 1 कारण होगा कि मुझे पता है कि यह संभवत: इस आधार पर काम करेगा कि अधिकांश std :: स्ट्रिंग्स को कैसे लागू किया जाता है, हालांकि c_str का वास्तव में अंतर्निहित स्ट्रिंग को संशोधित करने का स्थान नहीं है। इसका केवल-पढ़ने के लिए होना चाहिए।
डग टी।

6
और पहले से परिणामी स्ट्रिंग की लंबाई प्राप्त करने के लिए, देखें: stackoverflow.com/a/7825892/908336 मैं sizeप्रत्येक पुनरावृत्ति में वृद्धि करने के बिंदु को नहीं देखता , जब आप इसे पहली कॉल द्वारा प्राप्त कर सकते हैं vsnprintf()
मसूद खारी

107

boost::format() आप चाहते हैं कार्यक्षमता प्रदान करता है:

बूस्ट प्रारूप पुस्तकालयों के सारांश के रूप में:

एक प्रारूप ऑब्जेक्ट का निर्माण एक प्रारूप-स्ट्रिंग से किया जाता है, और फिर ऑपरेटर कॉल को दोहराया कॉल के माध्यम से तर्क दिया जाता है। उन तर्कों में से प्रत्येक को स्ट्रिंग्स में बदल दिया जाता है, जो प्रारूप-स्ट्रिंग के अनुसार एक स्ट्रिंग में संयुक्त होते हैं।

#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"

5
आप पुस्तकालयों को बढ़ा सकते हैं जिन्हें आपको बढ़ावा देने की आवश्यकता है। एक लागू उपकरण का उपयोग करना।
हसन सैयद

7
बूस्ट फॉर्मेट न केवल बड़ा है, बल्कि बहुत धीमा भी है। देखें zverovich.net/2013/09/07/... और boost.org/doc/libs/1_52_0/libs/spirit/doc/html/spirit/karma/...
vitaut

14
अपनी परियोजना में कहीं भी बढ़ावा देने से तुरंत संकलन समय बढ़ जाता है। बड़ी परियोजनाओं के लिए, यह सबसे ज्यादा मायने नहीं रखता है। छोटी परियोजनाओं के लिए, बढ़ावा एक खींचें है।
quant_dev

2
@vitaut जबकि यह विकल्पों की तुलना में बहुत अधिक संसाधन है। आप स्ट्रिंग्स को कितनी बार स्वरूपित करते हैं? इसे ध्यान में रखते हुए केवल कुछ माइक्रो सेकंड लगते हैं और अधिकांश परियोजनाएं शायद केवल कुछ दर्जन बार इसका उपयोग करती हैं, यह एक ऐसी परियोजना में ध्यान देने योग्य नहीं है जो स्ट्रिंग प्रारूपण पर बहुत ध्यान केंद्रित नहीं करता है, है ना?
AturSams

2
Unfortunatelly, बढ़ावा :: प्रारूप उसी तरह से काम नहीं करता है: var_args को स्वीकार नहीं करता है। कुछ लोग एक ही कार्यक्रम से संबंधित सभी कोड को समान / समान मुहावरों का उपयोग करना पसंद करते हैं।
14:00 पर xor007

88

C ++ 20 में वह शामिल होगा std::formatजो sprintfएपीआई के संदर्भ में जैसा दिखता है , लेकिन पूरी तरह से सुरक्षित है, उपयोगकर्ता द्वारा परिभाषित प्रकारों के साथ काम करता है, और पायथन जैसे प्रारूप स्ट्रिंग सिंटैक्स का उपयोग करता है। यहां बताया गया है कि आप इसे कैसे std::stringस्ट्रीम कर सकते हैं:

std::string s = "foo";
std::cout << std::format("Look, a string: {}", s);

या

std::string s = "foo";
puts(std::format("Look, a string: {}", s).c_str());

वैकल्पिक रूप से, आप एक स्ट्रिंग को प्रारूपित करने के लिए {fmt} लाइब्रेरी का उपयोग कर सकते हैं और इसे stdoutएक बार में या एक फ़ाइल स्ट्रीम में लिख सकते हैं:

fmt::print(f, "Look, a string: {}", s); // where f is a file stream

जैसा कि यहाँ sprintfया अधिकतर अन्य उत्तर हैं, दुर्भाग्यवश वे varargs का उपयोग करते हैं और स्वाभाविक रूप से असुरक्षित हैं जब तक कि आप GCC की formatविशेषता जैसी किसी चीज़ का उपयोग नहीं करते हैं जो केवल शाब्दिक स्वरूप के तार के साथ काम करता है। आप देख सकते हैं कि ये कार्य निम्न उदाहरण पर असुरक्षित क्यों हैं:

std::string format_str = "%s";
string_format(format_str, format_str[0]);

string_formatएरिक एरोनीस्टी के जवाब से एक कार्यान्वयन कहां है। यह कोड संकलित करता है, लेकिन जब आप इसे चलाने की कोशिश करेंगे तो यह सबसे अधिक दुर्घटनाग्रस्त होगा:

$ g++ -Wall -Wextra -pedantic test.cc 
$ ./a.out 
Segmentation fault: 11

अस्वीकरण : मैं {fmt} और C ++ 20 का लेखक हूं std::format


IMHO तुम्हें याद में शामिल हैं error: 'fmt' has not been declared
सर्जियो

यह सिर्फ एक स्निपेट है, पूरा कोड नहीं। स्पष्ट रूप से आपको <fmt / format.h> शामिल करने और कोड को फ़ंक्शन में रखने की आवश्यकता है।
vitaut

मेरे लिए इतना स्पष्ट नहीं है, IMHO आपको इसे स्निपेट में शामिल करना चाहिए, इस प्रतिक्रिया के लिए धन्यवाद
Sérgio

1
एक fmtकार्यान्वयन की तरह सी ++ 20 को जोड़ा गया है! stackoverflow.com/a/57286312/895245 fmt वर्तमान में इसके लिए समर्थन का दावा करता है। अद्भुत कार्य!
सिरो सेंटिल्ली 郝海东 冠状 iro i 法轮功 '

2
@vitaut इस पर आपके काम के लिए धन्यवाद!
कर्ट निकोल्स

18

यदि आप केवल एक प्रिंटफ-जैसा सिंटैक्स चाहते हैं (खुद को प्रिंट करने के लिए कॉल किए बिना), तो Boost Format पर एक नज़र डालें ।


इस तरह की एक साधारण चीज के लिए पूरी लाइब्रेरी को जोड़ना नेस्सेरी नहीं है। इसका जवाब stackoverflow.com/questions/19009094/… पर दिया गया था ।
डगलस डसेको

15

मैंने vsnprintf का उपयोग करके अपना स्वयं का लिखा है, इसलिए यह अपना बफर बनाने के बजाय स्ट्रिंग देता है।

#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;
}

तो आप इसका उपयोग कर सकते हैं

std::string mystr = format("%s %d %10.5f", "omg", 1, 10.5);

यह डेटा की एक पूरी अतिरिक्त प्रतिलिपि बनाता है, यह vsnprintfसीधे स्ट्रिंग में उपयोग करना संभव है ।
मिंग डक

1
पहले से परिणामी स्ट्रिंग लंबाई प्राप्त करने के लिए stackoverflow.com/a/7825892/908336 में कोड का उपयोग करें । और आप एक अपवाद-सुरक्षित कोड के लिए स्मार्ट पॉइंटर्स का उपयोग कर सकते हैं:std::unique_ptr<char[]> buffer (new char[size]);
मासूद खारी

मुझे यकीन नहीं है कि यह गिरावट के मामले में सही है; मुझे लगता है कि तर्कों को सही ढंग से देखने के लिए आपको दूसरी vsnprintf () के लिए vl_copy की आवश्यकता है। एक उदाहरण के लिए देखें: github.com/haberman/upb/blob/…
जोश हैबरमैन

15

std::string'स्प्रिंटफ' तरीके से प्रारूपित करने के लिए , बफर की लंबाई प्राप्त करने के लिए कॉल snprintf(तर्क nullptrऔर 0) करें। इस तरह C ++ 11 वैरिएड टेम्पलेट का उपयोग करके अपना फ़ंक्शन लिखें:

#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 str;
}

GCC में उदाहरण के लिए C ++ 11 समर्थन के साथ संकलित करें: g++ -std=c++11

उपयोग:

  std::cout << string_sprintf("%g, %g\n", 1.23, 0.001);

std :: snprintf VC ++ 12 (विजुअल स्टूडियो 2013) में उपलब्ध नहीं है। इसके बजाय इसे _snprintf से बदलें।
शीतल शाह

तुम char buf[length + 1];इसके बजाय का उपयोग क्यों नहीं करते char* buf = new char[length + 1];?
बेहरोज़।

नए के साथ प्रयोग करने में char[]और अंतर यह char*है कि पूर्व मामले में buf को स्टैक पर आवंटित किया जाएगा। यह छोटे बफ़र्स के लिए ठीक है, लेकिन चूंकि हम परिणामी स्ट्रिंग के आकार की गारंटी नहीं दे सकते, इसलिए इसका उपयोग करना थोड़ा बेहतर है new। मेरी मशीन पर उदाहरण के लिए string_sprintf("value: %020000000d",5), संख्या 5 से पहले प्रमुख शून्य का अपमानजनक संख्या प्रिंट करें, स्टैक पर सरणी का उपयोग करते समय कोर डंप, लेकिन गतिशील रूप से आवंटित सरणी का उपयोग करते समय ठीक काम करता हैnew char[length + 1]
user2622016

स्वरूपित आउटपुट के लिए आवश्यक वास्तविक बफ़ आकार प्राप्त करने के लिए बहुत चतुर विचार
क्रिस

1
@ user2622016: समाधान के लिए धन्यवाद! कृपया ध्यान दें कि std::move अतिश्योक्तिपूर्ण है
मिहाई टोडर

14

[संपादित करें: 20/05/25] अभी भी बेहतर है ...:
हेडर में:

// `say` prints the values
// `says` returns a string instead of printing
// `sayss` appends the values to it's first argument instead of printing
// `sayerr` prints the values and returns `false` (useful for return statement fail-report)<br/>

void PRINTSTRING(const std::string &s); //cater for GUI, terminal, whatever..
template<typename...P> void say(P...p) { std::string r{}; std::stringstream ss(""); (ss<<...<<p); r=ss.str(); PRINTSTRING(r); }
template<typename...P> std::string says(P...p) { std::string r{}; std::stringstream ss(""); (ss<<...<<p); r=ss.str(); return r; }
template<typename...P> void sayss(std::string &s, P...p) { std::string r{}; std::stringstream ss(""); (ss<<...<<p); r=ss.str();  s+=r; } //APPENDS! to s!
template<typename...P> bool sayerr(P...p) { std::string r{}; std::stringstream ss("ERROR: "); (ss<<...<<p); r=ss.str(); PRINTSTRING(r); return false; }

PRINTSTRING(r)समारोह जीयूआई या टर्मिनल या किसी विशेष उत्पादन जरूरतों को प्रयोग करने के लिए पूरा करने के लिए है #ifdef _some_flag_, डिफ़ॉल्ट है:

void PRINTSTRING(const std::string &s) { std::cout << s << std::flush; }

[संपादित करें '17 / 8/31] एक वैरेडिक टेम्पलेटेड संस्करण '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;
}

प्रभावी रूप से कभी-कभी बाधा डालने वाले <<-ऑपरेटर का कॉमा-सीमांकित संस्करण (इसके बजाय) इस तरह से उपयोग किया जाता है:

char chSpace=' ';
double pi=3.1415;
std::string sWorld="World", str_var;
str_var = vtspf("Hello", ',', chSpace, sWorld, ", pi=", pi);


एरिक एरोनीस्टी के उत्तर (ऊपर) में तकनीक का उपयोग करने के लिए अनुकूलित किया गया:

#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;
}

[पिछला उत्तर]
एक बहुत देर से जवाब, लेकिन उन लोगों के लिए, जो मेरी तरह, 'स्प्रिंटफ-वे' की तरह करते हैं: मैंने लिखा है और निम्नलिखित कार्यों का उपयोग कर रहे हैं। यदि आप इसे पसंद करते हैं, तो आप स्प्रिंटफ को अधिक बारीकी से फिट करने के लिए%-गोद लेने का विस्तार कर सकते हैं; वहां के लोग वर्तमान में मेरी जरूरतों के लिए पर्याप्त हैं। जैसे ही आप स्प्रिंट करेंगे आप स्ट्रिंग () और स्ट्रिंगफैपेंड () का उपयोग करेंगे। बस याद रखें कि पैरामीटर ... POD प्रकार के होने चाहिए।

//=============================================================================
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;
}

@MooDDuck: एरनैस्टी के उत्तर में दान की टिप्पणी के अनुसार बदल गया कार्य पैरामीटर। मैं केवल लिनक्स / जीसीसी का उपयोग करता हूं, और fmtसंदर्भ के रूप में यह ठीक काम करता है। (लेकिन मुझे लगता है कि लोग खिलौनों के साथ खेलना चाहेंगे, इसलिए ...) अगर कोई अन्य 'बग' हो तो क्या आप विस्तृत कर सकते हैं?
स्लैशमाईस

मुझे गलत लगा कि उनके कोड का हिस्सा कैसे काम करता है और यह सोचता है कि यह कई आकार बदल रहा था। Reexamining से पता चलता है कि मुझसे गलती हुई थी। आपका कोड सही है।
मिंग डक

एरिक एरोनिटी के उत्तर का निर्माण एक लाल हेरिंग है। उनका पहला कोड नमूना असुरक्षित है और उनका दूसरा अकुशल और अनाड़ी है। स्वच्छ कार्यान्वयन को इस तथ्य से स्पष्ट रूप से संकेत मिलता है कि, यदि फ़ंक्शन के किसी भी vprintf परिवार का buf_siz शून्य है, तो कुछ भी नहीं लिखा गया है और बफर अशक्त सूचक हो सकता है, हालांकि रिटर्न वैल्यू (बाइट्स की संख्या जो नहीं लिखी जाएगी) null टर्मिनेटर) की गणना अभी भी की जाती है और लौटाई जाती है। उत्पादन की गुणवत्ता का उत्तर यहां है: stackoverflow.com/questions/2342162/…
डगलस डसेको

10

यह Google ऐसा कैसे करता है: StringPrintf(बीएसडी लाइसेंस)
और फेसबुक इसे काफी समान तरीके से करता है: StringPrintf(अपाचे लाइसेंस)
दोनों एक सुविधाजनक StringAppendFभी प्रदान करते हैं।


10

इस बहुत ही लोकप्रिय प्रश्न पर मेरे दो सेंट।

समान कार्यों के मैनपेजprintf को उद्धृत करने के लिए :

सफल वापसी पर, ये फ़ंक्शन मुद्रित किए गए वर्णों की संख्या लौटाते हैं (स्ट्रिंग को आउटपुट समाप्त करने के लिए उपयोग किए गए नल बाइट को छोड़कर)।

फंक्शन स्नप्रिंट () और वर्नप्रिंट () साइज बाइट्स (टर्मिनेटिंग नल बाइट ('0 0) सहित) से अधिक नहीं लिखते हैं। यदि इस सीमा के कारण आउटपुट को छोटा कर दिया गया था, तो वापसी मूल्य वर्णों की संख्या (टर्मिनेटिंग नल बाइट को छोड़कर) है, जो पर्याप्त स्थान उपलब्ध होने पर अंतिम स्ट्रिंग को लिखा गया होता। इस प्रकार, आकार का एक वापसी मूल्य या अधिक का मतलब है कि आउटपुट को छोटा किया गया था।

दूसरे शब्दों में, एक समझदार C ++ 11 कार्यान्वयन निम्नलिखित होना चाहिए:

#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);
}

यह काफी अच्छी तरह से काम करता है :)

वैरिएडिक टेम्प्लेट केवल C ++ 11 में समर्थित हैं। पिक्सेल पॉइंट का उत्तर पुरानी प्रोग्रामिंग शैलियों का उपयोग करके एक समान तकनीक दिखाता है।

यह अजीब है कि C ++ में बॉक्स से बाहर ऐसा कुछ नहीं है। उन्होंने हाल ही में जोड़ा to_string(), जो मेरी राय में एक महान कदम है। मुझे आश्चर्य है कि क्या वे अंततः एक .formatऑपरेटर को जोड़ देंगे std::string...

संपादित करें

जैसा कि alexk7 ने बताया, ए +1की वापसी मूल्य पर जरूरत है std::snprintf, क्योंकि हमें \0बाइट के लिए जगह की आवश्यकता है । सहजता से, अधिकांश आर्किटेक्चरों के लापता होने के +1कारण requiredपूर्णांक को आंशिक रूप से अधिलेखित कर दिया जाएगा 0। यह वास्तविक पैरामीटर के मूल्यांकन के बाद होगा , इसलिए प्रभाव दिखाई नहीं देना चाहिए।requiredstd::snprintf

हालांकि यह समस्या संकलक अनुकूलन के साथ बदल सकती है: यदि कंपाइलर requiredचर के लिए रजिस्टर का उपयोग करने का निर्णय लेता है तो क्या होगा ? यह उस तरह की त्रुटियां हैं जो कभी-कभी सुरक्षा के मुद्दों में परिणत होती हैं।


1
स्निप्रफ हमेशा एक समाप्ति नल-बाइट जोड़ता है, लेकिन इसके बिना वर्णों की संख्या देता है। क्या यह कोड हमेशा अंतिम वर्ण को छोड़ता नहीं है?
अलेक्सा 7

@ alexk7, अच्छा कैच! मैं जवाब अपडेट कर रहा हूं। कोड अंतिम वर्ण को छोड़ता नहीं है, लेकिन bytesबफर के अंत से परे लिखता है , शायद requiredपूर्णांक पर (जो कि उस बिंदु पर सौभाग्य से पहले से ही मूल्यांकन किया गया है)।
डेकाव ५'१५ को

1
बस एक छोटा संकेत: 0 के बफर आकार के साथ, आप nullptrबफर तर्क के रूप में पास कर सकते हैं , char b;अपने कोड में लाइन को समाप्त कर सकते हैं। ( स्रोत )
iFreilicht

@iFreilicht, को ठीक करता है। इसके अलावा +1
Dacav

2
"चार बाइट्स [आवश्यक]" का उपयोग ढेर के बजाय ढेर पर आवंटित किया जाएगा, यह बड़े प्रारूप के तारों पर खतरनाक हो सकता है। इसके बजाय एक नए प्रयोग का उपयोग करने पर विचार करें। यान
यान्नुत

9
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;
}

C99 स्निपरफ और C ++ 11 का उपयोग करना


9

परीक्षण, उत्पादन गुणवत्ता उत्तर

यह उत्तर मानकों के अनुरूप तकनीकों के साथ सामान्य मामले को संभालता है। उसी दृष्टिकोण को उनके पृष्ठ के नीचे CppReference.com पर एक उदाहरण के रूप में दिया गया है । उनके उदाहरण के विपरीत, यह कोड प्रश्न की आवश्यकताओं को पूरा करता है और रोबोटिक्स और उपग्रह अनुप्रयोगों में परीक्षण किया जाता है। इसमें टिप्पणी करने में भी सुधार हुआ है। डिजाइन की गुणवत्ता पर नीचे चर्चा की गई है।

#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; }

प्रिडिक्टेबल लीनियर एफिशिएंसी

प्रश्न विनिर्देशों के अनुसार एक सुरक्षित, विश्वसनीय और पूर्वानुमान योग्य पुन: प्रयोज्य कार्य के लिए दो पास आवश्यक हैं। एक पुन: प्रयोज्य फ़ंक्शन में विभिन्न आकारों के आकार के वितरण के बारे में अनुमान खराब प्रोग्रामिंग शैली है और इसे टाला जाना चाहिए। इस मामले में, मनमाने ढंग से बड़ी चर लंबाई का प्रतिनिधित्व एल्गोरिदम की पसंद में एक महत्वपूर्ण कारक है।

अतिप्रवाह पर पुनः प्रयास करना अक्षम्य है, जो कि C ++ 11 मानकों की समिति द्वारा लिखित बफ़र के रिक्त होने पर सूखा चलाने के लिए उपर्युक्त प्रस्ताव पर चर्चा किए जाने पर चर्चा की गई एक अन्य कारण है।

उपरोक्त उत्पादन तैयार कार्यान्वयन में, आवंटन आकार निर्धारित करने के लिए पहला रन एक ऐसा सूखा रन है। कोई आवंटन नहीं होता है। प्रिंटफ के निर्देशन और पार्स के पठन को दशकों से बेहद कुशल बनाया गया है। पुन: प्रयोज्य कोड पूर्वानुमान योग्य होना चाहिए, भले ही तुच्छ मामलों के लिए एक छोटी सी अक्षमता का त्याग किया जाना चाहिए।

सुरक्षा और विश्वसनीयता

कैम्ब्रिज इवेंट में अपने व्याख्यान के बाद एंड्रयू कोएनिग ने हमारे एक छोटे से समूह से कहा, "उपयोगकर्ता के कार्यों को अनपेक्षित कार्यक्षमता के लिए विफलता के शोषण पर भरोसा नहीं करना चाहिए।" हमेशा की तरह, उनकी बुद्धि को रिकॉर्ड में सही दिखाया गया है। फिक्स्ड और क्लोज्ड सिक्योरिटी बग इश्यू अक्सर फिक्स होने से पहले शोषित छेद के वर्णन में रीट हैक को दर्शाते हैं।

यह स्प्रिंटफ, C9X संशोधन प्रस्ताव , आईएसओ IEC दस्तावेज़ WG14 N645 / X3J11 96-008 के वैकल्पिक में अशक्त बफर सुविधा के लिए औपचारिक मानकों संशोधन प्रस्ताव में उल्लेख किया गया है । डायनामिक मेमोरी उपलब्धता की बाधाओं के भीतर, प्रिंट निर्देशन के अनुसार, मनमाने ढंग से लंबा स्ट्रिंग, "% s", एक अपवाद नहीं है, और इसका उत्पादन करने के लिए शोषण नहीं किया जाना चाहिए, "अनएक्सपेक्टेशनल कार्यक्षमता।"

इस उत्तर के पहले पैराग्राफ में लिंक C ++ Reference.org पेज के नीचे दिए गए उदाहरण कोड के साथ प्रस्ताव पर विचार करें।

इसके अलावा, विफलता के मामलों का परीक्षण शायद ही कभी सफलता के मामलों में मजबूत होता है।

पोर्टेबिलिटी

सभी प्रमुख OS विक्रेता कंपाइलर प्रदान करते हैं जो पूरी तरह से std :: vsnprintf को c ++ 11 मानकों के भाग के रूप में समर्थन करते हैं। विक्रेताओं के उत्पाद चलाने वाले होस्ट जो अब वितरण को बनाए नहीं रखते, उन्हें कई कारणों से g ++ या clang ++ से सुसज्जित किया जाना चाहिए।

ढेर का उपयोग

1 कॉल करने के लिए std में स्टैक का उपयोग :: vsnprintf 2 की तुलना में कम या इसके बराबर होगा, और यह 2 कॉल शुरू होने से पहले मुक्त हो जाएगा। यदि पहला कॉल स्टैक उपलब्धता से अधिक है, तो std :: fprintf भी विफल हो जाएगा।


संक्षिप्त और मजबूत। यह HP-UX, IRIX, Tru64 पर विफल हो सकता है जिनके पास गैर-अनुरूपता vsnprintf-s है। संपादित करें: यह भी, यह देखते हुए कि कैसे दो-पास प्रदर्शनों को प्रभावित कर सकते हैं, esp। सबसे आम, छोटे तार के लिए स्वरूपण, क्या आपने प्रारंभिक पास के लिए एक अनुमान लगाया है, जो पर्याप्त रूप से बड़ा हो सकता है?
इंजीनियर

एफडब्ल्यूआईडब्ल्यू, अनुमान लगाने वाला मैं एक स्टैक-आवंटित बफर का उपयोग करने का उल्लेख कर रहा था जहां पहला रन होता है। यदि यह फिट बैठता है तो यह एक दूसरे रन की लागत और वहां होने वाले गतिशील आवंटन को बचाता है। संभवतः, छोटे तार अधिक बार बड़े तार की तुलना में उपयोग किए जाते हैं। मेरे कच्चे बेंचमार्क में वह रणनीति (लगभग) छोटी स्ट्रिंग्स के लिए चल रहे समय को रोक देती है और ऊपर की रणनीति के कुछ प्रतिशत (निश्चित ओवरहेड हो सकता है?) के भीतर है। क्या आप कृपया C ++ 11 डिज़ाइन के बारे में विस्तार से बताएंगे जो एक सूखा रन, आदि को नियोजित करता है? मैं इसके बारे में पढ़ना चाहूंगा।
इंजीनियर

@Engineerist, आपके सवालों के जवाब शरीर में, कोड के ऊपर और नीचे दिए गए हैं। उप विषयों को इस तरह से पढ़ना आसान बनाया जा सकता है।
डगलस डसेको

6

सी ++ 20 std::format

यह पहुंच चुका है! इस सुविधा का वर्णन यहां किया गया है: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0645r9.html और पायथन जैसे .format()सिंटैक्स का उपयोग करता है ।

मुझे उम्मीद है कि उपयोग इस प्रकार होगा:

#include <format>
#include <string>

int main() {
    std::string message = std::format("The answer is {}.", 42);
}

जब समर्थन जीसीसी के लिए आता है, तो मैं इसे एक कोशिश देता हूं, जीसीसी 9.1.0 के साथ g++-9 -std=c++2aअभी भी इसका समर्थन नहीं करता है।

एपीआई एक नया std::formatहेडर जोड़ देगा :

प्रस्तावित स्वरूपण एपीआई नए हेडर में परिभाषित किया गया है <format>और मौजूदा कोड पर कोई प्रभाव नहीं होना चाहिए।

मौजूदा fmtलाइब्रेरी का दावा है कि अगर आपको पॉलीफिल की जरूरत है तो इसे लागू करें : https://github.com/fmtlib/fmt

C ++ 20 का कार्यान्वयन std::format

और पहले उल्लेख किया गया था: std :: स्प्रिंट की तरह स्ट्रिंग प्रारूपण


5

एरिक Aronesty द्वारा प्रदान किए गए उत्तर के आधार पर:

std::string string_format(const std::string &fmt, ...) {
    std::vector<char> str(100,'\0');
    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();
}

यह constउस परिणाम से दूर जाने की आवश्यकता से बचता है .c_str()जो मूल उत्तर में था।


1
एरिक एरोनिटी के उत्तर का निर्माण एक लाल हेरिंग है। उसका पहला कोड नमूना असुरक्षित है और दूसरा, लूप अक्षम और अनाड़ी है। स्वच्छ कार्यान्वयन को इस तथ्य से स्पष्ट रूप से संकेत मिलता है कि, यदि फ़ंक्शन के किसी भी vprintf परिवार का buf_siz शून्य है, तो कुछ भी नहीं लिखा गया है और बफर अशक्त सूचक हो सकता है, हालांकि रिटर्न वैल्यू (बाइट्स की संख्या जो नहीं लिखी जाएगी) null टर्मिनेटर) की गणना अभी भी की जाती है और लौटाई जाती है। उत्पादन की गुणवत्ता का उत्तर यहां है: stackoverflow.com/questions/2342162/…
डगलस डसेको

एरिक एरोनेस्टी का उत्तर तब से संपादित किया गया है जब मेरा जोड़ा गया था। मैं वेक्टर <char> तार का उपयोग करने के विकल्प को उजागर करना चाहता था क्योंकि वे निर्मित होते हैं। C ++ कोड से C फ़ंक्शन को कॉल करते समय मैं अक्सर इस तकनीक का उपयोग करता हूं। यह दिलचस्प है कि सवाल अब 34 जवाब है।
चेतस

Vfprintf पेज पर cppreference.com का उदाहरण बाद में जोड़ा गया था। मेरा मानना ​​है कि सबसे अच्छा जवाब वर्तमान में स्वीकार किया गया उत्तर है, एक प्रिंटफ वेरिएंट के बजाय स्ट्रिंग स्ट्रीम का उपयोग करना चीजों का C ++ तरीका है। हालाँकि मेरे उत्तर ने मूल्य प्रदान किया जब यह प्रदान किया गया था; यह उस समय अन्य उत्तरों की तुलना में बेहतर था। अब मानक में string_view, पैरामीटर पैक और Variadic टेम्पलेट है एक नया उत्तर उन सुविधाओं को शामिल कर सकता है। मेरे जवाब के लिए, हालाँकि यह अब अतिरिक्त वोटों के लायक नहीं हो सकता है, यह डिलीट या डाउन-वोट करने लायक नहीं है, इसलिए मैं इसे छोड़ रहा हूं।
चेतस

5
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);
}

1
स्मार्ट विचार के लिए +1, लेकिन यह बहुत स्पष्ट नहीं _vscprintfहै कि क्या है। मुझे लगता है कि आपको इस जवाब पर विस्तार से बताना चाहिए।
दकाव

3

स्ट्रिंग में वह नहीं है जो आपको चाहिए, लेकिन std :: stringstream करता है। स्ट्रिंग बनाने के लिए एक स्ट्रिंग का उपयोग करें और फिर स्ट्रिंग निकालें। यहां उन चीजों पर एक व्यापक सूची दी गई है जो आप कर सकते हैं। उदाहरण के लिए:

cout.setprecision(10); //stringstream is a stream like cout

एक डबल या फ्लोट को प्रिंट करते समय आपको 10 दशमलव सटीकता का स्थान देगा।


8
जो अभी भी आपको नियंत्रण के पास कुछ भी नहीं देता है प्रिंटफ आपको देता है ... लेकिन अच्छा है।
एरिक एरोनिटी

3

आप यह कोशिश कर सकते हैं:

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

3

यदि आप ऐसी प्रणाली पर हैं जिसमें asprintf (3) है , तो आप इसे आसानी से लपेट सकते हैं:

#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;
}

2
मैं इस लाइन को पहले घोषणा के formatरूप में जोड़ूंगा, क्योंकि यह gcc को तर्कों के प्रकारों की जांच करने और एक अच्छी चेतावनी देने के लिए कहता है -Wall:std::string format(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
हारून मैकडैड

2
मैंने अभी एक कॉल जोड़ा है va_end"अगर va_end को एक फ़ंक्शन से पहले नहीं बुलाया जाता है जो va_start या va_copy रिटर्न कहता है, तो व्यवहार अपरिभाषित है।" - डॉक्स
हारून मैकडैड

1
आपको vasprintf के रिटर्न रिजल्ट की जांच करनी चाहिए क्योंकि पॉइंटर वैल्यू असफल होने पर अपरिभाषित है। तो, संभवतः <new> शामिल करें और जोड़ें: if (size == -1) {फेंक std :: bad_alloc (); }
नील मैकगिल

अच्छी बात है, मैंने उत्तर को तदनुसार संशोधित किया है, मैंने करने के बजाय सिर्फ एक टिप्पणी करने का निर्णय लिया है throw std::bad_alloc();, क्योंकि मैं अपने कोडबेस में C ++ अपवादों का उपयोग नहीं कर रहा हूं, और जो लोग करते हैं, वे आसानी से इसे जोड़ सकते हैं स्रोत टिप्पणी और अपनी टिप्पणी पर यहाँ।
थॉमस पर्ल

2

यह वह कोड है जो मैं अपने कार्यक्रम में ऐसा करने के लिए उपयोग करता हूं ... यह कुछ भी फैंसी नहीं है, लेकिन यह चाल करता है ... ध्यान दें, आपको अपने आकार को लागू के रूप में समायोजित करना होगा। मेरे लिए MAX_BUFFER 1024 है।

std::string Format ( const char *fmt, ... )
{
    char textString[MAX_BUFFER*5] = {'\0'};

    // -- Empty the buffer properly to ensure no leaks.
    memset(textString, '\0', 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;
}

4
TextString का प्रारंभ पहले से ही पूरे बफ़र को शून्य पर सेट करता है। याद करने की ज़रूरत नहीं ...
एरिकसोफ़र

यह डेटा की एक पूरी अतिरिक्त प्रतिलिपि बनाता है, यह vsnprintfसीधे स्ट्रिंग में उपयोग करना संभव है ।
मिंग डक

2

Dacav और पिक्सेल पॉइंट के जवाब से विचार लिया । मैं लगभग थोड़ा खेला और यह मिला:

#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('\0');
    va_end(vl);

    char buffer[size];

    va_start(vl, fmt);
    size = vsnprintf(buffer, size, fmt, vl);
    va_end(vl);

    return std::string(buffer, size);
}

साथ समझदार प्रोग्रामिंग अभ्यास मेरा मानना है कि कोड के लिए पर्याप्त होना चाहिए, हालांकि मैं अभी भी अधिक सुरक्षित विकल्प है कि अभी भी काफी सरल कर रहे हैं और सी ++ 11 की आवश्यकता नहीं होगी के लिए खुला रहा हूँ।


और यहाँ एक और संस्करण है जो प्रारंभिक बफर का उपयोग दूसरी कॉल को रोकने के लिए करता है vsnprintf()जब प्रारंभिक बफर पहले से ही पर्याप्त है।

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('\0');

    char buffer[size];

    va_start(vl, fmt);
    size = vsnprintf(buffer, size, fmt, vl);
    va_end(vl);

    return std::string(buffer, size);
}

(यह पता चला है कि यह संस्करण Piti Ongmongkolkul के उत्तर के समान है , केवल इसका उपयोग नहीं करता है newऔर delete[]बनाते समय इसका आकार भी निर्दिष्ट करता हैstd::string

यहाँ उपयोग नहीं करने का विचार है newऔर delete[]ढेर पर स्टैक का उपयोग करने के लिए है क्योंकि इसे आवंटन और निपटान कार्यों को कॉल करने की आवश्यकता नहीं है, हालांकि अगर ठीक से उपयोग नहीं किया जाता है, तो यह कुछ (शायद पुराने, या में ओवरफ्लो बफर करना खतरनाक हो सकता है) शायद सिर्फ कमजोर) सिस्टम। यदि यह एक चिंता का विषय है, तो मैं अत्यधिक उपयोग करने newऔर delete[]इसके बजाय सुझाव देता हूं । ध्यान दें कि यहां केवल आवंटन के बारे में चिंता है जैसा vsnprintf()कि पहले से ही सीमा के साथ कहा जाता है, इसलिए दूसरे बफर पर आवंटित आकार के आधार पर एक सीमा को निर्दिष्ट करने से उन लोगों को भी रोका जा सकेगा।)


2

मैं आमतौर पर इसका उपयोग करता हूं:

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;
}

नुकसान: सभी सिस्टम वैसप्रिंट का समर्थन नहीं करते हैं


vasprintf अच्छा है - हालाँकि आपको रिटर्न कोड की जाँच करने की आवश्यकता है। -1 बफर पर एक अपरिभाषित मूल्य होगा। आवश्यकता: if (size == -1) {फेंक std :: bad_alloc (); }
नील मैकगिल

2

@IFreilicht उत्तर के थोड़े संशोधित संस्करण के नीचे, C ++ 14 में अपडेट किया गया ( make_uniqueकच्चे घोषणा के बजाय फ़ंक्शन का उपयोग ) और std::stringतर्कों के लिए समर्थन जोड़ा गया (केनी केर लेख पर आधारित )

#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";
}

आउटपुट:

i = 3, f = 5.000000, s = hello world

वांछित होने पर मूल के साथ इस उत्तर को मर्ज करने के लिए स्वतंत्र महसूस करें।


1

पोको फाउंडेशन लाइब्रेरी में एक बहुत ही सुविधाजनक प्रारूप फ़ंक्शन है, जो प्रारूप स्ट्रिंग और मान दोनों में std :: string का समर्थन करता है:


1

आप iomanip हेडर फ़ाइल का उपयोग करके Cout में C ++ आउटपुट फॉर्मेट कर सकते हैं। सुनिश्चित करें कि आप किसी भी सहायक कार्यों जैसे कि सेटरपिटेशन, सेटफिल आदि का उपयोग करने से पहले iomanip हेडर फ़ाइल को शामिल करते हैं।

यहाँ एक कोड स्निपेट है जिसका उपयोग मैंने अतीत में वेक्टर में औसत प्रतीक्षा समय को प्रिंट करने के लिए किया है, जिसे मैंने "संचित" किया है।

#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;

यहाँ एक संक्षिप्त विवरण है कि हम C ++ स्ट्रीम को कैसे प्रारूपित कर सकते हैं। http://www.cprogramming.com/tutorial/iomanip.html


1

समस्याएं हो सकती हैं, अगर बफर स्ट्रिंग को प्रिंट करने के लिए पर्याप्त बड़ा नहीं है। वहाँ एक स्वरूपित संदेश को मुद्रित करने से पहले आपको स्वरूपित स्ट्रिंग की लंबाई निर्धारित करनी चाहिए। मैं इसे अपना सहायक बनाता हूं (विंडोज और लिनक्स जीसीसी पर परीक्षण किया गया) ) के , और आप इसका उपयोग करने की कोशिश कर सकते हैं।

String.cpp: http://pastebin.com/DnfvzyKP
String.h: http://pastebin.com/7U6iCUMa

String.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;
}

string.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();

लाइन के संबंध में vsnprintf((char *)dst.data(), dst.size() + 1, format, ap);- क्या यह मान लेना सुरक्षित है कि स्ट्रिंग के बफर में एक समाप्ति अशक्त चरित्र के लिए जगह है? क्या ऐसे कार्यान्वयन हैं जो आकार + 1 वर्णों को आवंटित नहीं करते हैं। क्या यह करना सुरक्षित होगाdst.resize(length+1); vsnprintf((char *)dst.data(), dst.size(), format, ap); dst.resize(length);
drwatsoncode

जाहिरा तौर पर मेरी पिछली टिप्पणी का जवाब है: नहीं, यह मानना ​​सुरक्षित नहीं है कि एक अशक्त चरित्र है। सी ++ 98 के संबंध में विशेष रूप से: "डेटा पर मान का उपयोग () + आकार () अपरिभाषित व्यवहार पैदा करता है : इस फ़ंक्शन द्वारा लौटाए गए मान द्वारा इंगित किए गए वर्ण क्रम को समाप्त करता है कोई गारंटी नहीं है । :: c_str एक फ़ंक्शन के लिए जो इस तरह की गारंटी प्रदान करता है। एक प्रोग्राम इस क्रम में किसी भी वर्ण को नहीं बदलेगा। "हालांकि, C ++ 11 कल्पना इंगित करती है कि dataऔर c_strसमानार्थी हैं।
drwatsoncode


1

बहुत-बहुत सरल उपाय।

std::string strBuf;
strBuf.resize(256);
int iCharsPrinted = sprintf_s((char *)strPath.c_str(), strPath.size(), ...);
strBuf.resize(iCharsPrinted);

1

मुझे लगता है कि यह कई बार उत्तर दिया गया है, लेकिन यह अधिक संक्षिप्त है:

std::string format(const std::string fmt_str, ...)
{
    va_list ap;
    char *fp = NULL;
    va_start(ap, fmt_str);
    vasprintf(&fp, fmt_str.c_str(), ap);
    va_end(ap);
    std::unique_ptr<char[]> formatted(fp);
    return std::string(formatted.get());
}

उदाहरण:

#include <iostream>
#include <random>

int main()
{
    std::random_device r;
    std::cout << format("Hello %d!\n", r());
}

Http://rextester.com/NJB14150 भी देखें


1

अद्यतन 1 : जोड़ा गया fmt::formatपरीक्षण

मैंने अपनी स्वयं की जाँच यहाँ ली गई विधियों के इर्द-गिर्द की है और यहाँ वर्णित बनाम परिणाम के विपरीत परिणाम प्राप्त कर रहा हूँ।

मैंने 4 विधियों में 4 कार्यों का उपयोग किया है:

  • varadic function + vsnprintf+std::unique_ptr
  • varadic function + vsnprintf+std::string
  • वैरेडिक टेम्प्लेट फ़ंक्शन + std::ostringstream+ std::tuple+utility::for_each
  • fmt::formatfmtपुस्तकालय से कार्य करते हैं

परीक्षण के लिए बैकएंड googletestका उपयोग किया है।

#include <string>
#include <cstdarg>
#include <cstdlib>
#include <memory>
#include <algorithm>

#include <fmt/format.h>

inline std::string string_format(size_t string_reserve, const std::string fmt_str, ...)
{
    size_t str_len = (std::max)(fmt_str.size(), string_reserve);

    // plain buffer is a bit faster here than std::string::reserve
    std::unique_ptr<char[]> formatted;

    va_list ap;
    va_start(ap, fmt_str);

    while (true) {
        formatted.reset(new char[str_len]);

        const int final_n = vsnprintf(&formatted[0], str_len, fmt_str.c_str(), ap);

        if (final_n < 0 || final_n >= int(str_len))
            str_len += (std::abs)(final_n - int(str_len) + 1);
        else
            break;
    }

    va_end(ap);

    return std::string(formatted.get());
}

inline std::string string_format2(size_t string_reserve, const std::string fmt_str, ...)
{
    size_t str_len = (std::max)(fmt_str.size(), string_reserve);
    std::string str;

    va_list ap;
    va_start(ap, fmt_str);

    while (true) {
        str.resize(str_len);

        const int final_n = vsnprintf(const_cast<char *>(str.data()), str_len, fmt_str.c_str(), ap);

        if (final_n < 0 || final_n >= int(str_len))
            str_len += (std::abs)(final_n - int(str_len) + 1);
        else {
            str.resize(final_n); // do not forget to shrink the size!
            break;
        }
    }

    va_end(ap);

    return str;
}

template <typename... Args>
inline std::string string_format3(size_t string_reserve, Args... args)
{
    std::ostringstream ss;
    if (string_reserve) {
        ss.rdbuf()->str().reserve(string_reserve);
    }
    std::tuple<Args...> t{ args... };
    utility::for_each(t, [&ss](auto & v)
    {
        ss << v;
    });
    return ss.str();
}

for_each: कार्यान्वयन यहाँ से लिया जाता है टपल पर पुनरावृति

#include <type_traits>
#include <tuple>

namespace utility {

    template <std::size_t I = 0, typename FuncT, typename... Tp>
    inline typename std::enable_if<I == sizeof...(Tp), void>::type
        for_each(std::tuple<Tp...> &, const FuncT &)
    {
    }

    template<std::size_t I = 0, typename FuncT, typename... Tp>
    inline typename std::enable_if<I < sizeof...(Tp), void>::type
        for_each(std::tuple<Tp...> & t, const FuncT & f)
    {
        f(std::get<I>(t));
        for_each<I + 1, FuncT, Tp...>(t, f);
    }

}

जाँच:

TEST(ExternalFuncs, test_string_format_on_unique_ptr_0)
{
    for (size_t i = 0; i < 1000000; i++) {
        const std::string v = string_format(0, "%s+%u\n", "test test test", 12345);
        UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
    }
}

TEST(ExternalFuncs, test_string_format_on_unique_ptr_256)
{
    for (size_t i = 0; i < 1000000; i++) {
        const std::string v = string_format(256, "%s+%u\n", "test test test", 12345);
        UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
    }
}

TEST(ExternalFuncs, test_string_format_on_std_string_0)
{
    for (size_t i = 0; i < 1000000; i++) {
        const std::string v = string_format2(0, "%s+%u\n", "test test test", 12345);
        UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
    }
}

TEST(ExternalFuncs, test_string_format_on_std_string_256)
{
    for (size_t i = 0; i < 1000000; i++) {
        const std::string v = string_format2(256, "%s+%u\n", "test test test", 12345);
        UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
    }
}

TEST(ExternalFuncs, test_string_format_on_string_stream_on_variadic_tuple_0)
{
    for (size_t i = 0; i < 1000000; i++) {
        const std::string v = string_format3(0, "test test test", "+", 12345, "\n");
        UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
    }
}

TEST(ExternalFuncs, test_string_format_on_string_stream_on_variadic_tuple_256)
{
    for (size_t i = 0; i < 1000000; i++) {
        const std::string v = string_format3(256, "test test test", "+", 12345, "\n");
        UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
    }
}

TEST(ExternalFuncs, test_string_format_on_string_stream_inline_0)
{
    for (size_t i = 0; i < 1000000; i++) {
        std::ostringstream ss;
        ss << "test test test" << "+" << 12345 << "\n";
        const std::string v = ss.str();
        UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
    }
}

TEST(ExternalFuncs, test_string_format_on_string_stream_inline_256)
{
    for (size_t i = 0; i < 1000000; i++) {
        std::ostringstream ss;
        ss.rdbuf()->str().reserve(256);
        ss << "test test test" << "+" << 12345 << "\n";
        const std::string v = ss.str();
        UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
    }
}

TEST(ExternalFuncs, test_fmt_format_positional)
{
    for (size_t i = 0; i < 1000000; i++) {
        const std::string v = fmt::format("{0:s}+{1:d}\n", "test test test", 12345);
        UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
    }
}

TEST(ExternalFuncs, test_fmt_format_named)
{
    for (size_t i = 0; i < 1000000; i++) {
        const std::string v = fmt::format("{first:s}+{second:d}\n", fmt::arg("first", "test test test"), fmt::arg("second", 12345));
        UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
    }
}

UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR

अनसुना.हप्प :

#define UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(var)   ::utility::unused_param(&var)

namespace utility {

    extern const volatile void * volatile g_unused_param_storage_ptr;

    extern void
#ifdef __GNUC__
    __attribute__((optimize("O0")))
#endif
        unused_param(const volatile void * p);

}

अप्रयुक्त

namespace utility {

    const volatile void * volatile g_unused_param_storage_ptr = nullptr;

    void
#ifdef __GNUC__
    __attribute__((optimize("O0")))
#endif
        unused_param(const volatile void * p)
    {
        g_unused_param_storage_ptr = p;
    }

}

परिणाम :

[ RUN      ] ExternalFuncs.test_string_format_on_unique_ptr_0
[       OK ] ExternalFuncs.test_string_format_on_unique_ptr_0 (556 ms)
[ RUN      ] ExternalFuncs.test_string_format_on_unique_ptr_256
[       OK ] ExternalFuncs.test_string_format_on_unique_ptr_256 (331 ms)
[ RUN      ] ExternalFuncs.test_string_format_on_std_string_0
[       OK ] ExternalFuncs.test_string_format_on_std_string_0 (457 ms)
[ RUN      ] ExternalFuncs.test_string_format_on_std_string_256
[       OK ] ExternalFuncs.test_string_format_on_std_string_256 (279 ms)
[ RUN      ] ExternalFuncs.test_string_format_on_string_stream_on_variadic_tuple_0
[       OK ] ExternalFuncs.test_string_format_on_string_stream_on_variadic_tuple_0 (1214 ms)
[ RUN      ] ExternalFuncs.test_string_format_on_string_stream_on_variadic_tuple_256
[       OK ] ExternalFuncs.test_string_format_on_string_stream_on_variadic_tuple_256 (1325 ms)
[ RUN      ] ExternalFuncs.test_string_format_on_string_stream_inline_0
[       OK ] ExternalFuncs.test_string_format_on_string_stream_inline_0 (1208 ms)
[ RUN      ] ExternalFuncs.test_string_format_on_string_stream_inline_256
[       OK ] ExternalFuncs.test_string_format_on_string_stream_inline_256 (1302 ms)
[ RUN      ] ExternalFuncs.test_fmt_format_positional
[       OK ] ExternalFuncs.test_fmt_format_positional (288 ms)
[ RUN      ] ExternalFuncs.test_fmt_format_named
[       OK ] ExternalFuncs.test_fmt_format_named (392 ms)

जैसा कि आप देख सकते हैं कि vsnprintf+ के माध्यम से कार्यान्वयन std::stringसमान है fmt::format, लेकिन vsnprintf+ के माध्यम से तेजी से std::unique_ptrहोता है, जो कि के माध्यम से तेजी से होता हैstd::ostringstream

परीक्षण संकलित किए गए Visual Studio 2015 Update 3और इसमें चले गए Windows 7 x64 / Intel Core i7-4820K CPU @ 3.70GHz / 16GB

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.