首頁>Program>source

我必须格式化 std::string 使用 sprintf 並將其發送到檔案流中.我怎麼能這樣做?

最新回復
  • 2019-8-20
    1 #

    你不能直接這樣做,因為你没有對底層緩衝區的寫訪問權限(直到C ++ 11;參见Dietrich Epp的評論).您必须先在c-string中執行此操作,然後將其複製到std :: string:

     char buff[100];
      snprintf(buff, sizeof(buff), "%s", "Hello");
      std::string buffAsStdStr = buff;
    

    但我不確定為什麼你不会只使用字元串流? 我假設你有特定的理由不要這樣做:

     std::ostringstream stringStream;
      stringStream << "Hello";
      std::string copyOfStr = stringStream.str();
    

  • 2019-8-20
    2 #

    使用 vsnprintf()的C ++ 11解決方案   內部:

    #include <stdarg.h>  // For va_start, etc.
    std::string string_format(const std::string fmt, ...) {
        int size = ((int)fmt.size()) * 2 + 50;   // Use a rubric appropriate for your code
        std::string str;
        va_list ap;
        while (1) {     // Maximum two passes on a POSIX system...
            str.resize(size);
            va_start(ap, fmt);
            int n = vsnprintf((char *)str.data(), size, fmt.c_str(), ap);
            va_end(ap);
            if (n > -1 && n < size) {  // Everything worked
                str.resize(n);
                return str;
            }
            if (n > -1)  // Needed size returned
                size = n + 1;   // For null char
            else
                size *= 2;      // Guess at a larger size (OS specific)
        }
        return str;
    }
    

    更安全,更高效(我測試過,而且速度更快)方法:

    #include <stdarg.h>  // For va_start, etc.
    #include <memory>    // For std::unique_ptr
    std::string string_format(const std::string fmt_str, ...) {
        int final_n, n = ((int)fmt_str.size()) * 2; /* Reserve two times as much as the length of the fmt_str */
        std::unique_ptr<char[]> formatted;
        va_list ap;
        while(1) {
            formatted.reset(new char[n]); /* Wrap the plain char array into the unique_ptr */
            strcpy(&formatted[0], fmt_str.c_str());
            va_start(ap, fmt_str);
            final_n = vsnprintf(&formatted[0], n, fmt_str.c_str(), ap);
            va_end(ap);
            if (final_n < 0 || final_n >= n)
                n += abs(final_n - n + 1);
            else
                break;
        }
        return std::string(formatted.get());
    }
    

    fmt_str   按值傳遞以符合 va_start的要求

    註意:"更安全"和"更快"的版本在某些系統上不起作用.因此两者仍然列出.此外,"更快"完全取決於預分配步骤是否正確,否則 strcpy   使它變慢。

  • 2019-8-20
    3 #

    使用C ++ 11 std::snprintf ,這變得非常簡單和安全.我看到很多關於這个問题的答案顯然是在C ++ 11之前編寫的,它使用固定緩衝區长度和vargs,我不建議出於安全性,效率和清晰度的原因。

    #include <memory>
    #include <iostream>
    #include <string>
    #include <cstdio>
    template<typename ... Args>
    std::string string_format( const std::string& format, Args ... args )
    {
        size_t size = snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0'
        std::unique_ptr<char[]> buf( new char[ size ] ); 
        snprintf( buf.get(), size, format.c_str(), args ... );
        return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside
    }
    

    上面的代碼段在CC0 1.0下获得许可。

    逐行說明:

    Aim:   寫信给 char*   通過使用 std::snprintf   然後將其轉換為 std::string

    首先,我们確定char陣列的所需长度。

    来自cppreference.com:

    Return value

         

    [...]如果由於buf_size限製而匯致結果字元串被截斷,    函式返迴总字元數(不包括    如果限製是,則会寫入已寫入的空位元組    没有強加。

    這意味着所需的大小是 plus one的字元數 ,以便null终止符將位於所有其他字元之後,並且可以再次被字元串構造函式切斷. @ alexk7在評論中解釋了這个問题。

    然後,我们分配一个新的字元陣列並將其分配给 std::unique_ptr .通常建議這樣做,因為您不必手動 delete   再一次。

    請註意,這不是分配 unique_ptr的安全方法   使用使用者定義的型別,因為如果構造函式丟擲異常,則無法釋放記憶體!

    之後,我们当然可以使用 snprintf   為了它的預期用途,並將格式化的字元串寫入 char[]   然後建立並返迴一个新的 std::string   从那起。


    您可以在此處查看示例。


    如果你還想使用 std::string   在引數列表中,看看這个要點.


    Visual Studio使用者的其他資訊:

    正如本迴答中所解釋的那樣,微软更名為 std::snprintf   到 _snprintf   (是的,没有 std:: ). MS进一步將其設置為已棄用並建議使用 _snprintf_s   相反,無論如何 _snprintf_s   不会接受緩衝區為零或小於格式化輸出,如果發生這種情况,則不会計算輸出长度。 因此,為了在編譯期間删除棄用警告,您可以在檔案頂部插入以下行,其中包含使用 _snprintf

    #pragma warning(disable : 4996)
    

  • 2019-8-20
    4 #

    boost::format()   提供您想要的功能:

    从Boost格式庫概要:

    A format object is constructed from a format-string, and is then given arguments through repeated calls to operator%. Each of those arguments are then converted to strings, who are in turn combined into one string, according to the format-string.

    #include <boost/format.hpp>
    cout << boost::format("writing %1%,  x=%2% : %3%-th try") % "toto" % 40.23 % 50; 
    // prints "writing toto,  x=40.230 : 50-th try"
    

  • 2019-8-20
    5 #

    不幸的是,這裏的大部分答案都使用varargs,除非你使用像GCC的 format這樣的东西,否則這些varargs本質上是不安全的   屬性仅適用於文字格式字元串.您可以在以下示例中看到為什麼這些函式不安全:

    std::string format_str = "%s";
    string_format(format_str, format_str[0]);
    

    哪裏有 string_format   是Erik Aronesty的迴答.此代碼編譯,但当您尝試執行它時很可能会崩潰:

    $ g++ -Wall -Wextra -pedantic test.cc 
    $ ./a.out 
    Segmentation fault: 11
    

    可以實施安全的 printf   並將其擴充套件為格式 std::string   使用(可變引數)模板.這已经在{fmt}庫中完成,它為 sprintf提供了一个安全的替代方案   迴到 std::string

    #include <fmt/printf.h>
    std::string format_str = "The answer is %d";
    std::string result = fmt::sprintf(format_str, 42);
    

    {fmt}跟蹤引數型別,如果型別与格式規範不匹配,則没有分段錯誤,只有異常或編譯時錯誤,如果 constexpr   使用格式字元串檢查。

    Disclaimer :我是{fmt}的作者。

  • javascript:如何設置每頁的文件標题?
  • jquery:將匯航欄品牌和鏈接与Bootstrap 4中的頁面內容對齐