在 C++ 中将 sprintf 与 std::string 一起使用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36908994/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 14:39:27  来源:igfitidea点击:

Using sprintf with std::string in C++

c++stringc++11

提问by daniel the man

I am using sprintffunction in C++ 11, in the following way:

sprintf在 C++ 11 中使用函数,方法如下:

std::string toString()
{
    std::string output;
    uint32_t strSize=512;
    do
    {
        output.reserve(strSize);
        int ret = sprintf(output.c_str(), "Type=%u Version=%u ContentType=%u contentFormatVersion=%u magic=%04x Seg=%u",
            INDEX_RECORD_TYPE_SERIALIZATION_HEADER,
            FORAMT_VERSION,
            contentType,
            contentFormatVersion,
            magic,
            segmentId);

        strSize *= 2;
    } while (ret < 0);

    return output;
}

Is there a better way to do this, than to check every time if the reserved space was enough? For future possibility of adding more things.

有没有更好的方法来做到这一点,而不是每次都检查保留空间是否足够?为了将来添加更多东西的可能性。

回答by DevSolar

Your construct -- writinginto the buffer received from c_str()-- is undefined behaviour, even if you checked the string's capacity beforehand. (The return value is a pointer to constchar, and the function itself marked const, for a reason.)

即使您事先检查了字符串的容量,您的构造 -写入收到的缓冲区c_str()- 也是未定义的行为。(返回值是一个指向constchar的指针,函数本身标记为const,这是有原因的。)

Don't mix C and C++, especiallynot for writing into internal object representation. (That is breaking very basic OOP.) Use C++, for type safety and not running into conversion specifier / parameter mismatches, if for nothing else.

不要混合使用 C 和 C++,尤其不要将其写入内部对象表示。(这打破了非常基本的 OOP。)使用 C++,为了类型安全并且不会遇到转换说明符/参数不匹配,如果没有别的。

std::ostringstream s;
s << "Type=" << INDEX_RECORD_TYPE_SERIALIZATION_HEADER
  << " Version=" << FORMAT_VERSION
  // ...and so on...
  ;
std::string output = s.str();

Alternative:

选择:

std::string output = "Type=" + std::to_string( INDEX_RECORD_TYPE_SERIALIZATION_HEADER )
                   + " Version=" + std::to_string( FORMAT_VERSION )
                   // ...and so on...
                   ;

回答by eerorika

The C++ patterns shown in other answers are nicer, but for completeness, here is a correct way with sprintf:

其他答案中显示的 C++ 模式更好,但为了完整起见,这里有一个正确的方法sprintf

auto format = "your %x format %d string %s";
auto size = std::snprintf(nullptr, 0, format /* Arguments go here*/);
std::string output(size + 1, '
template <typename ...Args>
std::string stringWithFormat(const std::string& format, Args && ...args)
{
    auto size = std::snprintf(nullptr, 0, format.c_str(), std::forward<Args>(args)...);
    std::string output(size + 1, '
std::string message = fmt::sprintf("The answer is %d", 42);
'); std::sprintf(&output[0], format.c_str(), std::forward<Args>(args)...); return output; }
'); std::sprintf(&output[0], format, /* Arguments go here*/);

Pay attention to

注意

  • You must resizeyour string. reservedoes not change the size of the buffer. In my example, I construct correctly sized string directly.
  • c_str()returns a const char*. You may not pass it to sprintf.
  • std::stringbuffer was not guaranteed to be contiguous prior to C++11 and this relies on that guarantee. If you need to support exotic pre-C++11 conforming platforms that use rope implementation for std::string, then you're probably better off sprintinginto std::vector<char>first and then copying the vector to the string.
  • This only works if the arguments are not modified between the size calculation and formatting; use either local copies of variables or thread synchronisation primitives for multi-threaded code.
  • 你必须resize你的字符串。reserve不会改变缓冲区的大小。在我的示例中,我直接构造了正确大小的字符串。
  • c_str()返回一个const char*. 您可能不会将其传递给sprintf.
  • std::string在 C++11 之前,缓冲区不能保证是连续的,这依赖于该保证。如果您需要支持异国预C ++ 11个符合平台,利用绳索实施std::string,那么你可能会更好过冲刺std::vector<char>第一,然后复制向量的字符串。
  • 这仅在大小计算和格式设置之间未修改参数时才有效;对多线程代码使用变量的本地副本或线程同步原语。

回答by zergeny

We can mix code from here https://stackoverflow.com/a/36909699/2667451and here https://stackoverflow.com/a/7257307and result will be like that:

我们可以从这里https://stackoverflow.com/a/36909699/2667451和这里https://stackoverflow.com/a/7257307混合代码,结果将是这样的:

std::string message = fmt::format("The answer is {}", 42);`

回答by Tavi Cacina

A better way is to use the {fmt} library. Ex:

更好的方法是使用{fmt} 库。前任:

std::ostringstream ss;
ss << "Type=" << INDEX_RECORD_TYPE_SERIALIZATION_HEADER
   << " Version=" << FORAMT_VERSION
   << /* ... the rest ... */;
return ss.str();

It exposes also a nicer interface than iostreams and printf. Ex:

它还公开了比 iostreams 和 printf 更好的接口。前任:

##代码##

See:
https://github.com/fmtlib/fmt
http://fmtlib.net/latest/api.html#printf-formatting-functions

请参阅:
https: //github.com/fmtlib/fmt
http://fmtlib.net/latest/api.html#printf-formatting-functions

回答by Angew is no longer proud of SO

Your code is wrong. reserveallocates memory for the string, but does not change its size. Writing into the buffer returned by c_strdoes not change its size either. So the string still believes its size is 0, and you've just written something into the unused space in the string's buffer. (Probably. Technically, the code has Undefined Behaviour, because writing into c_stris undefined, so anything could happen).

你的代码是错误的。reserve为字符串分配内存,但不改变其大小。写入由返回的缓冲区c_str也不会改变其大小。所以字符串仍然认为它的大小是 0,而您刚刚在字符串缓冲区中未使用的空间中写入了一些内容。(可能。从技术上讲,代码具有未定义的行为,因为写入c_str未定义,所以任何事情都可能发生)。

What you really want to do is forget sprintfand similar C-style functions, and use the C++ way of string formatting—string streams:

你真正想做的是忘记sprintf和类似的 C 风格的函数,并使用 C++ 的字符串格式化方式——字符串流:

##代码##

回答by CiaPan

Yes, there is!

就在这里!

In C, the better way is to associate a file with the nulldevice and make a dummy printfof the desired output to it, to learn how much space would it take if actually printed. Then allocate appropriate buffer and sprintfthe same data to it.

在 C 中,更好的方法是将文件与设备相关联,并printf为它制作一个所需输出的虚拟对象,以了解如果实际打印需要多少空间。然后为其分配适当的缓冲区和sprintf相同的数据。

In C++ you could associate the output stream with a null device, too, and test the number of charactes printed with std::ostream::tellp. However, using ostringstreamis a way better solution – see the answers by DevSolar or Angew.

在 C++ 中,您也可以将输出流与空设备关联,并测试使用std::ostream::tellp打印的字符数。但是,使用ostringstream是一种更好的解决方案 - 请参阅 DevSolar 或 Angew 的答案。