C ++:如何获得fprintf结果作为std :: string w / o 输出

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (50)

我正在使用一个在C++中实现的开源UNIX工具,我需要修改一些代码,使它能够实现我想做的事情。我想做尽可能小的改变。首选在标准C++中可实现且不创建更多外部依赖关系的解决方案。

这是我的问题。我有一个C++类--让我们称之为“A”--它目前使用fprintf()将其格式很重的数据结构输出到文件指针。在它的print函数中,它还递归地调用几个成员类的相同定义的打印函数(“B”就是一个例子)。还有一个类C有一个成员std::string“foo”,需要设置为A实例的print()结果。_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());

我应该指出,C是相当稳定的,但是A和B处于变化状态,所以代码所需的变化越少越好。当前输出(文件)*(F)接口也需要保留。我曾考虑过几种实施A的办法:_Str(),各有优缺点:

  1. 将对fprintf()的调用更改为sprintf()
- I wouldn't have to rewrite any format strings
- print() could be reimplemented as: fprint(f, this.to\_str());
- But I would need to manually allocate char[]s, merge a lot of c strings , and finally convert the character array to a std::string

  1. 尝试在字符串流中捕获.print()的结果
- I would have to convert all of the format strings to << output format. There are hundreds of fprintf()s to convert :-{
- print() would have to be rewritten because there is no standard way that I know of to create an output stream from a UNIX file handle (though [this guy says it may be possible](http://synflood.at/blog/index.php?/archives/456-One-word-of-warning-about-stdio_filebuf.html)).

  1. 使用Boost的字符串格式库
- More external dependencies. Yuck.
- Format's syntax is different enough from printf() to be annoying: 

输出格式_>cout<<Boost::Format(格式)_1%,2%,等等

  1. 使用QTQString::asprintf()
- A different external dependency.

那么,我已经用尽所有可能的选择了吗?如果是的话,你认为哪一种是我的最佳选择?如果没有,我忽略了什么?

提问于
用户回答回答于

添加示例如何使用boost :: format代替printf:

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

将会像boost :: format库一样:

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

希望这有助于澄清boost :: format的用法

用户回答回答于

下面是我喜欢的代码,用于使功能与‘sprintf’相同,但返回一个std::string,并避免缓冲区溢出问题。这段代码是我正在编写的开源项目的一部分(BSD许可证),所以每个人都可以随意使用它。

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

扫码关注云+社区