2008-09-16 14 views
17

के रूप में fprintf परिणामों को कैसे प्राप्त करें I C++ में कार्यान्वित एक ओपन-सोर्स यूनिक्स टूल के साथ काम कर रहा हूं, और मुझे कुछ कोड बदलने की आवश्यकता है जो मैं इसे करने के लिए करता हूं। चाहते हैं। मैं अपने पैच को अपस्ट्रीम स्वीकार करने की उम्मीद में सबसे छोटा संभव परिवर्तन करना चाहता हूं। समाधान जो मानक सी ++ में कार्यान्वित करने योग्य हैं और अधिक बाह्य निर्भरता नहीं बनाते हैं उन्हें प्राथमिकता दी जाती है।सी ++: std :: स्ट्रिंग w/o sprintf

यहां मेरी समस्या है। मेरे पास एक सी ++ वर्ग है - चलिए इसे "ए" कहते हैं - जो वर्तमान में फ़ाइल सूचक में अपने भारी स्वरूपित डेटा संरचनाओं को मुद्रित करने के लिए fprintf() का उपयोग करता है। अपने प्रिंट फ़ंक्शन में, यह कई सदस्य वर्गों ("बी" का एक उदाहरण है) के समान रूप से परिभाषित प्रिंट फ़ंक्शंस को बार-बार कॉल करता है। एक और वर्ग सी है जिसमें एक सदस्य std :: string "foo" है जिसे ए के उदाहरण के प्रिंट() परिणामों पर सेट करने की आवश्यकता है। इसे ए

के लिए एक to_str() सदस्य फ़ंक्शन के रूप में सोचें। स्यूडोकोड में:

class A { 
public: 
    ... 

    void print(FILE* f); 
    B b; 

    ... 
}; 

... 

void A::print(FILE *f) 
{ 
    std::string s = "stuff"; 
    fprintf(f, "some %s", s); 
    b.print(f); 
} 

class C { 
    ... 
    std::string foo; 
    bool set_foo(std::str); 
    ... 
} 

... 

A a = new A(); 
C c = new C(); 

... 

// wish i knew how to write A's to_str() 
c.set_foo(a.to_str()); 

