使用 C 函数来操作 std::string

Using C functions to manipulate std::string

有时您需要用 C 函数构造的字符填充 std::string。一个典型的例子是这样的:

constexpr static BUFFERSIZE{256};
char buffer[BUFFERSIZE];
snprint (buffer, BUFFERSIZE, formatstring, value1, value2);
return std::string(buffer);

注意我们首先需要填充本地缓冲区,然后将其复制到 std::string

如果计算了最大缓冲区大小并且不一定要存储在堆栈中的内容,则该示例将变得更加复杂。例如:

constexpr static BUFFERSIZE{256};
if (calculatedBufferSize>BUFFERSIZE)
   {
   auto ptr = std::make_unique<char[]>(calculatedBufferSize);
   snprint (ptr.get(), calculatedBufferSize, formatstring, value1, value2);
   return std::string(ptr.get());
   }
else
   {
   char buffer[BUFFERSIZE];
   snprint (buffer, BUFFERSIZE, formatstring, value1, value2);
   return std::string(buffer);
   }

这使得代码更加复杂,如果计算出的 BufferSize 大于我们在堆栈上想要的大小,我们基本上会执行以下操作:

因为 C++17 std::string 有一个非常量 data() 方法,暗示这是操作字符串的方式。所以这样做似乎很诱人:

std::string result;
result.resize(calculatedBufferSize);
snprint (result.data(), calculatedBufferSize, formatstring, value1, value2);
result.resize(strlen(result.c_str()));
return result;

我的实验表明,需要最后一次调整大小才能确保正确报告字符串的长度。 std::string::length() 不搜索空终止符,它只是 returns 大小(就像 std::vector 一样)。

请注意,我们进行的分配和复制要少得多:

说实话,虽然看起来效率高了很多,但在我看来也很'un-standard'。有人可以指出这是否是 C++17 标准允许的行为吗?还是有另一种方法可以更有效地进行这种操作?

请不要参考问题 ,因为该问题是关于更肮脏的逻辑(甚至使用 memset)。 也不要回答我必须使用 C++ 流(std::string_stream,高效?老实说?)。有时您只是想重用 C 中的高效逻辑。

修改data() 指向的内容是可以的,前提是您没有将data() + size() 处的值设置为空字符以外的任何内容。来自 [string.accessors]:

charT* data() noexcept;

Returns: A pointer p such that p + i == addressof(operator[](i)) for each i in [0, size()].

Complexity: Constant time.

Remarks: The program shall not modify the value stored at p + size() to any value other than charT(); otherwise, the behavior is undefined.


不过,result.resize(strlen(result.c_str())); 语句看起来确实有点奇怪。 std::snprintf returns写入的字符数;使用该值来调整字符串的大小会更合适。此外,构建具有正确大小的字符串而不是构建一个立即调整大小的空字符串看起来更整洁:

std::string result(maxlen, '[=10=]');
result.resize(std::max(0, std::snprintf(result.data(), maxlen, fmt, value1, value2)));
return result;

一般方法对我来说很好。我会做一些改变。

  1. 捕获 snprinf 的 return 值。
  2. 使用它来执行错误检查并避免调用 strlen

std::string result;
result.resize(calculatedBufferSize);
int n = snprint (result.data(), calculatedBufferSize, formatstring, value1, value2);

if ( n < 0 )
{
   // Problem. Deal with the error.
}

result.resize(n);
return result;