# Wörter in einem String zählen ohne split()



## maidenn (4. Jun 2022)

Hallo zusammen,

ich soll in einer Aufgabe die Anzahl der Wörter in einem String zählen und habe hierfür hauptsächlich die String Methoden zur Verfügung. Theoretisch sind auch noch Wrapperklassen und StringBuilder verfügbar, aber ich glaube die bringen bei der Aufgabe keinen Vorteil.

Meine Idee war mich an den Leerzeichen zu orientieren, doch irgendwie liegt da glaube ich das Problem. Ich bekomme als Ausgabe immer 1, also wird die Schleife 1 mal durchlaufen und direkt wieder verlassen.

Ich würde mich sehr freuen, wenn mir jemand erklären kann wieso oder eine alternative hat, wie gesagt die split() bzw. die herkömmliche Art und Weise ist hier nicht gefragt.


```
import java.util.Scanner;

public class KA_wordCount {

    public static int wordCountKA(String satz) {
        int fromIndex = 0;
        int index = 0;
        int summe = 0;

        while (index != -1) {
            index = satz.indexOf(" ", fromIndex);
            fromIndex = index;
            summe += 1;
        }
        return summe;
    }

    public static void main(String[] args) {

        Scanner eingabe = new Scanner(System.in);

        System.out.println("Satz eingeben: ");
        String satzE = eingabe.next();
        System.out.println("Der Satz hat " + wordCountKA(satzE) + " Wort/Woerter.");
    }

}
```


----------



## httpdigest (4. Jun 2022)

Also bei mir ist die Schleife eine Endlosschleife, sobald in der Eingabe mindestens ein Leerzeichen drin ist, weil du immer wieder denselben Index berechnest. Es kommt also niemals 1 raus, sondern ein endlos arbeitendes Programm.
Du musst schreiben: `satz.indexOf(" ", fromIndex + 1)`


----------



## maidenn (4. Jun 2022)

httpdigest hat gesagt.:


> Also bei mir ist die Schleife eine Endlosschleife, sobald in der Eingabe mindestens ein Leerzeichen drin ist, weil du immer wieder denselben Index berechnest. Es kommt also niemals 1 raus, sondern ein endlos arbeitendes Programm.
> Du musst schreiben: `satz.indexOf(" ", fromIndex + 1)`


Danke für deine Hilfe, leider bekomme ich aber noch immer weiterhin nur das Ergebnis 1 angezeigt


----------



## httpdigest (4. Jun 2022)

Achso und: Der Scanner selbst trennt bei next() auch selbst an Leerzeichen. Du wolltest vermutlich nicht next() sondern nextLine() aufrufen.
Ich hatte in meinem Test deinen Code abgeändert und erstmal keine Usereingabe angefragt, sondern einfach einen konstanten String genommen.


----------



## maidenn (4. Jun 2022)

httpdigest hat gesagt.:


> Achso und: Der Scanner selbst trennt bei next() auch selbst an Leerzeichen. Du wolltest vermutlich nicht next() sondern nextLine() aufrufen.
> Ich hatte in meinem Test deinen Code abgeändert und erstmal keine Usereingabe angefragt, sondern einfach einen konstanten String genommen.


oh man, danke dir! Das hat das Problem gelöst... dabei ist dieser nextLine() Aufruf eigentlich noch gar nicht Inhalt von unserer Vorlesung...


----------



## httpdigest (4. Jun 2022)

Ja, es ist immer wichtig, bei sämtlichen Informatikthemen niemals auf die Idee zu kommen, selber auf die Suche zu gehen und Recherche zu betreiben außerhalb von Vorlesungen/Übungen, sondern immer nur die Klassen/Methoden und Syntaxelemente zu verwenden, die auch explizit in der Vorlesung behandelt wurden.

Not.


----------



## maidenn (4. Jun 2022)

httpdigest hat gesagt.:


> Ja, es ist immer wichtig, bei sämtlichen Informatikthemen niemals auf die Idee zu kommen, selber auf die Suche zu gehen und Recherche zu betreiben außerhalb von Vorlesungen/Übungen, sondern immer nur die Klassen/Methoden und Syntaxelemente zu verwenden, die auch explizit in der Vorlesung behandelt wurden.
> 
> Not.


wie du vielleicht meiner Nachricht entnehmen konntest, bin ich auf Recherche gegangen und habe die Lösungen mit split() und anderen Methoden genau aus dem Grund ausklammern müssen, da unser Dozent explizit von uns verlangt hat, dass mit von ihm referenzierten Methoden zu lösen, deswegen bin ich verwundert, dass es nun doch etwas ist, dass nicht auf seiner Referenz steht


----------



## Beispiel0 (4. Jun 2022)

@httpdigest hat natürlich recht, so wirst du erstmal kein Informatiker.

