How to construct a `std::string` from a buffer that may or may not contain a null?

968 Views Asked by At

I have a char buffer of known size (for simplicity, let's say a fixed-sized array) from which I would like to construct a std::string. The string in this buffer may be null-terminated or it may run up to and include the very last char. I wish to create a std::string containing the contents up to the first null byte or the end of the buffer, whichever comes first.

This seems like a common enough thing to want to do, but the solution is not immediately obvious to me while looking at the std::string API.

  • std::string has a constructor taking a range via const char * and a length, but this constructor happily continues past null bytes and copies them into the string.
  • Calling std::strlen() on the buffer before constructing the string is not an option as strlen requires the buffer to be null-terminated, which may not be the case.
  • We could use the above constructor to make a std::string containing nulls and then resize it down to just before the first null, but that wastes memory as the string will be over-allocated.

What is the best and/or idiomatic way to do this?

2

There are 2 best solutions below

4
Parker Coates On

The answer, as with so many questions regarding the C++ standard library, is to think about the problem in terms of iterators.

std::string stringFromBuffer(const auto & buffer)
{
    return std::string(std::begin(buffer),
                       std::find(std::begin(buffer), std::end(buffer), '\0'));
}

std::string has a constructor taking two iterators, first and last. The string will be created by copying from first up until but not including last.

So first should obviously be the beginning of our buffer, while last should either be the first null in our buffer or one past the end of the buffer. Conveniently, that is exactly what a call to std::find searching the buffer for '\0' will return.

0
doug On

Here is a simple way for an array of chars of known size. It's similar to Parker's answer but is just done in the constructor argument and is specific to the question of an array of chars.

#include <algorithm> // std::find
#include <iostream>
#include <string>

int main() {
  const char c[] = {'a', 'b', 'c', 'd', 'e'}; // prints abcde
  // const char c[] = { 'a','b','\0','d','\0' };    // test alternate: prints ab
  // const char c[] = { 'a','b','\0','d','e' };     // test alternate: prints ab
  std::string s{c, std::find(c, c + sizeof c, '\0')};
  std::cout << s << '\n';
  return 0;
}