Anzahl verschiedener Buchstaben im String

Heinrich500

Bekanntes Mitglied
Hallo,
ich will ein Programm schreiben, das einen String einliest und prozentual angibt, wie oft ein bestimmter Buchstabe vorkommt. Dabei soll zwischen Groß-und Kleinbchstaben nicht unterschieden werden.
Das habe ich so erstmal geschafft:
Java:
public static void main(String[] args){
           
       
        Scanner s=new Scanner(System.in);
            
        double counter =0;
        byte[] eingabe = new byte[200];
        int[] anzahlBuchstaben = new int[26];
        String[] Buchstaben=new String[] {"A","B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S","T" ,"U", "V", "W", "X", "Y", "Z"};
       
       
        String text= s.nextLine();
        eingabe = text.getBytes();


        for (int i = 0; i < eingabe.length; i++)
            if (eingabe[i]>90) eingabe[i]-=32;         // Alle Buchstaben in Großbuchstaben umwandeln
                                                                            

        for (int i = 0; i < eingabe.length; i++)    //Anzahl der im eingelesenen Wort enthaltenen Buchstaben alphabetisch im Array
            anzahlBuchstaben[(eingabe[i]-65)]++;   
   
   
        
       
        for(int i=0; i<anzahlBuchstaben.length; i++)
            if(anzahlBuchstaben[i] !=0) counter+= anzahlBuchstaben[i];
       
        for(int i=0; i<anzahlBuchstaben.length; i++) {
            if(anzahlBuchstaben[i] !=0) {
                System.out.println(Buchstaben[i] + ": " + anzahlBuchstaben[i] + " (" + Math.round((anzahlBuchstaben[i]/counter)*100) + "%)" );
            }
        }
        }
   
}

Eingabe:Hallo
Ausgabe:
A: 1 (20%)
H: 1 (20%)
L: 2 (40%)
O: 1 (20%)

Wenn ich aber jetzt einen String mit Leerzeichen oder Ausreufezeichen eingeben will, dann bekomme ich einen outofindex Error. Was muss ich an meinem Programm ändern, damit er diese Nichtbuchstaben ignoriert?
 
Hallo, das ist die Fehlermeldung:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -32
at Buchstaben.main(Buchstaben.java:25)
bei Eingabe von Hallo!
 
Ja ich gebe Buchstaben ein und bestimme dann anteilig wie oft der Buchstaben in dem Wort vorkommt. Wenn ich jedoch nicht nur Buchstaben sondern Leerzeichen, Fragzeichen usw eingebe, soll er diese ignorieren und nur die Buchstaben zählen.
Wo genau soll ich das prüfen mit dem isLetter?
 
sehe gerade, dass Du ja einen ganzen String einliest (war aufgrund Deiner Variablennamen von einzelnen Buchstaben ausgegangen ...)

zuerst einmal: da Du ja einen String hast nach den Einlesen, kannst Du ihn ganz ein mit
Java:
String text= s.nextLine();
text.toUpperCase();
in Großbuchstaben umwandeln!
Danach läufst Du über diesen String (Stichwort: charAt(i)) und prüfst/zählst jedes Zeichen wie gewünscht !

VG Klaus
 
Dann hätte ich mir das Ascii-Umwandeln in Großbuchstaben doch sparen können 🙂
Wenn ich mit charAt drüber gehe, woher weis ich dann später wie viel Buchstaben es von jeder Sorte gibt?
 
Wie woher? Von welcher Klasse?
Ich habe sie einfach ins Programm geschrieben. Ich habe gedacht, dass diese schon irgendwie verebt wird. Aber das ganze Thema hatten wir noch nicht. Deshalb weis ich es nicht so genau.
 
Die von mir oben genannte statische Methode stammt aus der vordefinierten Klasse Character.
Java:
char c = 'a' ;
if(Character.isLetter(c)){
  ... 
}
 
