我必须用sprintf格式化std::string,并将其发送到文件流。我该怎么做呢?


当前回答

我现在将为Visual Studio编写版本,希望有一天有人会让它变得可移植。(怀疑需要用vsnwprintf替换_vsnwprintf之类的东西。)

您需要使用项目配置中的define _CRT_SECURE_NO_WARNINGS来禁用已弃用的警告。

我使用_vsnwprintf与第一个参数作为nullptr能够估计缓冲区大小,保留wstring缓冲区,然后格式化字符串直接到缓冲区。

不确定为什么需要禁用已弃用警告,因为相同方法调用(_vsnwprintf_s)的安全版本不能使用nullptr作为输入。怀疑需要报告给微软c++团队。

这个版本应该同时使用- string类或wstring类。

如果你发现任何错误或不一致,请再问一次,我会尽力修复它。

stringHelpers.h:

#pragma once
#include <string>

//
//  Formats string/wstring according to format, if formatting fails (e.g. invalid %s pointer - returns empty string)
//
template <typename T>
std::basic_string<T> sFormat(const T* format, ...)
{
    va_list args;
    va_start(args, format);
    int size;

    if constexpr (std::is_same_v<T, char>)
        size = vsnprintf(nullptr, 0, format, args);
    else
        size = _vsnwprintf(nullptr, 0, format, args);

    size++; // Zero termination
    std::basic_string<T> s;
    s.resize(size);

    if constexpr (std::is_same_v<T, char>)
        vsnprintf(&s[0], size, format, args);
    else
        _vsnwprintf(&s[0], size, format, args);

    va_end(args);
    return s;
}

以上是代码示例,可以复制。我将维护工作版本在我自己的仓库在github:

https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/helpers.h#L12

其他回答

这是一个特定于Windows的解决方案,旨在避免Visual Studio中的编译器警告而不消除它们。所讨论的警告是针对使用std::string和va_start,这会错误地产生警告,以及针对使用已弃用的printf变量。

template<typename ... va>
std::string Format( const std::string& format, va ... args )
{
    std::string s;
    s.resize( _scprintf( format.c_str(), args ... ) + 1 );
    s.resize( _snprintf_s( s.data(), s.capacity(), _TRUNCATE, format.c_str(), args ... ) );
    return s;
}

template<typename ... va>
std::wstring Format( const std::wstring& format, va ... args )
{
    std::wstring s;
    s.resize( _scwprintf( format.c_str(), args ... ) + 1 );
    s.resize( _snwprintf_s( s.data(), s.capacity(), _TRUNCATE, format.c_str(), args ... ) );
    return s;
}

std::string s = Format( "%hs %d", "abc", 123 );
std::wstring ws = Format( L"%hs %d", "abc", 123 );

为了以'sprintf'方式格式化std::string,调用snprintf(参数nullptr和0)来获得所需的缓冲区长度。使用c++ 11可变模板编写函数,如下所示:

#include <cstdio>
#include <string>
#include <cassert>

template< typename... Args >
std::string string_sprintf( const char* format, Args... args ) {
  int length = std::snprintf( nullptr, 0, format, args... );
  assert( length >= 0 );

  char* buf = new char[length + 1];
  std::snprintf( buf, length + 1, format, args... );

  std::string str( buf );
  delete[] buf;
  return str;
}

使用c++11支持编译,例如在GCC: g++ -std=c++11中编译

用法:

  std::cout << string_sprintf("%g, %g\n", 1.23, 0.001);

以下是我的(简单的解决方案):

std::string Format(const char* lpszFormat, ...)
{
    // Warning : "vsnprintf" crashes with an access violation
    // exception if lpszFormat is not a "const char*" (for example, const string&)

    size_t  nSize     = 1024;
    char    *lpBuffer = (char*)malloc(nSize);

    va_list lpParams;

    while (true)
    {
        va_start(lpParams, lpszFormat);

        int nResult = vsnprintf(
            lpBuffer,
            nSize,
            lpszFormat,
            lpParams
        );

        va_end(lpParams);

        if ((nResult >= 0) && (nResult < (int)nSize) )
        {
            // Success

            lpBuffer[nResult] = '\0';
            std::string sResult(lpBuffer);

            free (lpBuffer);

            return sResult;
        }
        else
        {
            // Increase buffer

            nSize =
                  (nResult < 0)
                ? nSize *= 2
                : (nResult + 1)
            ;

            lpBuffer = (char *)realloc(lpBuffer, nSize);
        }
    }
}

你可以试试这个:

string str;
str.resize( _MAX_PATH );

sprintf( &str[0], "%s %s", "hello", "world" );
// optionals
// sprintf_s( &str[0], str.length(), "%s %s", "hello", "world" ); // Microsoft
// #include <stdio.h>
// snprintf( &str[0], str.length(), "%s %s", "hello", "world" ); // c++11

str.resize( strlen( str.data() ) + 1 );

我现在将为Visual Studio编写版本,希望有一天有人会让它变得可移植。(怀疑需要用vsnwprintf替换_vsnwprintf之类的东西。)

您需要使用项目配置中的define _CRT_SECURE_NO_WARNINGS来禁用已弃用的警告。

我使用_vsnwprintf与第一个参数作为nullptr能够估计缓冲区大小,保留wstring缓冲区,然后格式化字符串直接到缓冲区。

不确定为什么需要禁用已弃用警告,因为相同方法调用(_vsnwprintf_s)的安全版本不能使用nullptr作为输入。怀疑需要报告给微软c++团队。

这个版本应该同时使用- string类或wstring类。

如果你发现任何错误或不一致,请再问一次,我会尽力修复它。

stringHelpers.h:

#pragma once
#include <string>

//
//  Formats string/wstring according to format, if formatting fails (e.g. invalid %s pointer - returns empty string)
//
template <typename T>
std::basic_string<T> sFormat(const T* format, ...)
{
    va_list args;
    va_start(args, format);
    int size;

    if constexpr (std::is_same_v<T, char>)
        size = vsnprintf(nullptr, 0, format, args);
    else
        size = _vsnwprintf(nullptr, 0, format, args);

    size++; // Zero termination
    std::basic_string<T> s;
    s.resize(size);

    if constexpr (std::is_same_v<T, char>)
        vsnprintf(&s[0], size, format, args);
    else
        _vsnwprintf(&s[0], size, format, args);

    va_end(args);
    return s;
}

以上是代码示例,可以复制。我将维护工作版本在我自己的仓库在github:

https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/helpers.h#L12