Aber mal andersherum gefragt, welche Methoden dürfen benutzt werden, bzw. sind referenziert?


----------



## httpdigest (4. Jun 2022)

maidenn hat gesagt.:


> wie du vielleicht meiner Nachricht entnehmen konntest, bin ich auf Recherche gegangen und habe die Lösungen mit split() und anderen Methoden genau aus dem Grund ausklammern müssen, da unser Dozent explizit von uns verlangt hat, dass mit von ihm referenzierten Methoden zu lösen, deswegen bin ich verwundert, dass es nun doch etwas ist, dass nicht auf seiner Referenz steht


Wir wissen ja nicht, was auf der für uns unbekannten Referenz vom Dozent steht.
Du schriebst:


maidenn hat gesagt.:


> ich soll in einer Aufgabe die Anzahl der Wörter in einem String zählen und habe hierfür hauptsächlich die String Methoden zur Verfügung. Theoretisch sind auch noch Wrapperklassen und StringBuilder verfügbar, aber ich glaube die bringen bei der Aufgabe keinen Vorteil.


Also: Du hast hauptsächlich die String Methoden zur Verfügung. Also darfst du _keine_ Scanner Methoden verwenden? Und, falls du _doch _Scanner Methoden verwenden darfst, welche denn da genau?

Wie @Beispiel0 schon schrieb, brauchen wir von dir erstmal eine Liste aller erlaubten bzw. vom Dozenten referenzierten Methoden. Ansonsten ist es immer ein Hinundhergerate und Abgelehne von Lösungen basierend auf für uns unbekannten Informationen.

Am besten wäre es vermutlich, wenn du uns einfach die exakte Aufgabenbeschreibung nennst und alle Materialien/Referenzen zur Verfügung stellst, aus denen du eine Lösung selbst ableiten würdest. Du schreibst auch, dass die Aufgabe darin besteht, die Anzahl der Wörter in einem String zu ermitteln. Das beinhaltet laut dieser Aufgabenstellung aber nicht unbedingt, dass der User per Standard-Eingabe den Text tatsächlich interaktiv auf der Konsole eingeben soll, oder etwa doch?


----------



## Blender3D (5. Jun 2022)

maidenn hat gesagt.:


> Meine Idee war mich an den Leerzeichen zu orientieren, doch irgendwie liegt da glaube ich das Problem. Ich bekomme als Ausgabe immer 1, also wird die Schleife 1 mal durchlaufen und direkt wieder verlassen.




```
public class TestWordCount {

    public static void main(String[] args) {
        String text = " Hallo das ist   ein Satz zum Testen. ";
        System.out.println(wordCount(text));
    }

    private static int ignoreSpace(String text, int idx) {
        while (text.charAt(idx) == ' ') {
            if (++idx == text.length())
                return -1;
        }
        return idx;
    }

    private static int wordCount(String text) {
        if (text == null || text.length() == 0)
            return 0;
        int cnt = 0;
        int idx = 0;
        do {
            if ((idx = ignoreSpace(text, idx)) < 0)
                return cnt;
            if ((idx = text.indexOf(' ', idx)) > 0)
                cnt++;
        } while (idx >= 0);
        return cnt + 1;
    }
}
```


----------



## Beispiel0 (5. Jun 2022)

Blender3D hat gesagt.:


> if (++idx == text.length())


Kurzes Review: niemals die Indexvariable stillschweigend auf diese Weise erhöhen, immer geschweifte Klammern setzen, Index nicht mit idx abkürzen, count auch nicht; nicht auf length=0 prüfen, do while nicht verwenden, nicht im return erhöhen, wordCount muss public sein, nicht -1 verwenden.


----------



## Beispiel0 (5. Jun 2022)

So hätte ich es vielleicht gemacht:


```
public static long wordCount1(String string) {
        return Pattern.compile("\\S+").matcher(string).results().count();
    }

    public static long wordCount2(String string) {
        int count = 0;
        int index = 0;
        int begin = 0;
        for (char ch : string.toCharArray()) {
            if (Character.isWhitespace(ch)) {
                if (begin < index) {
                    count++;
                }
                begin = index + 1;
            }
            index++;
        }
        if (begin < index) {
            count++;
        }
        return count;
    }

    public static void main(String[] args) {
        System.out.println(wordCount1(" Hallo das ist   ein Satz zum Testen. "));
        System.out.println(wordCount2(" Hallo das ist   ein Satz zum Testen. "));
        System.out.println(wordCount2(" a  bb   ccc"));
    }
```


----------



## Blender3D (5. Jun 2022)

Beispiel0 hat gesagt.:


> So hätte ich es vielleicht gemacht:


