FilterStream häufigkeit der Buchstaben

Bitte aktiviere JavaScript!
Guten Tag zusammen,

ich möchte sehr gerne ein kleines Programm schreiben in das ich eine Datei eingebe und eine Liste bekomme wie oft ein Buchstabe vorgekommen ist.
Nun habe ich aber schon ein Problem mir fällt derzeit keine Effiziente Möglichkeit ein die Buchstaben eines Textes zu zählen.
Das ganze habe ich erst mal so gemacht:
Java:
public class AnzahlBuchstaben {

    public static void main(String... args) {
        String s = "Das ist ein Test";
        int[] a = new int[300];
        
        for(int i=0;i<s.length();i++)
            if(Character.isLetter(s.charAt(i)))
                a[s.charAt(i)]++;

        for(int i=0;i<a.length;i++)
            if(a[i]!=0)
            System.out.println(a[i]+" "+(char)i);
        
        
    }
}
Erst wenn ich diese Teilaufgabe ordentlich gelöst habe komme ich zu den streams ;)
Ich hoffe mir kann dabei jemand helfen :)

LG
 
A

Anzeige




Schau mal hier —> (hier klicken)
Wenn die Buchstaben sortiert vorlägen, könntest Du in O(n) zählen...
 
Nein sortiert sind die Buchstaben ja nicht.
Es geht darum ich lese einen Text ein und zähle einfach die Vorkommenden Buchstaben.
So gesehen könnte ich mir auch das Array sparen doch dann hätte ich dutzend zugriffe auf die Datei.
Denn man könnte ja auch z. B. der Text wäre "Das ist nicht Ihr Haus!", den ersten Buchstaben zählen D und schauen ob der nächste Buchstabe wieder ein D ist denn counter nochmal um eins erhöhen, kommt ein anderer Buchstabe als das "a" dann auf in die Datei mit D und Counter, dass dann für jeden Buchstaben. Doch das wäre doch auch sehr Ineffizient oder :p

Hat noch jemand eine Idee wenn die Buchstaben nicht sortiert sind?

LG


mihe7 meintest du ich soll die Datei erst sortieren? Dann müsste aber doch der gesamte Text in den Hauptspeicher geladen werde sprich in ein Array. Ist das nicht noch ineffizienter?
 
Das mit dem Sortieren war als Wink mit dem Betonpfeiler gedacht... Du kannst natürlich auch Buchstaben auf Array-Plätze abbilden und diese hochzählen. Dabei hast Du aber entweder ein fixes Alphabet oder aber Du musst das Alphabet dynamisch erzeugen und darin suchen.
 
gut ich verstehe leider immer noch nicht was du meinst :D

also gibt es keine bessere Möglichkeit als eine von den beiden die du aufgeschrieben hast. Wobei ich nicht verstehe was du mit dem dynamischen erzeugen des Alphabetes meinst? Wieso soll man so etwas tun? Kannst du das etwas genauer erklären?

Mir ist jetzt nur noch eingefallen das man die Plätze des Arrays ja noch sehr drastisch verkleiner kann indem ich die Buchstaben dann einfach umrechne auf die ersten 60 Plätze :) Dann zähle ich in einem Int-Array nur noch hoch und gib es später aus :)

LG
 
Sortier die Zeichen, dann kannst Du einfach zählen.
Wobei ich nicht verstehe was du mit dem dynamischen erzeugen des Alphabetes meinst? Wieso soll man so etwas tun? Kannst du das etwas genauer erklären?
Irgendwie musst du ja die Zählwerte den Buchstaben zuordnen. Du kannst ein festes Alphabet verwenden (a-z usw.), dann reservierst Du Platz für Zeicheb, die ggf. Im Wort nicht vorkommen. Oder Du verwendest nur die Buchstaben, die im Wort vorkommen, dann hängt das Alphabet vom Wort ab (dynamisch).
 
achso jetzt habe ich das verstanden :)
Dabei könnte man dann auch die von Java zur verfügung gestellten Dynamischen-Arrays nutzen :) die hatten wir zwar noch nicht vom Stoff her aber das macht ja nix :D wobei ich es erst einmal nicht dynamisch machen will ob es überhaupt klappt :)

was mir jetzt nur leider noch nicht so ganz einleuchtet wäre das sortieren. Wie bekomme ich so eine Aufgabe vernünftig sortiert? Denn es hängt ja von der Datei ab die ins Programm kommt wieviele Buchstaben vorliegen.

LG
 
