Forum: Mikrocontroller und Digitale Elektronik C++ Best Practice für Schleife mit Abbruch in der Mitte


von Peter Sahl (Gast)


Lesenswert?

Hallo,

im folgenden Code habe ich in f() und g() jeweils eine Variante einer 
Schleife mit Abbruchbedingung in der Mitte. Mit getc() lies sich dies 
vermeiden, mit getline() sehe ich mich dazu gezwungen. Das Beispiel 
liest Zeilen aus einem Stream bis eine Leerzeile kommt.
1
#include <iostream>
2
#include <sstream>
3
#include <string>
4
#include <vector>
5
using namespace std;
6
vector <string> f (istream& is) {
7
        string s;
8
        vector <string> v;
9
        while (1) {
10
                getline (is, s);
11
                if ("" == s) {
12
                        break;
13
                }
14
                v.push_back (s);
15
        }
16
        return v;
17
}
18
vector <string> g (istream& is) {
19
        vector <string> v;
20
        for (string s;
21
                        getline (is, s),
22
                        "" != s;
23
            ) {
24
                v.push_back (s);
25
        }
26
        return v;
27
}
28
int main (int argc, char** argv) {
29
        string s = "Line 1\nLine 2\nLine 3\n\nLine 5";
30
        istringstream iss1 (s);
31
        for (auto a: f (iss1)) {
32
                cout << a << endl;
33
        }
34
        cout << "---" << endl;
35
        istringstream iss2 (s);
36
        for (auto a: g (iss2)) {
37
                cout << a << endl;
38
        }
39
        return 0;
40
}
1
% ./a.out
2
Line 1
3
Line 2
4
Line 3
5
---
6
Line 1
7
Line 2
8
Line 3

Derzeit liebäugle ich mit der Variante in g(), da ich while(1) und 
for(;;) optisch nicht so schön finde.

Wie macht ihr sowas?

von Oliver S. (oliverso)


Lesenswert?

Ich würde die Variante nehmen, in der du einfacher auf eof prüfen 
kannst.

Oliver

von Thomas K. (muetze1)


Lesenswert?

Ich würde auch keinen Stringvergleich machen sondern die Methode empty() 
bemühen. So wird temporär ein std::string(""), der Vergleich 
durchgeführt und dann wieder verworfen.

von Peter Sahl (Gast)


Lesenswert?

Oliver S. schrieb:
> einfacher auf eof prüfen

Das sollte doch eigentlich getline() machen. Trotzdem stürtzt mir der PC 
ab, wenn ich den String nicht vorher selber lösche. Was eigentlich auch 
getline() machen sollte!?

Thomas K. schrieb:
> keinen Stringvergleich machen sondern die Methode empty()
> bemühen

Danke, das gefällt mir gut.

Dann sehen die Schleifen folgendermaßen aus, wobei mir immernoch g() 
besser gefällt. Kennt ihr noch mehr Möglichkeiten solche Schleifen in 
C++ auszudrücken. Oder auch zu Formatieren. In Ada gibts das ja built 
in.

f()
1
while (1) {
2
        s.clear ();
3
        getline (is, s);
4
        if (s.empty ()) {
5
                break;
6
        }
7
        v.push_back (s);
8
}

g()
1
for (   string s;
2
3
        s.clear (),
4
        getline (is, s),
5
        !s.empty ();
6
    ) {
7
        v.push_back (s);
8
}

Input
1
string s = "Line 1\nLine 2";

Output
1
% ./a.out
2
Line 1
3
Line 2
4
---
5
Line 1
6
Line 2

von Wilhelm M. (wimalopaan)


Lesenswert?

1
using namespace std;
2
vector<string> f(istream& is) {
3
        vector <string> v;
4
        for(string s; getline(is, s) && !s.empty();) {
5
            v.push_back(s);
6
        }
7
        return v;
8
}

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Warum packst Du das v.push_back(s) nicht auch noch hinter das letzte 
Semikolon der for-Zeile?

von Dirk K. (merciless)


Lesenswert?

Frank M. schrieb:
> Warum packst Du das v.push_back(s) nicht auch noch hinter das letzte
> Semikolon der for-Zeile?

Wozu sollte das gut sein? Der Lesbarkeit dient es nicht...

merciless

von Peter Sahl (Gast)


Lesenswert?

Wilhelm M. schrieb:
> &&

Sehr schön. Jetzt prüft es auch explizit auf EOF.

