Buchstaben aus einer Textdatei zählen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Marieee
User
Beiträge: 2
Registriert: Samstag 8. Oktober 2022, 16:46

Hallo zusammen,

ich habe folgendes Problem:
Ich muss aus einer Textdatei zählen, wie oft jeder Buchstabe (a-z) vorkommt und welcher der häufigste ist.
Mein Ansatz ist mit viel Schreibarbeit verbunden, das klappt zwar aber ich würde es lieber mit einer For schleife lösen, da komme ich allerdings nicht weiter.
Hat jemand vielleicht einen Tipp/Ansatz ?

Vielen Dank
Benutzeravatar
darktrym
User
Beiträge: 785
Registriert: Freitag 24. April 2009, 09:26

Versteh ich nicht, du kannst doch mit einer For Schleife über den Datei Referenz gehen und zählst da fleißig in einem Dictionary mit?
„gcc finds bugs in Linux, NetBSD finds bugs in gcc.“[Michael Dexter, Systems 2008]
Bitbucket, Github
Sirius3
User
Beiträge: 18260
Registriert: Sonntag 21. Oktober 2012, 17:20

Das ist ein Einzeiler mit collections.Counter.
Benutzeravatar
__blackjack__
User
Beiträge: 14019
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Marieee: Der Ansatz wäre eine ``for``-Schleife über die eingelesenen Daten zu schreiben. 😎

Wo kommst Du denn konkret nicht weiter? Sollen Grossbuchstaben wie Kleinbuchstaben zählen? Also ist in "Hoho" zweimal "h" oder einmal "H" und einmal "h"? Sollen wirklich nur A bis Z gezählt werden, oder auch andere Buchstaben wie Ä und ß? Was wäre mit π oder Δ?

Am einfachsten wäre es übrigens `collections.Counter` zu verwenden.
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
Marieee
User
Beiträge: 2
Registriert: Samstag 8. Oktober 2022, 16:46

Ich hab es jetzt mittels collection.counter versucht. Dies klappt auch soweit für eingegebene Texte. Wenn ich jetzt versuche mich auf eine Textdatei zu beziehen bekomme ich es nicht hin.
Es soll nur A-Z gezählt werden und groß und klein - Schreibung soll nicht unterschieden werden.
f = open("Text.txt", "r")
data = f.read()

count = Counter()
count.update('data')

print('\n')
for char in 'abcdefghijklmnopqrstuvwxyz':
print('%s : %d' % (char, count[char]))
Benutzeravatar
__blackjack__
User
Beiträge: 14019
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Marieee: Was ist denn der Unterschied zwischen ``print(data)`` und ``print("data")``? Genau den Fehler machst Du da.

Edit: Zeichenkettenformatierung mit ``%`` macht man nicht mehr, dafür gibt es f-Zeichenketten. Und die ASCII-Kleinbuchstaben gibt es im `string`-Modul, die muss man nicht selbst eintippen.

Edit2: Ach so: Du unterscheidest zwischen Gross- und Kleinschreibung, das heisst Grossbuchstaben werden bei Deinem Code einfach gar nicht gezählt.
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
Benutzeravatar
snafu
User
Beiträge: 6854
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Man sollte auch nicht über alle Buchstaben des Alphabets, sondern über den Counter iterieren. Ansonsten wirft das einen Fehler, falls einer der Buchstaben nicht im Text enthalten ist. Oder man macht es via ``count.get(char, 0)``.
Benutzeravatar
__blackjack__
User
Beiträge: 14019
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@snafu: Nee das ist schon okay so. `collections.Counter` liefert 0 bei Zugriff auf unbekannte Schlüssel:

Code: Alles auswählen

In [1034]: counter = collections.Counter()

In [1035]: counter[42]
Out[1035]: 0
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
nezzcarth
User
Beiträge: 1752
Registriert: Samstag 16. April 2011, 12:47

'collections.Counter' ist hier korrekt, wie die anderen schon meinten. Lernenden, die allerdings nicht wüssten, wie es sonst geht, kann ich nur empfehlen, zur Übung z.B. auch die "klassische" Variante mit einem Dictionary zu probieren. Die Dinge, die man dafür benötigt, sollte man finde ich in jedem Fall beherrschen und das wäre dann eine Gelegenheit, an dieser Lücke zu arbeiten.
Benutzeravatar
__blackjack__
User
Beiträge: 14019
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Klassisch kenne ich das eher in diese Richtung (also das zählen, nicht die Aufbereitung für die Ausgabe):

