简体   繁体   中英

Copy null terminated char array to std::string respecting buffer length

Maybe it's just the lack of coffee, but I'm trying to create a std::string from a null-terminated char array with a known maximum length and I don't know, how to do it.

auto s = std::string(buffer, sizeof(buffer));

.. was my favorite candidate but since C++ strings are not null-terminated this command will copy sizeof(buffer) bytes regardless of any contained '\\0'.

auto s = std::string(buffer);

.. copies from buffer until \\0 is found. This is almost what I want but I can't trust the receive buffer so I'd like to provide a maximum length.

Of course, I can now integrate strnlen() like this:

auto s = std::string(buffer, strnlen(buffer, sizeof(buffer)));

But that seems dirty - it traverses the buffer twice and I have to deal with C-artifacts like string.h and strnlen() (and it's ugly).

How would I do this in modern C++?

const char* end = std::find(buffer, buffer + sizeof(buffer), '\0');
std::string s(buffer, end);

Something like this could work in a single pass..

auto eos = false;
std::string s;
std::copy_if(buffer, buffer + sizeof(buffer), std::back_inserter(s),
  [&eos](auto v) {
    if (!eos) {
      if (v) {
        return true;
      }
      eos = true;
    }
    return false;
  });

If you want a single-pass solution, start with this:

template<class CharT>
struct smart_c_string_iterator {
  using self=smart_c_string_iterator;
  std::size_t index = 0;
  bool is_end = true;
  CharT* ptr = nullptr;
  smart_c_string_iterator(CharT* pin):is_end(!pin || !*pin), ptr(pin) {}
  smart_c_string_iterator(std::size_t end):index(end) {}
};

now, gussy it up and make it a full on random-access iterator. Most of the operations are really simple ( ++ etc should advance both ptr and index ), except == and != .

friend bool operator==(self lhs, self rhs) {
  if (lhs.is_end&&rhs.is_end) return true;
  if (lhs.index==rhs.index) return true;
  if (lhs.ptr==rhs.ptr) return true;
  if (lhs.is_end && rhs.ptr && !*rhs.ptr) return true;
  if (rhs.is_end && lhs.ptr && !*lhs.ptr) return true;
  return false;
}
friend bool operator!=(self lhs, self rhs) {
  return !(lhs==rhs);
}

we also need:

template<class CharT>
std::pair<smart_c_string_iterator,smart_c_string_iterator>
smart_range( CharT* ptr, std::size_t max_length ) {
  return {ptr, max_length};
}

now we do this:

auto r = smart_range(buffer, sizeof(buffer));
auto s = std::string(r.first, r.second);

and at each step we check for both buffer length and null termination when doing the copy.

Now, Ranges v3 brings about the concept of a sentinal, which lets you do something like the above with reduced runtime cost. Or you can hand-craft the equivalent solution.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM