C++:كيفية الحصول على fprintf النتائج كما std::string w/o sprintf

StackOverflow https://stackoverflow.com/questions/69738

سؤال

أنا أعمل مع المصدر المفتوح UNIX الأداة التي يتم تنفيذها في C++ و أنا بحاجة إلى تغيير بعض التعليمات البرمجية على أن أفعل ما أريد.وأود أن جعل أصغر من الممكن تغيير أملا في الحصول على بلدي التصحيح قبول المنبع.حلول قابلة للتنفيذ في المعيار C++ و لا خلق المزيد من الاعتماد على الخارج المفضلة.

هنا هو بلدي المشكلة.لدي فئة C++ -- دعونا نسميها "أ" -- التي تستخدم حاليا fprintf() لطباعة لها بشكل كبير مهيأ هياكل البيانات إلى ملف المؤشر.في وظيفة الطباعة ، كما متكرر يدعو مماثل تعريف الطباعة وظائف العديد من الأعضاء في الطبقات ("B" هو مثال على ذلك).هناك فئة أخرى C الذي يحتوي على الأعضاء std::string "فو" هذا يحتاج إلى أن يتم تعيين الطباعة() نتائج مثيل A.كما أنها تفكر في to_str() الدالة العضو A.

في شبة الكود:

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

أود أن أذكر أن ج مستقرة إلى حد ما ، ولكن أ و ب (و بقية من المعالين) هي في حالة تغير مستمر ، وبالتالي فإن أقل رمز التغييرات اللازمة على نحو أفضل.الطباعة الحالية(FILE* F) واجهة أيضا يحتاج إلى الحفاظ عليه.لقد نظرت عدة طرق تنفيذ::to_str () ، مع كل المزايا و العيوب:

  1. تغيير يدعو إلى fprintf() إلى sprintf()

    • لن تضطر إلى كتابة أي شكل سلاسل
    • طباعة() يمكن أن يكون ل reimplemented مثل:fprint(و هذا.to_str());
    • ولكن سوف تحتاج إلى يدويا تخصيص char[]s, دمج الكثير من ج سلاسل , وأخيرا تحويل الحرف الصفيف إلى std::string
  2. في محاولة للقبض على نتائج.طباعة() في سلسلة تيار

    • كنت قد لتحويل كل شكل سلاسل << تنسيق الإخراج.هناك المئات من fprintf()s لتحويل :-{
    • طباعة() يجب أن تعاد كتابة لأنه لا توجد طريقة قياسية أن أعرف من خلق تيار الإخراج من UNIX التعامل مع ملف (على الرغم من يقول هذا الرجل قد يكون من الممكن).
  3. استخدام دفعة سلسلة شكل مكتبة

    • المزيد من الاعتماد على الخارج.يع.
    • شكل جملة مختلفة بما فيه الكفاية من printf() أن تكون مزعجة:

    printf(format_str, وسائط) -> cout << دفعة::شكل(format_str) % arg1 % arg2 % الخ

  4. استخدام "كيو تي" QString::asprintf()

    • مختلفة تبعية خارجية.

حتى لقد استنفدت جميع الخيارات الممكنة?إذا كان الأمر كذلك ، ما رأيك هو الأفضل ؟ إذا لم يكن كذلك ، ما قد أشرفت ؟

شكرا

هل كانت مفيدة؟

المحلول

أنا باستخدام #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);

آمل أن يساعد هذا توضيح استخدام دفعة::تنسيق

لقد استعملت دفعة::شكل sprintf / printf بديل في 4 أو 5 تطبيقات (كتابة تنسيق السلاسل إلى الملفات أو العرف الإخراج إلى logfiles) وليس لديهم مشاكل مع شكل الاختلافات.قد يكون هناك بعض (أكثر أو أقل غامضة) شكل محددات والتي هي بشكل مختلف - لكن لم يكن لدي مشكلة.

في المقابل كان بعض مواصفات تنسيق لم أستطع حقا مع تيارات (كما أتذكر)

نصائح أخرى

هنا لغة أحب لجعل وظيفة مماثلة 'sprintf', ولكن العودة std::string و المناعة إلى تجاوز سعة المخزن المؤقت المشاكل.هذا القانون هو جزء من مشروع مفتوح المصدر أن أكتب (BSD license) ، لذلك الجميع لا تتردد في استخدام هذا كما يحلو لك.

#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 المطابقة و أن ويندوز (وكذلك كبار السن سي العمومية) مختلفة 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];
    }
}

يمكنك استخدام std::string و iostreams مع التنسيق ، مثل setw() دعوة الآخرين في iomanip

التالية قد تكون حلا بديلا:

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 لتجنب تجاوزات المخزن المؤقت.هل يمكن أيضا بشكل انتقائي تغيير أكثر خطورة sprintfs إلى << تنسيق لتكون أكثر أمانا و بعد تغيير أقل قدر ممكن.

يجب أن تحاول لوكي المكتبة SafeFormat الملف header (http://loki-lib.sourceforge.net/index.php?n=Idioms.Printf).انها مماثلة دفعة شكل سلسلة المكتبة ، ولكن يبقى جملة printf(...) وظائف.

آمل أن يساعد هذا!

هو هذا التسلسل?أو طباعة مناسب ؟ لو السابق ، النظر في دفعة::التسلسل كذلك.انها كل شيء عن "متكررة" التسلسل من الكائنات sub-object.

فإن {fmt} مكتبة يوفر fmt::sprintf الوظيفة التي يقوم printfمتوافق مع تنسيق (بما في ذلك الموضعية الحجج وفقا مواصفات POSIX) وإرجاع النتيجة std::string:

std::string s = fmt::sprintf("The answer is %d.", 42);

تنويه:أنا صاحب هذه المكتبة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top