Abgesehen von dem Pattern Beispiel das nicht in der Aufgabestellung nicht erlaubt ist.
Hätte ich den Code nicht so unleserlicher gemacht wie es in Deinem Beispiel demonstriert wird.
Du führst unnötigerweise eine 3 Variable begin ein und. vergisst nebenbei dich gegen null abzusichern.
Was die Lesbarkeit des Codes ist sehe ich darin eine Verschlechtbesserung


----------



## Beispiel0 (5. Jun 2022)

Blender3D hat gesagt.:


> Du führst unnötigerweise eine 3 Variable begin ein und. vergisst nebenbei dich gegen null abzusichern.
> Was die Lesbarkeit des Codes ist sehe ich darin eine Verschlechtbesserung


Alle drei Punkte sind unbegründet.
Man braucht 3 Variablen (Minimum).
Gegen null ist abgesichert (.toCharArray() wirft dann die passende Exception).
Die Lesbarkeit ist ggü deiner Variante sogar erhöht.

Falls du mir nicht glaubst, stelle beide Versionen auf https://codereview.stackexchange.com/ Ich ärgere mich wegen so etwas nicht mehr.


----------



## KonradN (5. Jun 2022)

Naja. Clean Code ist immer etwas, das jeder für sich entscheiden muss. Bei den meisten Punkten bin ich durchaus bei @Beispiel0. Wobei ich nicht sehe, das gegen das do ... while spricht. Das ist halt auch nur ein durchaus übliches Konstrukt. Evtl. wäre der Code so umzuschreiben, dass die Prüfung der Länge nicht notwendig ist, da dies automatisch erfolgen könnte. Bei null ist kein String übergeben worden, daher wäre da die Rückgabe 0 eher ein Kritikpunkt von mir.

Also eine Variable zu viel? Ich bin eher der Meinung, dass man lieber mehr Variablen mit guten Bezeichnern hat als zwanghaft wenig. Da würde ich eher anmeckern, dass da ein Parameter verwendet wird für neue Werte. Ein Parameter ist eine Eingabe. Das ist also kein index, dem dem man weiter arbeitet sondern ein startIndex. Und dann kann man innen drin einen currentIndex haben, der bei startIndex startet und dann erhöht wird. Etwas in der Art.

Was mich bei @Beispiel0 stören würde: Es wird eine for each verwendet, aber der Index spielt eine Rolle und wird parallel mitgeführt? So eine Konstellation gibt es bei mir nicht: Wenn der index wichtig ist, dann mache ich den Index basierten Zugriff und keine for each.

Generell finde ich, dass man bei dem Code zu viel über die Funktionalität nachdenken muss. Der Algorithmus ist gradlinig zu formulieren und dann entsprechend umzusetzen. Wie macht man das denn?
Man geht den Text durch mit einem currentIndex:
- Ist man bei einem Buchstaben, dann hat man ein Wort gefunden und geht mit dem Index bis ans Ende des Wortes.
- Ist man bei einem Whitespace, dann ignoriert man dieses.

So in der Art. Und das würde ich dann auch so bauen - 1:1 mit Methoden, die dies so handhaben.

Hier hat man jetzt eine klare, kleine Aufgabe, da ist alles Andere vermutlich Overkill. Aber da kann man sehr schön objektorientiert arbeiten. 
Man hat dann eine Klasse, die mit einem String initialisiert wird und einen current Zeiger auf ein Zeichen des String hat.
Und da kann man dann sehr schön die notwendigen Klassen aufbauen. Dann hat man nicht so Konstrukte, dass ein Index hinter dem Wort gesucht wird und zurück gegeben wird, was ja entsprechend heissen muss. Und man hat dann auch den Fall, dass man eine Klasse mit public Methoden hat und keine Funktionalität entwickelt, die eigentlich nicht in die Klasse gehört und somit nicht direkt getestet werden kann.

Das wäre einfach einmal meine Sicht auf die angebotenen Lösungen.


----------



## mihe7 (5. Jun 2022)

Interessant ist ja schon, wie viele Möglichkeiten man hat, selbst einen so einfachen Algorithmus wie das Zurückgeben des Index des nächsten Nicht-Leerzeichens, zu implementieren.

Im Real-Life hätte ich das auch als No-Brainer umgesetzt:


KonradN hat gesagt.:


> Der Algorithmus ist gradlinig zu formulieren und dann entsprechend umzusetzen.



Man kann sich aber durchaus mal mit verschiedenen Möglichkeiten befassen. Da spricht mich

```
public static final int NOT_FOUND = -1;

public int ignoreSpace(String text, int ix) {
    String remainingText = text.substring(ix).trim();
    if (remainingText.isEmpty()) {
        return NOT_FOUND;
    }
    return text.indexOf(remainingText, ix);
}
```
irgendwie an.


----------

