59 votes

Comment vérifier si une variable StringStream est vide/nulle ?

J'ai une petite question à vous poser. J'ai cherché en vain jusqu'à présent.

Un peu plus d'informations ici :

stringstream report_string;

report_string << "some string here...";

Dans mon code, il existe plusieurs conditions pour attribuer des valeurs à la variable report_string.

J'aimerais vérifier si une valeur lui a été attribuée ou non.

0 votes

Eh bien, il y a [potentiellement] deux "choses" différentes demandées - qu'avez-vous essayé ?

0 votes

Définissez vos termes. Un exemple de programme dans lequel vous utiliseriez cette capacité serait d'une grande aide (avec un espace réservé où vous faites la "vérification de la nullité").

0 votes

Pouvez-vous préciser ce que vous entendez par "variable is empty/null" ? Voulez-vous dire que vous voulez savoir si le flux ne contient aucune donnée ?

4voto

Tony D Points 43962

Il est normalement raisonnable et lisible d'utiliser...

report_string.str().empty()

...mais cela peut impliquer une allocation dynamique et la copie de la chaîne entière dans un fichier temporaire, pour être ensuite jetée.

Si les performances sont importantes, une autre option est...

report_string.peek() == decltype(report_string)::traits_type::eof()
  • cela ressemble à un personnage pas encore extrait depuis le flux, en ignorant les entrées qui ont déjà été avec succès analysé/extrait

    • c'est différent des tests report_string.str().empty() qui "voit" toujours les données déjà extraites.
  • si une analyse précédente a laissé le flux dans un fail vous n'avez pas clear() ed, cela donnera eof() indépendamment du fait qu'il y ait plus de caractères non extraits

2voto

Nikolaj Points 174

Je sais que cette question est très ancienne et a déjà reçu une réponse, mais selon la situation, une autre approche peut être envisagée :

Lorsque vous testez si un flux de chaînes est vide, vous avez l'intention de faire quelque chose avec les chaînes individuelles ou avec chaque ligne du flux de chaînes ; ainsi, vous utiliserez probablement l'une ou l'autre des méthodes suivantes >> opérateur ou std::getline sur le flux de chaînes de caractères... et si le flux est vide, ils ont simplement la valeur false, donc vous pourriez écrire :

stringstream report_string;

foo(report_string)// some functions which may or may not write to report_string

string single_report;//string to read to

bool empty=true;//First assume it was empty
while(getline(report_string,single_report))//Alternatively use report_string>>single_report if you don't want entire lines
{
    empty=false;//...it wasn't empty
    bar(single_report);//Do whatever you want to do with each individual appended line 
}

if (empty)
{
    //... whatever you want to do if the stream was empty goes here
}

Il convient de noter que cette approche suppose que vous aviez l'intention de traverser le stringstream à vélo ; si ce n'est pas le cas, cette approche ne peut pas être utilisée.

0voto

Richard Hodges Points 1972

Que diriez-vous d'une autre approche ?

Si vous faites de l'ostringstream un type optionnel, vous pouvez vérifier qu'il a été assigné avant de l'utiliser.

Imaginez une classe appelée lazy<> qui construit paresseusement un objet quand c'est nécessaire, alors nous pourrions faire ceci :

int main()
{
    using namespace std;

    auto oss1 = lazy<std::ostringstream>();
    auto oss2 = lazy<std::ostringstream>();

    use(oss1) << "Hello";

    if (oss1) cout << use(oss1).str() << endl;
    if (oss2) cout << use(oss2).str() << endl;

    if_used(oss1, [](auto& ss) { cout << ss.str() << endl; });
    if_used(oss2,
            [](auto& ss) { cout << ss.str() << endl; },
            [](auto& oss) { cout << "oss2 is not used" << endl; });

    use(oss2) << "Goodbye";
    if_used(oss2, [](auto& ss) { cout << ss.str() << endl; });

    return 0;
}

ce qui donne ce résultat :

Hello
Hello
oss2 is not used
Goodbye

Avantages :

  • pas de construction redondante de la stringstream lorsqu'il n'est pas utilisé.

  • optionnel fournit une exception si le stringstream inutilisé est utilisé par la suite (via une référence const).

Exemple complet ci-dessous avec un constructeur personnalisable :

J'ai utilisé std::experimental pour le optional mais vous pourriez tout aussi bien utiliser boost::optional .

#include <iostream>
#include <experimental/optional>
#include <utility>
#include <type_traits>
#include <sstream>

using std::experimental::optional;

namespace detail {
    template<class T, class Constructor>
    struct lazy final
    {
        template<class Con , std::enable_if_t< not std::is_same<std::decay_t<Con>, lazy>::value > * = nullptr>
        lazy(Con&& con)
        : _constructor(std::forward<Con>(con))
        {}

        T& get() {
            if (not bool(_opt)) {
                _opt = _constructor();
            }
            return *_opt;
        }

        const T& get() const {
            return *_opt;
        }

        bool used() const {
            return bool(_opt);
        }

        operator bool() const {
            return used();
        }

    private:
        Constructor _constructor;
        optional<T> _opt;
    };

    template<class T>
    struct default_construct {
        T operator()() const { return T(); }
    };

    struct no_action {
        template<class T>
        void operator()(T&) const { }
    };
}

template<class T, class Constructor = detail::default_construct<T> >
auto lazy(Constructor&& con = detail::default_construct<T>())
{
    return detail::lazy<T, std::decay_t<Constructor>>(std::forward<Constructor>(con));
}

template<class T, class Constructor>
auto& use(detail::lazy<T, Constructor>& l)
{
    return l.get();
}

template<class T, class Constructor>
auto& use(const detail::lazy<T, Constructor>& l)
{
    return l.get();
}

template<class T, class Constructor, class F, class Else = detail::no_action>
void if_used(detail::lazy<T, Constructor>& l, F&& f, Else&& e = detail::no_action())
{
    if (l.used())
        f(l.get());
    else
        e(l);
}

template<class T, class Constructor, class F, class Else = detail::no_action>
void if_used(const detail::lazy<T, Constructor>& l, F&& f, Else&& e)
{
    if (l.used())
        f(l.get());
    else
        e(l);
}

int main()
{
    using namespace std;

    auto oss1 = lazy<std::ostringstream>();
    auto oss2 = lazy<std::ostringstream>();

    use(oss1) << "Hello";

    if (oss1) cout << use(oss1).str() << endl;
    if (oss2) cout << use(oss2).str() << endl;

    if_used(oss1, [](auto& ss) { cout << ss.str() << endl; });
    if_used(oss2,
            [](auto& ss) { cout << ss.str() << endl; },
            [](auto& oss) { cout << "oss2 is not used" << endl; });

    use(oss2) << "Goodbye";
    if_used(oss2, [](auto& ss) { cout << ss.str() << endl; });

    return 0;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X