Dabei könnte man dann auch die von Java zur verfügung gestellten Dynamischen-Arrays nutzen
Es geht in erster Linie nicht um die Datenstruktur sondern um den Inhalt, d. h. zunächst muss nicht das Array dynamisch sein, sondern das Alphabet. Es gibt eine obere Grenze: der String kann nicht mehr Buchstaben als Zeichen enthalten. Dass man dafür dynamische Strukturen verwendet, ist wiederum in erster Linie eine Frage der Speichereffizienz.

Code:
    S   b   z
    --+---+---
>0  D |   |
 1  a |   |
 2  s |   |
 3    |   |
 4  i |   |
 5  s |   |
 6  t |   |

Aktuelles Zeichen (D) ist in b nicht vorhanden, also wird es zu b hinzugefügt und der dazugehörige Zähler wird auf 1 gesetzt. Das nächste Zeichen wird betrachtet usw.

Das Leerzeichen ist kein Buchstabe, wird also ausgelassen, nach ein paar Iterationen landet man bei

    S   b   z
    --+---+---
 0  D | D | 1
 1  a | a | 1
 2  s | s | 1
 3    | i | 1
 4  i |   |
>5  s |   |
 6  t |   |

Jetzt ist das s in b bereits vorhanden, und zwar an Index 2. Der Zähler an Index 2 wird also erhöht und das nächste Zeichen betrachtet.

    S   b   z
    --+---+---
 0  D | D | 1
 1  a | a | 1
 2  s | s | 2
 3    | i | 1
 4  i |   |
 5  s |   |
>6  t |   |

Das t ist wieder nicht in b vorhanden, vorgehen wie bisher. Das letzte Zeichen von S ist erreicht -> Ende.

    S   b   z
    --+---+---
 0  D | D | 1
 1  a | a | 1
 2  s | s | 2
 3    | i | 1
 4  i | t | 1
 5  s |   |
 6  t |   |
>

Das wäre eine Möglichkeit, allerdings nicht besonders effizient. Wesentlich effizienter bzgl. der Laufzeit wäre die Verwendung einer HashMap, die einem Buchstaben den Zählwert zuordnet.
 
@mihe7 :
Java:
import java.text.DecimalFormat;
import java.util.Arrays;

/**
 * @author tn, 13.04.2019
 */
public class AnzahlBuchstaben {
    private final int[] a = new int[Character.MAX_VALUE];
    private int c = 0;

    public static void main(String[] args) {
        AnzahlBuchstaben ab = new AnzahlBuchstaben();
        ab.test("Das ist ein Test.");
        Arrays.stream(ab.a()).forEachOrdered(System.out::println);
    }

    void test(String s) {
        for (char ch : s.toCharArray()) {
            n(ch);
        }
    }

    void n(char ch) {
        a[ch]++;
        c++;
    }

    int s() {
        return c;
    }

    String[] a() {
        StringBuilder b = new StringBuilder();
        DecimalFormat d = new DecimalFormat();
        d.setMinimumIntegerDigits(2);
        d.setMinimumFractionDigits(3);
        d.setMaximumFractionDigits(3);
        d.setMultiplier(100);
        for (int i = 0; i < a.length; i++) {
            int cn = a[i];
            if (cn != 0) {
                b.append((char) i);
                b.append(" ");
                b.append(String.format("%03d", cn));
                b.append(" ");
                b.append(d.format((float) cn / (float) s()));
                b.append(";");
            }
        }
        return b.toString().split(";");
    }
}

Code:
  003 17,647
. 001 05,882
D 001 05,882
T 001 05,882
a 001 05,882
e 002 11,765
i 002 11,765
n 001 05,882
s 003 17,647
t 002 11,765

Will man Es nicht sortiert haben, lässt man das Ordered einfach weg...
 
Das ist eine Implementierung mit einem statischen Alphabet (alle möglichen chars). Wenn Du hier aber irgendwo sortierst, würde ich Dir Punkte abziehen, denn dann wäre ein zusätzliches Alphabet überflüssig.
 
Das ist eine Implementierung mit einem statischen Alphabet (alle möglichen chars). Wenn Du hier aber irgendwo sortierst, würde ich Dir Punkte abziehen, denn dann wäre ein zusätzliches Alphabet überflüssig.
65536 Zeichen reichen doch wohl...
Wenn Du hier aber irgendwo sortierst
siehe
Will man Es nicht sortiert haben, lässt man das Ordered einfach weg...

