代码之家  ›  专栏  ›  技术社区  ›  dreamlax

std::string.resize()和std::string.length()

  •  2
  • dreamlax  · 技术社区  · 14 年前

    std::string 使用printf样式的格式化程序。我意识到 stringstream 是一种类型更安全的方法,但我发现printf样式更易于阅读和处理(至少目前是这样)。这是我的职责:

    
    using namespace std;
    
    string formatStdString(const string &format, ...)
    {
        va_list va;
        string output;
        size_t needed;
        size_t used;
    
        va_start(va, format);
        needed = vsnprintf(&output[0], 0, format.c_str(), va);
        output.resize(needed + 1); // for null terminator??
        va_end(va);    
    
        va_start(va, format);
        used = vsnprintf(&output[0], output.capacity(), format.c_str(), va);
        // assert(used == needed);
        va_end(va);
    
        return output;
    }

    这有点用。我不确定的是:

    1. 我是否需要为空终止符腾出空间,或者这是不必要的?
    2. capacity() 在这里调用正确的函数?我一直在想 length() 将返回0,因为字符串中的第一个字符是 '\0' .

    偶尔在将此字符串的内容写入套接字时(使用 c_str() 长度()

    7 回复  |  直到 14 年前
        1
  •  13
  •   David Rodríguez - dribeas    14 年前

    对于当前的标准(这里upcomming标准不同),不能保证 std::string .c_str() 方法返回指向内部数据表示形式的指针(允许实现为该操作生成连续的只读块,并向其中返回指针)。指向实际内部数据的指针可以通过 .data() .data() 它不一定是空终止的,实现时只需要保证空终止 c_str() 所以即使在 .data() .c\u str() 被调用时,实现可以添加 \0 在调用缓冲区时返回到缓冲区的末尾。

    std::vector &myvector[0] 是指向实际缓冲区的第一个分配块的指针)。

    在我所知道的所有实现中,由 标准::字符串 实际上是一个连续的缓冲区 .data() 是未定义的行为(写入常量变量),但即使不正确,它也可能工作(我会避免它)。您应该使用其他为此目的而设计的库,例如 boost::format

    关于空终止。如果你最终决定走未定义的道路。。。您需要为空终止符分配额外的空间,因为库会将其写入缓冲区。现在的问题是,与C风格的字符串不同, s可以在内部保留空指针,因此必须调整字符串的大小,以适应从一开始就不包含空指针的最大连续内存块 \0 . 这可能就是您发现的伪空字符的问题。这意味着使用 vsnprintf (或家庭)后面必须有 str.resize( strlen( str.c_str() ) ) 在第一个 .

    总的来说,我会建议反对这种方法,并坚持要么使用C++的格式化方式,使用第三方库(Boost是第三方,但它也是最标准的非标准库),使用向量或管理内存,如在C.。但最后一个选择应该像瘟疫一样避免。

    // A safe way in C++ of using vsnprintf:
    std::vector<char> tmp( 1000 ); // expected maximum size
    vsnprintf( &tmp[0], tmp.size(), "Hi %s", name.c_str() ); // assuming name to be a string
    std::string salute( &tmp[0] );
    
        2
  •  5
  •   sbi    14 年前

    boost::format ,如果你愿意的话 printf()

    编辑: 为了说明这一点,实际上我完全同意艾伦的观点,他说你应该使用流。

        3
  •  2
  •   rjnilsson    14 年前

    我认为不能保证;输出[0]是连续的,您可以对其进行写入。

    using namespace std;
    
    string formatStdString(const string &format, ...)
    {
        va_list va;
        vector<string::value_type> output(1); // ensure some storage is allocated
        size_t needed;
        size_t used;
    
        va_start(va, format);
        needed = vsnprintf(&output[0], 0, format.c_str(), va);
        output.resize(needed); // don't need null terminator
        va_end(va);    
    
        // Here we should ensure that needed != 0
        va_start(va, format);
        used = vsnprintf(&output[0], output.size(), format.c_str(), va); // use size()
        // assert(used == needed);
        va_end(va);
    
        return string(output.begin(), output.end());
    }
    

    注意:必须将向量的初始大小设置为语句&否则,输出[0]可以尝试引用不存在的项(因为内部缓冲区可能尚未分配)。

        4
  •  0
  •   Richard    14 年前

    1) 不需要为空终止符留出空间。
    2) capacity()告诉您字符串在内部保留了多少空间。length()告诉您字符串的长度。您可能不需要容量()

        5
  •  0
  •   Alan    14 年前

    string类为您处理空终止符。

    但是,正如所指出的,由于您正在使用vnsprintf来处理原始的字符串缓冲区(C错误很难解决……),因此您必须确保有空终止符的空间。

        6
  •  0
  •   Brent81    14 年前

    我对函数的变量参数列表的实现如下:

    std::string format(const char *fmt, ...)
    {
      using std::string;
      using std::vector;
    
      string retStr("");
    
      if (NULL != fmt)
      {
         va_list marker = NULL;
    
         // initialize variable arguments
         va_start(marker, fmt);
    
         // Get formatted string length adding one for NULL
         size_t len = _vscprintf(fmt, marker) + 1;
    
         // Create a char vector to hold the formatted string.
         vector<char> buffer(len, '\0');
         int nWritten = _vsnprintf_s(&buffer[0], buffer.size(), len, fmt,
    marker);
    
         if (nWritten > 0)
         {
            retStr = &buffer[0];
         }
    
         // Reset variable arguments
         va_end(marker);
      }
    
      return retStr;
    }
    
        7
  •  0
  •   utnapistim    14 年前

    printf样式格式化程序。

    如果你这样做,你实际上不是在学习C++,而是用C++编译器编码C。这是一种不好的心态,不好的做法,它传播了 std::o*stream 创建类是为了避免。

    我意识到stringstream是一个 更容易找到printf样式 阅读并处理(至少,对于 暂时)。

    它不是一个

    除此之外,它是完全可扩展/可定制的:

    • 可以扩展区域设置格式

    • 您可以为自定义数据类型定义i/o操作

    • 可以添加新类型的输出格式

    • 您可以添加新的缓冲区i/o类型(例如,将std::clog写入窗口)

    • 您可以插入不同的错误处理策略。

    标准::o*流

    除非你有非常明确的要求,否则你的时间可能比在C++中编写PrimTf要好得多。