【问题标题】:Using C functions to manipulate std::string使用 C 函数来操作 std::string
【发布时间】:2019-06-04 00:06:12
【问题描述】:

有时您需要用 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比我们想要的栈上的大,我们基本上会做以下事情:

  • 分配内存 (make_unique)
  • 用想要的结果填满内存
  • 分配内存(std::string)
  • 复制内存到字符串
  • 释放内存

由于 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() 不搜索 nul 终止符,它只返回大小(就像 std::vector 一样)。

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

  • 分配内存(调整字符串大小)
  • 用想要的结果填满内存

说实话,虽然它看起来效率更高,但在我看来它也很“不标准”。有人可以指出这是否是 C++17 标准允许的行为吗?还是有其他方法可以更有效地进行这种操作?

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

【问题讨论】:

  • 到底为什么:o
  • 那么为什么你的calculatedBufferSize 不等于strlen(result)
  • 如果您不想让代码看起来“不标准”,那么不要混合使用 c 和 c++。要么使用 c++ 函数进行字符串操作,要么继续使用 C 但不要转换回 std::string 并使用 char* 继续你的代码。 (考虑到您想知道以空字符结尾的字符串长度而不是实际数据长度,这似乎更有意义)
  • 使用 asprintf 分配缓冲区,然后用它初始化 std::string。或者调用 sprintf 两次。
  • @Patrick 提供的示例可以而且应该用更安全的东西代替。 C 格式的字符串容易出错,而且通常不容易猜出 cstdint 类型的正确占位符类型(如果您不了解宏)。我看了看效率,提升很慢。但是还有其他的,比如 FastFormat 左右。

标签: c++ c++17 stdstring


【解决方案1】:

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

charT* data() noexcept;

返回: 一个指针p 使得p + i == addressof(operator[](i)) 对应[0, size()] 中的每个i

复杂性:恒定时间。

备注: 程序不得将p + size() 中存储的值修改为charT() 以外的任何值;否则,行为未定义。


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

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

【讨论】:

    【解决方案2】:

    一般方法对我来说看起来不错。我会做一些改变。

    1. 捕获snprinf的返回值。
    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;
    

    【讨论】:

      猜你喜欢
      • 2018-03-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-12
      • 2019-08-07
      • 1970-01-01
      相关资源
      最近更新 更多