Huch, den Kommentar habe ich ganz übersehen. Natürlich kann er das, dann verbrät er aber relativ viel Platz. Das wiederum muss man in Relation zur Eingabelänge und zur Laufzeit sehen. Es ist halt wie immer ein Balanceakt.
ja das stimmt schon... Viel schneller geht's aber nicht. :p

würde ich Dir Punkte abziehen
Fieser Mensch Du. :D
 
65536 Zeichen reichen doch wohl...
Die reichen für alles, aber 64 KiB für 10 Zeichen ist ..

Nein, schneller als O(n) gehts nicht und sortieren ist auch nicht immer eine Lösung (ich möchte nicht vorher 2 GB sortieren müssen). Er muss sich halt für eine Option entscheiden: will er sortieren, dann kann er die Zeichen einfach abzählen, will er ein fixes Alphabet (z. B. alle chars), dann muss er das Eingabealphabet auf dieses abbilden (bei allen chars trivial) und kann in einem Array zählen, will er das Alphabet während des Einlesens dynamisch aufbauen, dann ist der Algorithmus ausgabesensitiv bzgl. des Platzbedarfs, hat dafür Einbußen bei der Laufzeit.
 
@mihe7 Man könnte noch argumentieren, dass max. 65536 Strings fest wären und die Anzahl der zu zählenden Zeichen ja viel größer sein könnte. Dann würde das Sortieren in konstanter Zeit sein - und dann wäre das Sortieren sogar ein legitimes Mittel. :D (?)
 
Meine Frage ist gerade nur jetzt wo ich das verstanden habe mit dem dynamischen erstellen des Alphabetes wieso?
In einem größeren Text kommt ja sicherlich jeder Buchstabe mindestens 1x vor!

Ich habe das bis jetzt so gemacht ist zwar nicht wirklich schön aber es funktioniert erst einmal und das freut mich :p
Java:
package Insel;
import java.io.*;

public class AnzahlBuchstabenInDatei {
    
    public static void main(String... args) throws IOException {
        
          FileReader fr = null;
          BufferedReader br = null;
          LineNumberReader lnr = null;
          int i;
          String str;

          try {
              fr = new FileReader("/home/nachinstallierte_pakete.txt");
              lnr = new LineNumberReader(fr);
              br = new BufferedReader(fr);
              
              while((str = br.readLine()) !=null) {
                  int[] count = new int[58];

                  i = lnr.getLineNumber();
                    System.out.print("("+i+") ");
                    
                    System.out.println(str);

                  for(int j=0;j<str.length();j++)
                        if(Character.isLetter(str.charAt(j))) {
                            int tmp = str.charAt(j) - 65;
                            count[tmp]++;
                        }
                  
                  for(int k=0;k<count.length;k++)
                        if(count[k]!=0)
                            System.out.print(count[k]+" "+(char)(k + 65)+" ");
                  
                  System.out.println();

              }
          
        } catch(Exception e) {
            // if any error occurs
            e.printStackTrace();
        } finally {
            // closes the stream and releases system resources
            if(fr!=null)
                fr.close();
            if(lnr!=null)
                lnr.close();
     }
        
    }

}
 
Ach scheiße, UFT-8/16 ist ja bereits "sortiert"... Dann wäre das Sortieren eigentlich überflüssig... Das sind noch die Restwirkungen des Alkohols gestern.
@mihe7 Verzeihung.
 
Meine Frage ist gerade nur jetzt wo ich das verstanden habe mit dem dynamischen erstellen des Alphabetes wieso?
Um nicht für jeden möglichen Buchstaben Platz reservieren zu müssen, auch wenn dieser nicht vorkommt. Die einfachste Lösung ist die von @Tobias-nrw. Da wird für jedes erdenkliche Zeichen Platz reserviert (65536 Einträge á 4 Byte = 256 KiB; die int-Breite habe ich oben vergessen). Die Frage ist, ob bzw. wann es gerechtfertigt ist, 256 KiB an RAM zu verbraten, um z. B. 100 mögliche Buchstaben (von denen nur 50 tatsächlich im Text vorkommen) zu zählen.

Ach scheiße, UFT-8/16 ist ja bereits "sortiert"... Dann wäre das Sortieren eigentlich überflüssig... Das sind noch die Restwirkungen des Alkohols gestern.
@mihe7 Verzeihung.
Ich verstehe immer weniger :)
 
A

Anzeige




Vielleicht hilft dir das hier weiter: (klicke hier)
Passende Stellenanzeigen aus deiner Region:

Neue Themen

Oben