Dirk K. schrieb:
> Frank M. schrieb:
>> Warum packst Du das v.push_back(s) nicht auch noch hinter das letzte
>> Semikolon der for-Zeile?
>
> Wozu sollte das gut sein? Der Lesbarkeit dient es nicht...

Doch ich finde schon. Weil es die geschweiften Klammern spart.
1
for (
2
        string s;
3
4
        s.clear (),
5
6
        getline (is, s) &&
7
        !s.empty ();
8
9
        v.push_back (s)
10
    );

von Wilhelm M. (wimalopaan)


Lesenswert?

Wobei ich das eher generisch machen würde:
1
template<typename T, typename P>
2
auto read_if(std::istream& is, P predicate) {
3
    std::vector<T> v;
4
    for(T item{}; getline(is, item) && predicate(item);) {
5
        v.push_back(item);
6
    } 
7
    return v;
8
}

und dann:
1
int main () {
2
    std::string s{"Line 1\nLine 2\nLine 3\n\nLine 5"};
3
    std::istringstream iss1{s};
4
    for (const auto& a: read_if<std::string>(iss1, [](const auto& s){return !s.empty();})) {
5
        cout << a << endl;
6
    }
7
}

von leo (Gast)


Lesenswert?

Wilhelm M. schrieb:
> for (const auto& a: read_if<std::string>(iss1, [](const auto&
> s){return !s.empty();})) {

Ja, schaut sehr uebersichtlich und lesbar aus ;-) Der W. schafft es doch 
immer den Obfuscation-Wettbewert zu gewinnen.

SCNR, leo

von Wilhelm M. (wimalopaan)


Lesenswert?

leo schrieb:
> Ja, schaut sehr uebersichtlich und lesbar aus ;-) Der W. schafft es doch
> immer den Obfuscation-Wettbewert zu gewinnen.

Das hängt immer zu 50% vom Leser ab ;-)

von leo (Gast)


Lesenswert?

Wilhelm M. schrieb:
> leo schrieb:
>> Ja, schaut sehr uebersichtlich und lesbar aus ;-) Der W. schafft es doch
>> immer den Obfuscation-Wettbewert zu gewinnen.
>
> Das hängt immer zu 50% vom Leser ab ;-)

Yep. Meine Erfahrung sagt mir a) die weitaus meiste Zeit verbringe ich 
mit Lesen von Code und b) kuerzerer Code ist lesbarer und verbirgt 
weniger Fehler.

leo

von Peter Sahl (Gast)


Lesenswert?

Wilhelm M. schrieb:
> eher generisch machen

Wie hältst du es mit den ganzen Strings?

Wenn ich etwas generisch machen will dann kommt meist etwas heraus was 
dem folgenden strukturell recht ähnlich sieht. Und ich erwische mich 
dabei, wie ich schon längst aufgehöhrt habe an meinem Programm zu 
arbeiten, sondern eigentlich am Compiler bastel.

Dann nehm ich meist einfach wieder nur char und string.
1
template <typename P
2
        , typename CharT
3
        , typename Traits = char_traits <CharT>
4
        , typename Allocator = allocator <CharT>
5
        , typename S = basic_string <CharT, Traits, Allocator>
6
        , typename V = vector <S>>
7
V h (basic_istream <CharT, Traits>& is, P predicate) {
8
        V v;
9
        for (
10
                   S s
11
                ;  s.clear ()
12
                ,  getline (is, s)
13
                && predicate (s)
14
                ;  v.push_back (s)
15
        );
16
        return v;
17
}

von Dirk K. (merciless)


Lesenswert?

Peter Sahl schrieb:
> Dirk K. schrieb:
>> Wozu sollte das gut sein? Der Lesbarkeit dient es nicht...
>
> Doch ich finde schon. Weil es die geschweiften Klammern spart.
Lol, aber Schleifenköpfe auf mehrere Zeilen verteilen,
ganz großes Kino. Ich empfehle Lektüre zum
Thema "Clean Code" (https://clean-code-developer.de/).

merciless

von Wilhelm M. (wimalopaan)


Lesenswert?

Peter Sahl schrieb:
> Wenn ich etwas generisch machen will dann kommt meist etwas heraus was
> dem folgenden strukturell recht ähnlich sieht. Und ich erwische mich
> dabei, wie ich schon längst aufgehöhrt habe an meinem Programm zu
> arbeiten, sondern eigentlich am Compiler bastel.

Ist doch vollkommen unnötig.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.