Code: Alles auswählen

#!/usr/bin/env python3
import sys
from string import ascii_lowercase

import rich
from rich.bar import Bar
from rich.box import ROUNDED
from rich.table import Column, Table


def main():
    counters = [0] * len(ascii_lowercase)
    for line in sys.stdin:
        for character in line.lower():
            if "a" <= character <= "z":
                counters[ord(character) - ord("a")] += 1

    result = sorted(
        zip(ascii_lowercase, counters), key=lambda item: (-item[1], item[0])
    )

    total_count = sum(count for character, count in result)
    table = Table(
        Column("character", justify="center"),
        Column("count", f"{total_count:,}", justify="right"),
        "",
        title="Character frequencies",
        box=ROUNDED,
        show_footer=True,
        highlight=True,
    )
    max_count = result[0][1]
    for character, count in result:
        table.add_row(
            repr(character),
            f"{count:,}",
            Bar(max_count, 0, count, color="bright_blue"),
        )
    rich.print(table)


if __name__ == "__main__":
    main()
Ausgabe für den englischen Text von „Alice in Wonderland“:

Code: Alles auswählen

$ ./forum12.py < nltk_data/corpora/gutenberg/carroll-alice.txt
                             Character frequencies                              
╭───────────┬─────────┬────────────────────────────────────────────────────────╮
│ character │   count │                                                        │
├───────────┼─────────┼────────────────────────────────────────────────────────┤
│    'e'    │  13,570 │ ██████████████████████████████████████████████████████ │
│    't'    │  10,685 │ ██████████████████████████████████████████▌            │
│    'a'    │   8,791 │ ██████████████████████████████████▉                    │
│    'o'    │   8,145 │ ████████████████████████████████▍                      │
│    'i'    │   7,510 │ █████████████████████████████▉                         │
│    'h'    │   7,372 │ █████████████████████████████▎                         │
│    'n'    │   7,012 │ ███████████████████████████▉                           │
│    's'    │   6,500 │ █████████████████████████▊                             │
│    'r'    │   5,437 │ █████████████████████▋                                 │
│    'd'    │   4,929 │ ███████████████████▌                                   │
│    'l'    │   4,713 │ ██████████████████▊                                    │
│    'u'    │   3,465 │ █████████████▊                                         │
│    'w'    │   2,675 │ ██████████▋                                            │
│    'g'    │   2,531 │ ██████████                                             │
│    'c'    │   2,398 │ █████████▌                                             │
│    'y'    │   2,263 │ █████████                                              │
│    'm'    │   2,104 │ ████████▎                                              │
│    'f'    │   2,000 │ ███████▉                                               │
│    'p'    │   1,524 │ ██████                                                 │
│    'b'    │   1,476 │ █████▊                                                 │
│    'k'    │   1,158 │ ████▌                                                  │
│    'v'    │     846 │ ███▎                                                   │
│    'q'    │     209 │ ▊                                                      │
│    'x'    │     148 │ ▌                                                      │
│    'j'    │     146 │ ▌                                                      │
│    'z'    │      78 │ ▎                                                      │
├───────────┼─────────┼────────────────────────────────────────────────────────┤
│           │ 107,685 │                                                        │
╰───────────┴─────────┴────────────────────────────────────────────────────────╯
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
nezzcarth
User
Beiträge: 1752
Registriert: Samstag 16. April 2011, 12:47

@__blackjack__: Also wenn es um das klassisch geht – Mit dem ganzen syntaktischen Zucker & Komfort von Python und insbesondere ohne selbstgeschriebene Sortierfunktion ist das für mich allenfalls neoklassisch ;) (aber natürlich schick umgesetzt).
Benutzeravatar
__blackjack__
User
Beiträge: 14019
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Wenn man sich nur die erste Zeile von dem Alice im Wunderland-Text auswerten lässt, kommt eine interessante Gesamtbuchstabenanzahl bei heraus 🤓:

Code: Alles auswählen

$ head -n 1 nltk_data/corpora/gutenberg/carroll-alice.txt | ./forum12.py
                             Character frequencies                              
