Подтвердить что ты не робот

Скопируйте нулевой конец массива char в std::string с учетом длины буфера

Возможно, это просто нехватка кофе, но я пытаюсь создать std::string из массива char с нулевым завершением с известной максимальной длиной, и я не знаю, как это сделать.

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

.. был моим любимым кандидатом, но так как строки С++ не имеют нулевого окончания, эта команда будет копировать sizeof(buffer) байты независимо от содержащихся в нем "\ 0".

auto s = std::string(buffer);

.. копии с buffer пока не будет найдено \0. Это почти то, что я хочу, но я не могу доверять буфере приема, поэтому я хотел бы предоставить максимальную длину.

Конечно, теперь я могу интегрировать strnlen() следующим образом:

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

Но это кажется грязным - он дважды пересекает буфер, и мне приходится иметь дело с C-артефактами, такими как string.h и strnlen() (и это уродливо).

Как я могу сделать это в современном С++?

4b9b3361

Ответ 1

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

Ответ 2

Что-то вроде этого может работать за один проход.

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;
  });

Ответ 3

Если вы хотите однопроходное решение, начните с этого:

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) {}
};

теперь, gussy it up и сделайте его полным на итераторе с произвольным доступом. Большинство операций действительно просты (++ и т.д. Должны продвигать как ptr, так и index), кроме == и !=.

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);
}

нам также нужно:

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};
}

теперь мы делаем это:

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

и на каждом шаге мы проверяем как длину буфера, так и нулевое завершение при выполнении копии.

Теперь, диапазоны v3 вызывают концепцию отцу, который позволяет вам сделать что-то вроде выше, с уменьшенной стоимостью времени исполнения. Или вы можете использовать эквивалентное решение.