在C++中使用带有std :: string的sprintf

dan*_*man 12 c++ string c++11

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;
}
Run Code Online (Sandbox Code Playgroud)

有没有比这更好的方法来检查每次保留的空间是否足够?为了将来添加更多东西的可能性.

Dev*_*lar 15

您的构造 - 写入从缓冲区接收的缓冲区c_str()未定义的行为,即使您事先检查了字符串的容量.(返回值是指向const char 的指针,并且函数本身标记为const,原因.)

不要混用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();
Run Code Online (Sandbox Code Playgroud)

替代方案:

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

  • 我如何模拟 ostringstream 中的字符串格式,比如 magic=%04x 等等? (2认同)
  • @CiaPan 从技术上讲,这并不意味着代码甚至不应该编译。如果我们假设从 const 到非 const 的隐式转换是错误的,则意味着编译器不需要编译代码,但编译器可以支持将转换作为非标准语言扩展。如果程序根据标准格式不正确,编译器只需向用户提供诊断消息。*如果*它确实编译通过,那么通过 const 指针的写入就是 UB。 (2认同)

eer*_*ika 8

其他答案中显示的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, '\0');
std::sprintf(&output[0], format, /* Arguments go here*/);
Run Code Online (Sandbox Code Playgroud)

注意

  • 你必须是resize你的字符串.reserve不会改变缓冲区的大小.在我的例子中,我直接构造正确大小的字符串.
  • c_str()返回一个const char*.你可能不会把它传递给sprintf.
  • std::string在C++ 11之前,缓冲区不保证是连续的,这依赖于该保证.如果您需要支持异国预C++ 11个符合使用绳索实施平台std::string,那么你可能会更好过冲刺std::vector<char>第一,然后复制向量的字符串.
  • 这仅适用于在大小计算和格式之间未修改参数的情况; 使用变量的本地副本或线程同步原语用于多线程代码.


小智 6

我们可以混合这里的代码/sf/answers/2583678961/和这里的/sf/answers/508011521/,结果将是这样的:

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, '\0');
    std::sprintf(&output[0], format.c_str(), std::forward<Args>(args)...);
    return output;
}
Run Code Online (Sandbox Code Playgroud)