╭───────────┬───────┬──────────────────────────────────────────────────────────╮
│ character │ count │                                                          │
├───────────┼───────┼──────────────────────────────────────────────────────────┤
│    'e'    │     5 │ ████████████████████████████████████████████████████████ │
│    'l'    │     5 │ ████████████████████████████████████████████████████████ │
│    'a'    │     4 │ ████████████████████████████████████████████▊            │
│    'n'    │     4 │ ████████████████████████████████████████████▊            │
│    'r'    │     4 │ ████████████████████████████████████████████▊            │
│    'd'    │     3 │ █████████████████████████████████▌                       │
│    'i'    │     3 │ █████████████████████████████████▌                       │
│    's'    │     3 │ █████████████████████████████████▌                       │
│    'c'    │     2 │ ██████████████████████▍                                  │
│    'o'    │     2 │ ██████████████████████▍                                  │
│    'w'    │     2 │ ██████████████████████▍                                  │
│    'b'    │     1 │ ███████████▏                                             │
│    't'    │     1 │ ███████████▏                                             │
│    'u'    │     1 │ ███████████▏                                             │
│    'v'    │     1 │ ███████████▏                                             │
│    'y'    │     1 │ ███████████▏                                             │
│    'f'    │     0 │                                                          │
│    'g'    │     0 │                                                          │
│    'h'    │     0 │                                                          │
│    'j'    │     0 │                                                          │
│    'k'    │     0 │                                                          │
│    'm'    │     0 │                                                          │
│    'p'    │     0 │                                                          │
│    'q'    │     0 │                                                          │
│    'x'    │     0 │                                                          │
│    'z'    │     0 │                                                          │
├───────────┼───────┼──────────────────────────────────────────────────────────┤
│           │    42 │                                                          │
╰───────────┴───────┴──────────────────────────────────────────────────────────╯
@nezzcarth: Hier in klassich, statt neoklassich:

Code: Alles auswählen

   10 ti$="000000":dim c(25),ci(25):a=asc("a"):ua=asc("A")
   20 z=asc("z"):uz=asc("Z"):n=0
  100 open 2,8,2,"alice,s"
  110 get#2,c$:if st<>0 then 160
  120 c=asc(c$):if c=13 then n=n+1:print n:print "{up}";:goto 110
  130 if ua<=c and c<=uz then c=c-128
  140 if a<=c and c<=z then j=c-a:c(j)=c(j)+1
  150 goto 110
  160 close 2
  200 for i=0 to 25:ci(i)=i:next
  210 f=0:for i=0 to 24:if c(i)>=c(i+1) then 230
  220 f=-1:t=c(i):c(i)=c(i+1):c(i+1)=t:t=ci(i):ci(i)=ci(i+1):ci(i+1)=t
  230 next:if f then 210
  300 tc=0:for i=0 to 12
  310 print chr$(ci(i)+a);c(i);tab(10);chr$(ci(i+13)+a);c(i+13)
  320 tc=tc+c(i)+c(i+13):next:print "total characters:":print tc
 1000 print "in";ti/60;"s"
Lauffähig auf einem VIC-20 in Grundausstattung (allerdings mit Diskettenlaufwerk):

Code: Alles auswählen

RUN
E 13570   G 2531
T 10685   C 2398
A 8791    Y 2263
O 8145    M 2104
I 7510    F 2000
H 7372    P 1524
N 7012    B 1476
S 6500    K 1158
R 5437    V 846
D 4929    Q 209
L 4713    X 148
U 3465    J 146
W 2675    Z 78
TOTAL CHARACTERS:
 107685
IN 5900.68333 S

READY.
1 Stunde 38 Minuten und 20 Sekunden Laufzeit. Oder heisst das dann eher Kriechzeit‽ 🤣
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
Benutzeravatar
DeaD_EyE
User
Beiträge: 1231
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Es gibt viele Ansätze. Ich hoffe, das sind keine Hausaufgaben....

Python-Basic:

Code: Alles auswählen

import string


def count1(text):
    text = text.lower()
    result = {}
    
    for char in text:
        if char not in string.ascii_lowercase:
            continue
    
        if char not in result:
            result[char] = 1
        else:
            result[char] += 1

    return result


def count2(text):
    text = text.lower()
    result = {}
    
    for char in text:
        if char not in string.ascii_lowercase:
            continue
        
        # wenn der key char nicht existiert,
        # liefert die methode get das zweite Argument zurück.
        # in diesem falll eine 0
        result[char] = result.get(char, 0) + 1 
        
    return result



# mit defaultdict weniger code
from collections import defaultdict


def count3(text):
    text = text.lower()
    result = defaultdict(int)
    
    for char in text:
        if char not in string.ascii_lowercase:
            continue
        
        result[char] += 1
        
    return result