Aso danke🙂 Ich hätte noch eine andere Frage. Ich habe in meinem Programm ein Array mit dem Alphabet manuell initialisiert. Wie kann man das effizienter machen. Tut mir leid für die späte Antwort.
 
Okay jetzt weiß ich wozu du das ganze brauchst. Es gäbe schon andere Wege das Array zu initialisieren - aber lohnt sich das wirklich?

Ich würde dir vorschlagen dir mal das Map-Interface anzuschauen (konkret zB: HashMap).. Damit kannst du den Buchstaben auf die Anzahl "mappen"
 
@Robat ich glaube, Moxxi meinte das andersrum: wenn er nur die a-z zählen will, dann wird er sich mit isLetter hart tun, weil dort auch für andere Zeichen true zurückgegeben wird.

Die Map funktioniert natürlich immer -> laaaangweilig 😉 Viel interessanter ist es doch, auf Arrays zu arbeiten.

@Heinrich500 Wenn Du wirklich nur a-z haben willst, wird die Sache sehr einfach. Dazu brauchst Du nur ein int-Array der Länge 26. Für das jeweilige Zeichen kannst Du einerseits einfach prüfen, ob es zwischen 'a' und 'z' liegt. Andererseits kannst Du durch Subtraktion von 'a' vom betreffenden Zeichen den Index im int-Array direkt berechnen (perfektes Hashing): int ix = ch - 'a'.

Wenn Du beliebige Buchstaben zählen willst, solltest Du mit Character.isLetter() filtern. Das Problem ist aber, dass im Zeichensatz nicht alle Buchstaben aufeinander folgen und durch die eben gezeigte Umrechnung große Lücken entstehen würden. Würdest Du wie eben ein int-Array verwenden, würde das verbraucht viel Platz verschwenden. Die Ausgabe wäre zeitlich auch nicht besonders effizient.

Eine andere Möglichkeit besteht nun darin, die Zeichen des Strings zu sortieren, dann kann die Häufigkeit der Buchstaben einfach der Reihe nach ausgegeben werden. Beispiel:

1. Ausgangstext: "HL. ABEND IST AM 24. DEZEMBER"
2. nur Buchstaben: "HLABENDISTAMDEZEMBER"
3. sortiert: "AABBDDEEEEHILMMNRSTZ"
4. der Reihe nach gezählt: 2 x A, 2 x B, 2 x D, 4 x E, ...
 
Hallo, ich habe nochmal überlegt und habe jetzt folgendes Programm, was noch nicht ganz funktioniert:
Java:
import java.util.Scanner;
public class Buchstabenausgeben {
   
   
   
    public static void main (String [] args) {
       
        Scanner s=new Scanner(System.in);
       
        String text=s.nextLine();
        int []  anzahl=new int[26];
        int buchstabeninsgesamt=0;
       
       
       
        text=text.toUpperCase();
       
       
        for(int i=0; i<text.length(); i++) {
            if('A' <=text.charAt(i) && text.charAt(i) <= 'Z') {
                anzahl[text.charAt(i)-'A']+=1;
                buchstabeninsgesamt++;
            }
        }
        for(int i=0; i<text.length(); i++) {
            if(anzahl[i]!=0) {
                System.out.println((char)('A' +i) + ": " + anzahl[i] + Math.round(anzahl[i]/buchstabeninsgesamt) +" %");
               
            }
        }
       
    }

}

Hast du es so änhlich gemeint mihe7?
Tut mir leid, dass ich mich erst jetzt melde.
 
Sie doch schon ganz gut aus 🙂 was funktioniert denn noch nicht? Ich würde vermuten die Ausgabe ist noch nicht ok. Anzahl/gesamt ist immer eine Zahl zwischen 0 und 1. Du musst das Ergebnis noch mit 100 multiplizieren um die prozentual zu erhalten.
 
