Question Copier le tableau de caractères à terminaison nulle dans std :: string en respectant la longueur du tampon


Peut-être que c'est juste le manque de café, mais j'essaie de créer un std::string à partir d'un null-terminé char tableau avec une longueur maximale connue et je ne sais pas comment le faire.

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

.. était mon candidat préféré mais puisque les chaînes C ++ ne sont pas terminées par une valeur null, cette commande copiera sizeof(buffer) octets indépendamment de tout «\ 0» contenu.

auto s = std::string(buffer);

.. des copies de buffer jusqu'à \0 est trouvé. C'est presque ce que je veux, mais je ne peux pas faire confiance au tampon de réception, donc je voudrais fournir une longueur maximale.

Bien sûr, je peux maintenant intégrer strnlen() comme ça:

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

Mais cela semble sale - il traverse le tampon deux fois et je dois faire face à des artefacts C comme string.h et strnlen() (et c'est moche).

Comment est-ce que je ferais ceci dans le C ++ moderne?


13
2018-05-04 08:19


origine


Réponses:


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

19
2018-05-04 08:23



Quelque chose comme ça pourrait fonctionner en un seul passage.

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

1
2018-05-04 08:53



Si vous voulez une solution en un seul passage, commencez par ceci:

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

maintenant, gussy it up et en faire un itérateur à accès aléatoire complet. La plupart des opérations sont vraiment simples (++ etc. devrait avancer tous les deux ptr et index), sauf  == et !=.

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

nous avons aussi besoin de:

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

maintenant nous faisons ceci:

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

et à chaque étape, nous vérifions à la fois la longueur du tampon et la terminaison nulle lors de la copie.

Maintenant, Ranges v3 introduit le concept de sentinal, qui vous permet de faire quelque chose comme ci-dessus avec un coût d’exécution réduit. Ou vous pouvez créer manuellement la solution équivalente.


1
2018-05-04 13:55