# mit Counter noch weniger code
from collections import Counter


def count4(text):
    return Counter(char for char in text.lower() if char in string.ascii_lowercase)
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Sirius3
User
Beiträge: 18260
Registriert: Sonntag 21. Oktober 2012, 17:20

Wobei man das continue vermeiden würde und direkt den Code in den if-Block packen würde:

Code: Alles auswählen

def count3(text):
    text = text.lower()
    result = defaultdict(int)
    
    for char in text:
        if char in string.ascii_lowercase:
            result[char] += 1
        
    return result
Benutzeravatar
__blackjack__
User
Beiträge: 14019
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Oder man packt vorher mal 0en für alle Zeichen in das Wörterbuch:

Code: Alles auswählen

import string


def count0(text):
    result = dict.fromkeys(string.ascii_lowercase, 0)

    for character in text.lower():
        if character in result:
            result[character] += 1

    return result
Falls man `fromkeys()` nicht kennt, gibt's ja noch andere Möglichkeiten. Notfalls mit einer Schleife.
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
nezzcarth
User
Beiträge: 1752
Registriert: Samstag 16. April 2011, 12:47

@__blackjack__: Die BASIC Variante entspricht in der Zeilenanzahl ca. meinem Versuch mit gawk. Ich finde es jedoch beeindruckend, wie "dicht" BASIC anscheinend ist. :)

Code: Alles auswählen

#!/usr/bin/gawk -f
BEGIN {
    FS=""
    PROCINFO["sorted_in"] = "@val_num_asc"
}
{
    for (i=1; i<=NF; i++) {
        if ($i ~ /[a-zA-Z]/) {
            total += 1
            counts[tolower($i)] += 1
	}
    }
}
END {
    for (key in counts) 
        printf "%s %d %3.2f%%\n", key, counts[key], counts[key]/total*100
    print "Total:", total
}

Code: Alles auswählen

./count.awk carroll-alice.txt
z 78 0.07%
j 146 0.14%
x 148 0.14%
q 209 0.19%
v 846 0.79%
k 1158 1.08%
b 1476 1.37%
p 1524 1.42%
f 2000 1.86%
m 2104 1.95%
y 2263 2.10%
c 2398 2.23%
g 2531 2.35%
w 2675 2.48%
u 3465 3.22%
l 4713 4.38%
d 4929 4.58%
r 5437 5.05%
s 6500 6.04%
n 7012 6.51%
h 7372 6.85%
i 7510 6.97%
o 8145 7.56%
a 8791 8.16%
t 10685 9.92%
e 13570 12.60%
Total: 107685
Benutzeravatar
__blackjack__
User
Beiträge: 14019
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@nezzcarth: Ich fand das ja geradezu luftig locker wenn man bedenkt wie viele optionale Leerzeichen da drin sind, und das ich mich nicht besonders bemüht habe möglichst viel in jede Zeile zu packen. 😈 Die Leerzeichen sind da hauptsächlich für den Syntax-Highlighter hier, die hätte ich zumindest früher nicht geschrieben. Auf dem VIC-20 sowieso nicht, weil noch früher in der Anzeige umgebrochen wird als auf dem C64. 22 Zeichen pro Zeile 40 beim C64. Das hätte also eher so ausgesehen:

Code: Alles auswählen

  100 TI$="000000":DIMC(25),CI(25):N=0:OPEN2,8,2,"ALICE,S"
  110 GET#2,C$:IFST<>0THENCLOSE2:FORI=0TO25:CI(I)=I:NEXT:GOTO210
  120 C=ASC(C$):IFC=13THENN=N+1:PRINTN:PRINT"{UP}";:GOTO110
  130 IF193<=CANDC<=218THENC=C-128
  140 IF65<=CANDC<=90THENJ=C-A:C(J)=C(J)+1
  150 GOTO110
  210 F=0:FORI=0TO24:IFC(I)>=C(I+1)THEN230
  220 F=-1:T=C(I):C(I)=C(I+1):C(I+1)=T:T=CI(I):CI(I)=CI(I+1):CI(I+1)=T
  230 NEXT:IFFTHEN210
  300 TC=0:FORI=0TO12:PRINTCHR$(CI(I)+A);C(I);TAB(10);CHR$(CI(I+13)+A);C(I+13)
  320 TC=TC+C(I)+C(I+13):NEXT:PRINT"TOTAL CHARACTERS:":PRINTTC
  999 PRINT"IN";TI/60;"S"
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
Antworten