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

使用C函数操作std::string

  •  4
  • Patrick  · 技术社区  · 6 年前

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

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

    注意我们首先需要填充本地缓冲区,然后将其复制到 STD::字符串 .

    如果计算最大缓冲区大小,并且不一定要在堆栈上存储某些内容,则示例会变得更复杂。例如:

    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大于堆栈上所需的大小,则基本上可以执行以下操作:

    • 分配内存(使唯一)
    • 用想要的结果填充内存
    • 分配内存(std::string)
    • 将内存复制到字符串
    • 释放内存

    自从C++ 17 STD::字符串 有非常量 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中有一个您想要重用的有效逻辑。

    2 回复  |  直到 6 年前
        1
  •  7
  •   You    6 年前

    修改指向的内容 data() 如果不将值设置为 data() + size() 除空字符以外的任何字符。从 [string.accessors] :

    charT* data() noexcept;

    返回: 指针 p 这样 p + i == addressof(operator[](i)) 对于每一个 i 在里面 [0, size()] .

    复杂性: 固定时间。

    评论: 程序不得修改存储在 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
  •  4
  •   R Sahu    6 年前

    我觉得一般的方法不错。我会做一些改变。

    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;