मैं उल्लेख करना चाहिए कि सी काफी स्थिर है, लेकिन इतना कम कोड बेहतर आवश्यक परिवर्तन ए और बी (और एक के आश्रितों के बाकी), प्रवाह के एक राज्य में हैं। वर्तमान प्रिंट (फ़ाइल * एफ) इंटरफ़ेस को भी संरक्षित करने की आवश्यकता है।

  1. बदलें fprintf के लिए कॉल() sprintf के लिए()

    • मैं नहीं करनी होगी: मैं) को लागू करने के लिए एक :: to_str (करने के लिए कई दृष्टिकोण, फायदे और नुकसान के साथ प्रत्येक पर विचार किया है किसी भी प्रारूप स्ट्रिंग को फिर से लिखें
    • प्रिंट() को फिर से कार्यान्वित किया जा सकता है: fprint (f, this.to_str());
    • लेकिन मैं मैन्युअल रूप से आवंटित करने के लिए चार [] रों ग तार का एक बहुत विलय, और अंत में एक std :: स्ट्रिंग के लिए चरित्र सरणी परिवर्तित a.print के परिणामों को पकड़ने के लिए
  2. प्रयास की आवश्यकता होगी() एक स्ट्रिंग धारा

    • मैं < < उत्पादन प्रारूप करने के लिए प्रारूप तार के सभी परिवर्तित करने के लिए होता है। कनवर्ट करने के लिए सैकड़ों fprintf() s हैं: - {
    • प्रिंट() को फिर से लिखा जाना चाहिए क्योंकि यूनिक्स फ़ाइल हैंडल (हालांकि this guy says it may be possible) से आउटपुट स्ट्रीम बनाने के लिए मुझे कोई मानक तरीका नहीं है।
  3. उपयोग बूस्ट की स्ट्रिंग format library

    • अधिक बाहरी निर्भरता। छी।
    • स्वरूप की वाक्य रचना printf से काफी अलग है() कष्टप्रद होने के लिए:

    printf (format_str, args) -> अदालत < < बढ़ावा :: प्रारूप (format_str)% ARG1% ARG2% आदि

  4. क्यूटी के QString::asprintf()

    • एक अलग बाहरी निर्भरता का उपयोग करें।

तो, मैं हर संभव विकल्प समाप्त हो? यदि हां, तो आपको क्या लगता है कि मेरी सबसे अच्छी शर्त है? यदि नहीं, तो मैंने क्या अनदेखा किया है?

धन्यवाद।

+0

हालांकि मैं पहले से ही इस सवाल का जवाब, मैं भी इस परियोजना का कहना चाहते हैं: https://github.com/c42f/tinyformat जो अच्छी तरह से समस्या का हल है, और वास्तव में printf स्वरूपण अंकन प्रजनन का एक बड़ा काम करता है। इन दिनों, मैं अपने पैकेज में कुछ साल पहले विस्तृत किए गए बनामप्रिंट विधि की बजाय सीधे उस पैकेज का उपयोग करता हूं। –

उत्तर

12

मैं # 3 का उपयोग कर रहा हूं: बूस्ट स्ट्रिंग प्रारूप लाइब्रेरी - लेकिन मुझे यह मानना ​​है कि मुझे प्रारूप विनिर्देशों में मतभेदों के साथ कभी भी कोई समस्या नहीं है।

निर्माण मेरे लिए एक आकर्षण की तरह - और बाहरी निर्भरता भी बदतर हो सकता है (एक बहुत ही स्थिर पुस्तकालय)

संपादित: कैसे उपयोग करने के लिए बढ़ावा :: printf के बजाय प्रारूप एक उदाहरण जोड़ने:

sprintf(buffer, "This is a string with some %s and %d numbers", "strings", 42); 

बढ़ावा :: प्रारूप पुस्तकालय के साथ कुछ इस तरह होगा:

string = boost::str(boost::format("This is a string with some %s and %d numbers") %"strings" %42); 

आशा इस बढ़ावा के उपयोग :: प्रारूप

स्पष्ट में मदद करता है

मैंने 4 या 5 अनुप्रयोगों में स्प्रिंटफ/प्रिंटफ प्रतिस्थापन के रूप में बूस्ट :: प्रारूप का उपयोग किया है (फाइलों के लिए स्वरूपित स्ट्रिंग लिखना, या लॉगफाइल पर कस्टम आउटपुट) और स्वरूप अंतरों में कभी समस्या नहीं थी। कुछ (अधिक या कम अस्पष्ट) प्रारूप विनिर्देशक हो सकते हैं जो अलग-अलग हैं - लेकिन मुझे कोई समस्या नहीं थी।

इसके विपरीत मैं ऐसे setw() कॉल के रूप में कुछ प्रारूप विनिर्देशों मैं वास्तव में धाराओं के साथ नहीं कर सका था, (जैसा कि मुझे याद है के रूप में ज्यादा)

+0

बूस्ट :: प्रारूप के उपयोग पर स्पष्टीकरण के लिए धन्यवाद। यह प्रलोभन दिया गया है कि यह प्रोजेक्ट पहले से ही एक और बूस्ट लाइब्रेरी पर निर्भर करता है, लेकिन मुझे नहीं लगता कि कुछ भी प्रिंटफैट को धड़कता है जो सिर्फ std :: स्ट्रिंग के साथ काम करता है क्योंकि लोकी ऐसा लगता है। – underspecified

+0

मैंने लोकी के सुरक्षितफॉर्मेट की कोशिश की, लेकिन यह पता चला कि यह सिर्फ() एस के लिए बूस्ट के 5s को स्वैप कर रहा है। एक सकारात्मक नोट पर, मेरे कोड को बढ़ावा देने के बाद काम किया गया था :: प्रारूप :-) – underspecified

+0

सुनना अच्छा लगता है :: प्रारूप आपके लिए काम करता है - कभी लोकी विधि की कोशिश नहीं की। – bernhardrusch

0

क्या यह क्रमबद्धता के बारे में है? या उचित मुद्रण? यदि पूर्व, बूस्ट :: क्रमबद्धता पर विचार करें। यह ऑब्जेक्ट्स और उप-ऑब्जेक्ट के "पुनरावर्ती" क्रमिकरण के बारे में है।

+0

यह उचित मुद्रण के बारे में है। सीएफयू डेटा का एक टुकड़ा है जिसे अंततः उपयोगकर्ता (बड़े पैमाने पर) के रूप में दिखाया जाता है। अगर यह मेरा कोड था, तो मैं प्रिंट (फ़ाइल *) बकवास खो देता हूं जो अब तक बहुत ही सीमित है। – underspecified

1

आप std :: स्ट्रिंग का उपयोग और स्वरूपण के साथ iostreams कर सकते हैं और अन्य Iomanip

+0

धन्यवाद मिल गया है। यह निश्चित रूप से उपयोगी दिखता है, लेकिन मैं जितना संभव हो सके मौजूदा प्रारूप स्ट्रिंग को संरक्षित करने की कोशिश करना चाहता हूं। – underspecified

35

यहां 'idintom' की तरह कार्यक्षमता बनाने के लिए मुझे पसंद है, लेकिन एक std :: स्ट्रिंग लौटा रहा है, और बफर अतिप्रवाह समस्याओं को प्रतिरक्षा करने के लिए यहां है। यह कोड एक ओपन सोर्स प्रोजेक्ट का हिस्सा है जिसे मैं लिख रहा हूं (बीएसडी लाइसेंस), इसलिए हर कोई इसका इस्तेमाल करने के लिए स्वतंत्र महसूस करता है।

#include <string> 
#include <cstdarg> 
#include <vector> 
#include <string> 

std::string 
format (const char *fmt, ...) 
{ 
    va_list ap; 
    va_start (ap, fmt); 
    std::string buf = vformat (fmt, ap); 
    va_end (ap); 
    return buf; 
} 



std::string 
vformat (const char *fmt, va_list ap) 
{ 
    // Allocate a buffer on the stack that's big enough for us almost 
    // all the time. 
    size_t size = 1024; 
    char buf[size]; 

    // Try to vsnprintf into our buffer. 
    va_list apcopy; 
    va_copy (apcopy, ap); 
    int needed = vsnprintf (&buf[0], size, fmt, ap); 
    // NB. On Windows, vsnprintf returns -1 if the string didn't fit the 
    // buffer. On Linux & OSX, it returns the length it would have needed. 

    if (needed <= size && needed >= 0) { 
     // It fit fine the first time, we're done. 
     return std::string (&buf[0]); 
    } else { 
     // vsnprintf reported that it wanted to write more characters 
     // than we allotted. So do a malloc of the right size and try again. 
     // This doesn't happen very often if we chose our initial size 
     // well. 
     std::vector <char> buf; 
     size = needed; 
     buf.resize (size); 
     needed = vsnprintf (&buf[0], size, fmt, apcopy); 
     return std::string (&buf[0]); 
    } 
} 

संपादित करें: जब मैं इस कोड लिखा था, मुझे नहीं पता था कि यह आवश्यक C99 अनुरूपता और Windows (और साथ ही पुराने glibc) विभिन्न vsnprintf व्यवहार, जिसमें यह रिटर्न -1 विफलता के लिए किया था कि, बजाय एक निश्चित उपाय कितना अंतरिक्ष की आवश्यकता है। यहाँ मेरी संशोधित कोड है, हर कोई इस पर दिखाई दे सकता है और अगर आपको लगता है यह ठीक है, मैं फिर से संपादित करेंगे कि सूचीबद्ध केवल लागत बनाने के लिए:

std::string 
Strutil::vformat (const char *fmt, va_list ap) 
{ 
    // Allocate a buffer on the stack that's big enough for us almost 
    // all the time. Be prepared to allocate dynamically if it doesn't fit. 
    size_t size = 1024; 
    char stackbuf[1024]; 
    std::vector<char> dynamicbuf; 
    char *buf = &stackbuf[0]; 
    va_list ap_copy; 

    while (1) { 
     // Try to vsnprintf into our buffer. 
     va_copy(ap_copy, ap); 
     int needed = vsnprintf (buf, size, fmt, ap); 
     va_end(ap_copy); 

     // NB. C99 (which modern Linux and OS X follow) says vsnprintf 
     // failure returns the length it would have needed. But older 
     // glibc and current Windows return -1 for failure, i.e., not 
     // telling us how much was needed. 

     if (needed <= (int)size && needed >= 0) { 
      // It fit fine so we're done. 
      return std::string (buf, (size_t) needed); 
     } 

     // vsnprintf reported that it wanted to write more characters 
     // than we allotted. So try again using a dynamic buffer. This 
     // doesn't happen very often if we chose our initial size well. 
     size = (needed > 0) ? (needed+1) : (size*2); 
     dynamicbuf.resize (size); 
     buf = &dynamicbuf[0]; 
    } 
} 
+0

प्रारूप/vformat पर अच्छा काम। हो सकता है कि स्टैकओवरफ्लो को किसी प्रकार का कोड स्निपिट शेयरिंग सेक्शन की आवश्यकता हो :-) – underspecified

+1

ऐसा लगता है कि परिणामी स्ट्रिंग 1024 बाइट्स के बाद बड़ी हो तो आपका कोड काम नहीं करेगा। एमएसडीएन के मुताबिक: बनामप्रिंटफ - रिटर्न वैल्यू ... अगर लिखने के लिए वर्णों की संख्या गिनती से अधिक है, तो ये फ़ंक्शन -1 लौटते हैं जो दर्शाते हैं कि आउटपुट को छोटा कर दिया गया है। – Andreas

+1

यदि आप लूप में ऐसा नहीं कर रहे हैं तो एक वेरिएबल सरणी आकार का उपयोग क्यों करें? 'एपी' की प्रतिलिपि बनाने की कोई आवश्यकता नहीं है, जो महंगा हो सकता है। http://www.tin.org/bin/man.cgi?section=3&topic=snprintf – Bklyn

1

निम्नलिखित एक वैकल्पिक समाधान हो सकता है:

void A::printto(ostream outputstream) { 
    char buffer[100]; 
    string s = "stuff"; 
    sprintf(buffer, "some %s", s); 
    outputstream << buffer << endl; 
    b.printto(outputstream); 
} 

(B::printto समान), और परिभाषित

void A::print(FILE *f) { 
    printto(ofstream(f)); 
} 