Hallo, vielen Dank MoxxiManagarm🙂
Also wenn ich z.b hallo! eingebe, erhalte ich nur als Ausgabe:
A: 10 %
Der Rest fehlt. Das muss an der 2. for-Schleife irgendwo liegen, denn die 1. wird wegen buchstabeninsgesamt=5 durchlaufen.
Wie du auch feststellst, habe ich am Ende eine Zahl zwischen 0 und 1. Es kommt aber trotzdem die richtige Zahl mit 10 % heraus ohne nochmals mit 100 zu multiplizieren. Weist du der Fehler liegt?
 
1. A Kommt 1x vor. Deine division gerundet ergibt 0. String-concatiniert ergibt das 10. Daher 10%.

2. Die 2. Schleife durchläufst du abhängig von der Größe des Text. Für Hallo also von 0..4 da Hallo die Länge 5 hat. Bei Hallo hat nur a den Index im Bereich 0..4. A wird daher ausgegeben. Die 2. Schleife muss abhängig der Länge des Anzahl Array durchlaufen werden.
 
Ich habe es jetzt so geändert.
Java:
import java.util.Scanner;
public class Buchstabenausgeben {
 
 
 
    public static void main (String [] args) {
    
        Scanner s=new Scanner(System.in);
    
        String text=s.nextLine();
        int []  anzahl=new int[26];
        double buchstabeninsgesamt=0;
        text=text.toUpperCase();
    
    
        for(int i=0; i<text.length(); i++) {
            if('A' <=text.charAt(i) && text.charAt(i) <= 'Z') {
                anzahl[text.charAt(i)-'A']+=1;
                buchstabeninsgesamt++;
            }
        }
        for(int i=0; i<anzahl.length; i++) {
            if(anzahl[i]!=0) {
                System.out.println((char)('A' +i) + ": "  + anzahl[i] + " "+ "Math.round((anzahl[i]/buchstabeninsgesamt)*100.0) +" %");
            
            }
        }
    
    }

}
Ich habe auch buchstabeninsgesamt zu double gemacht. Geht das einfacher?

Edit: Ich könnte auch int lassen. Dann würde
Java:
Math.round((anzahl/(double)buchstabeninsgesamt)*100.0)
auch funktionieren.
 
Zuletzt bearbeitet:
Wer will, kann's auch so machen:
Java:
import static java.lang.Character.toUpperCase;
import static java.util.stream.Collectors.*;
import static java.util.function.Function.identity;
import java.util.*;

Scanner s = new Scanner(System.in);
Map<Character, Long> absoluteCounts = s
    .nextLine()
    .chars()
    .sorted()
    .mapToObj(c -> (char) toUpperCase(c))
    .collect(groupingBy(identity(), LinkedHashMap::new, counting()));
Map<Character, Double> relativeCounts = absoluteCounts
    .entrySet()
    .stream()
    .collect(toMap(Map.Entry::getKey, v -> v.getValue() * 100.0 / absoluteCounts.size()));
System.out.println(absoluteCounts);
System.out.println(relativeCounts);
 
Du kannst einfach mit einem try-catch-Block die ArrayIndexOutOfBoundsException abfangen und das Zeichen z.B.: in der Kategorie Sonderzeichen zählen.
 
Wenn ich die Buchstaben dann sortiert habe, wie genau kann ich dann ausgeben, dass ein Buchstabe x mal vorkommt. Ich verstehe die Idee, aber wie geht das algorithmisch?

Naja, Du überlegst Dir, dass Du so lange zählen musst, bis das nächste Zeichen entweder nicht existiert, weil Du am Ende des Arrays stehst, oder nicht mit dem aktuellen übereinstimmt.

Wenn ich mich nicht vertan habe, also:
Code:
int anzahl = 0;
for (int i = 0; i < zeichen.length; i++) {
    anzahl++;
    if ((i+1) == zeichen.length || zeichen[i] != zeichen[i+1]) {
        System.out.printf("%c: %.2f %%\n", 
                zeichen[i], anzahl*100.0/zeichen.length);
        anzahl = 0;
    }
}
 

Zurück
Oben