关于Unix:C ++:如何在没有sprintf的情况下将fprintf结果作为std :: string获得

关于Unix:C ++:如何在没有sprintf的情况下将fprintf结果作为std :: string获得

C++: how to get fprintf results as a std::string w/o sprintf

我正在使用以C ++实现的开源UNIX工具,我需要更改一些代码才能使其执行我想要的操作。我希望进行最小的更改,以期使我的补丁程序被上游接受。首选可在标准C ++中实现且不会创建更多外部依赖项的解决方案。

这是我的问题。我有一个C ++类(我们称它为" A"),该类当前使用fprintf()将其格式化的数据结构打印到文件指针上。在其打印功能中,它还递归调用几个成员类的相同定义的打印功能(" B"为示例)。还有另一个类C,其成员std :: string" foo"需要设置为A实例的print()结果。将其视为A的to_str()成员函数。

用伪代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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(以及A的其他受抚养人)处于不断变化的状态,因此所需的代码更改越少越好。当前的print(FILE * F)接口也需要保留。我考虑了几种实现A :: to_str()的方法,每种方法都有其优点和缺点:

  • 将对fprintf()的调用更改为sprintf()

    • 我不必重写任何格式字符串
    • print()可以重新实现为:fprint(f,this.to_str());
    • 但是我需要手动分配char [] s,合并很多c字符串,最后将字符数组转换为std :: string
  • 尝试在字符串流中捕获a.print()的结果

    • 我必须将所有格式字符串都转换为<<输出格式。有数百个fprintf()可以转换:-{
    • print()必须重写,因为我不知道从UNIX文件句柄创建输出流的标准方法(尽管这个人说这是可能的)。
  • 使用Boost的字符串格式库

    • 更多的外部依赖性。 uck
    • 格式的语法与printf()的区别足以使人烦恼:

    printf(format_str,args)-> cout << boost :: format(format_str)%arg1%arg2%等

  • 使用Qt的QString :: asprintf()

    • 不同的外部依赖性。
  • 那么,我是否穷尽了所有可能的选择?如果是这样,您认为我的最佳选择是什么?如果没有,我忽略了什么?

    谢谢。


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    #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,而不是确定多少空间是必需的。这是我修改后的代码,大家可以看看吗,如果您认为还可以,我将再次编辑以列出唯一的成本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    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];
        }
    }

    我正在使用#3:boost字符串格式库-但我必须承认,格式规范的差异从来没有任何问题。

    对我而言,它的工作就像一个魅力-外部依赖可能会更糟(一个非常稳定的库)

    编辑:添加示例如何使用boost :: format而不是printf:

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

    boost :: format库将是这样的:

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

    希望这有助于阐明boost :: format的用法

    我已经在4或5个应用程序中将boost :: format用作sprintf / printf的替代品(将格式化的字符串写入文件,或将自定义输出写入日志文件),并且从未遇到格式差异的问题。可能有一些(或多或少晦涩的)格式说明符有所不同-但我从来没有遇到过问题。

    相比之下,我有一些我无法真正使用流的格式规范(据我所记得)


    您可以使用std :: string和iostream进行格式化,例如setw()调用以及iomanip中的其他格式


    您应该尝试使用Loki库的SafeFormat头文件(http://loki-lib.sourceforge.net/index.php?n=Idioms.Printf)。它与boost的字符串格式库相似,但是保留了printf(...)函数的语法。

    我希望这有帮助!


    以下是替代解决方案:

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

    (B::printto类似),并定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    void A::print(FILE *f) {
        printto(ofstream(f));
    }

    string A::to_str() {
        ostringstream os;
        printto(os);
        return os.str();
    }

    当然,您应该真正使用snprintf而不是sprintf来避免缓冲区溢出。您也可以有选择地将风险较大的sprintfs更改为<<格式,以确保安全,但更改尽可能少。


    {fmt}库提供了fmt::sprintf函数,该函数执行与printf兼容的格式(包括根据POSIX规范的位置参数),并以std::string的形式返回结果:

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

    免责声明:我是这个图书馆的作者。


    这是有关序列化的吗?还是打印正确?
    如果是前者,请考虑也使用boost :: serialization。这都是关于对象和子对象的"递归"序列化。


    推荐阅读

      linux命令下载工具?

      linux命令下载工具?,工具,网络,代理,代码,简介,位置,系统,第一,下载工具,文

      linux命令行执行成功?

      linux命令行执行成功?,系统,信息,状态,服务,管理,百度,设计,灵活,代码,命令,L

      安卓执行linux命令行?

      安卓执行linux命令行?,系统,设备,基础,发展,标准,情况,信息,电话,号码,工具,

      linux分辨率操作命令?

      linux分辨率操作命令?,系统,情况,分辨率,底部,状态,命令,屏幕,屏幕分辨率,

      linux执行中退出命令?

      linux执行中退出命令?,档案,状态,命令,分析,数据,电脑,实时,系统,工具,编辑,l

      linux定时执行命令?

      linux定时执行命令?,时间,系统,服务,任务,工作,标准,情况,周期性,工具,命令,l

      linux好用的命令工具?

      linux好用的命令工具?,系统,管理,工具,基础,服务,信息,工作,发行,公司,代码,L

      linux命令行操作软件?

      linux命令行操作软件?,软件,工具,系统,名称,管理,工作,命令,设计,平台,标准,

      linux看命令执行过程?

      linux看命令执行过程?,系统,服务,状态,软件,时间,数据,地址,命令,进程,情况,l

      linux登陆执行命令?

      linux登陆执行命令?,系统,服务,工具,地址,密码,百度,管理,检测,网络,第一,怎

      linux权限命令可执行?

      linux权限命令可执行?,系统,工具,信息,权限,数字,网络,发行,底部,代码,文件,l

      linux常用命令行工具?

      linux常用命令行工具?,系统,工作,工具,地址,管理,信息,命令,软件,目录,基础,l

      linux命令行专业工具?

      linux命令行专业工具?,工具,系统,工作,信息,服务,环境,基础,命令,管理,发行,l

      linux常见操作命令?

      linux常见操作命令?,系统,工作,信息,管理,地址,命令,目录,单位,数据,标准,lin

      linux远程执行多命令?

      linux远程执行多命令?,工具,服务,命令,状态,暂停,代码,底部,时间,地址,系统,L

      linux下执行命令行?

      linux下执行命令行?,工作,系统,信息,单位,命令,基础,地址,设备,权威,标准,mv

      linux安装执行命令?

      linux安装执行命令?,系统,软件,网络,密码,官方网站,在线,工作,盘中,电脑,第

      linux执行命令超时?

      linux执行命令超时?,时间,代码,系统,名字,环境,工作,服务,下来,名称,地址,如

      linux远程执行多命令?

      linux远程执行多命令?,工具,服务,命令,状态,暂停,代码,底部,时间,地址,系统,L

      红帽子linux操作命令?

      红帽子linux操作命令?,服务,系统,密码,环境,信息,通用,软件,状态,设备,命令,