string A::to_str() { 
    ostringstream os; 
    printto(os); 
    return os.str(); 
} 
बेशक

, तुम सच में snprintf बजाय sprintf का उपयोग करें बफर अतिप्रवाह से बचने के लिए करना चाहिए। आप अधिक जोखिम भरा स्प्रिंट्स को < < प्रारूप में चुनिंदा रूप से बदल सकते हैं, ताकि सुरक्षित हो और फिर भी जितना संभव हो सके बदल सकें।

+0

मैं आपका उत्तर # 1 और # 2 के संयोजन के रूप में ले जाऊंगा :-) क्या स्ट्रीमस्ट्रीम में एक कन्स्ट्रक्टर है जो फ़ाइल हैंडल लेता है? मैं इस धारणा के तहत था कि वे असंगत थे ... – underspecified

+0

@underspecified: आप सही थे। – smerlin

1

आप लोकी लाइब्रेरी की SafeFormat हेडर फाइल (http://loki-lib.sourceforge.net/index.php?n=Idioms.Printf) कोशिश करनी चाहिए। यह बूस्ट की स्ट्रिंग प्रारूप लाइब्रेरी के समान है, लेकिन printf (...) फ़ंक्शंस का सिंटैक्स रखता है।

मुझे आशा है कि इस मदद करता है!

+0

कृपया कुछ टूल या लाइब्रेरी को उत्तर के रूप में न पोस्ट करें। उत्तर में कम से कम प्रदर्शन [यह समस्या कैसे हल करता है] (http://meta.stackoverflow.com/a/251605)। –

संबंधित मुद्दे