Zeichen aus Liste zählen und in Tabelle schreiben

Petra

Petra

Jungspund
Hallo,

ich habe ein (für mich) kniffliges Problem - vorweg: ich möchte keinen fertigen Codeschnipsel sondern ein "Lenken" in die richtige Fahrtrichtung ;)
wäre das ok?

Also ... ich bin leider als Einzelkämpferin unterwegs und finde keine Ansprechperson für folgende Aufgabe:

Ich habe eine *txt-Datei mit Wörtern bzw. den Zeichen, aus welchen die
Wörter bestehen.
Ich möchte ein Script bauen, welches die Aufgabe bekommt
- nimm ein Zeichen (ohne Leerzeichen)
- kuck wie oft es das Zeichen gibt
- schreibe das Ergebnis in tabellarischer Form | Zeichen | Anzahl | nieder
- geh zum nächsten Zeichen
- schau, ob du dieses Zeichen schon gezählt hast
- wenn ja - geh weiter
- wenn nein - kuck wie oft es das Zeichen gibt ...

- wenn alle Zeichen gezählt und niedergeschrieben sind, hör auf

Ich habe nun leider noch nicht herausgefunden, wo ich den Faden aufnehmen muss - mit welcher Syntax/welchem Vokabular fange ich an? Ich denke, das ist mein größtes Problem und wäre furchtbar dankbar, für einen Schubs in die richtige Richtung :)

Viele Grüße,
Petra
 
Hallo Petra,

so ganz spontan fällt mir auch keine einfache Lösung ein. Mit der Shell alleine wird es wahrscheinlich gar nicht gehen.
Du könntest ein Array abarbeiten, in dem alle zu zählenden Zeichen enthalten sind.
Du brauchst ferner einen Counter, der sich um eins erhöht, sobald ein Zeichen gefunden wurde.
Die Ausgabe als Tabelle ist einfach: echo -e "$mychar\t$counter" >> char-tabelle

Mein Problem, zu dem mir grade nichts einfällt, ist: Wie zählt man die Zeichen in einem Text? Zeilenweise abarbeiten? Könnte so aussehen (Fragment):
Code:
while read line;
do
  echo $line | \
  hier_kommt_der_code_der_die_zeichen_zählt_und_den_counter_erhöht
done < BLA.txt

Einfach alle Zeichen zu zählen wäre kein Problem. Dafür gibt es wc, das aber nicht zwischen Zeichen unterscheidet, sondern einfach alles zählt.

Was intelligenteres auf der Shell fällt mir grade nicht ein.

Gruß
 
Ich schliesse mich phrenicus an. Mit bash-Mitteln tust Du Dich da schwer. Ich empfehle da eher eher eine höhere Sprache, eventuell Perl oder C.

Gruss, Xanti
 
Mir würde auch bloß C/C++ einfallen, einen 2D-Array, [Zeichen][vorkommen,bei vorkommen++]
ich weis nicht, ob so etwas (2D-Array) mti der Shell geht.
 
Hallo

Die Perlvariante als Schnellschuss sähe in etwa so aus:

Code:
cat testtextext
Das ist ein Text mit ein paar Zeichen.
Hier kommen noch ein paar zeichen.
Ende.

$ perl -0777 -ne 's/\W+//g;$h{$_}++ for split "",$_;END{print "$_= $h{$_}\n" for keys %h}' testtext
perl -0777 -ne 's/\W+//g;$h{$_}++ for split "",$_;END{print "$_= $h{$_}\n" for sort keys %h}' testtext
D= 1
E= 1
H= 1
T= 1
Z= 1
a= 5
c= 3
d= 1
e= 11
h= 3
i= 8
k= 1
m= 3
n= 8
o= 2
p= 2
r= 3
s= 2
t= 3
x= 1
z= 1

Du kannst natürlich auch AWK dazu nehmen, was aber lansamer als perl ist.
Der Trick ist die Verwendung von Hashtables nach entfernen der nicht alphanumerischen Zeichen.
Sollen Groß und Kleinschreibung nicht berücksichtigt werden, dann wandle erst alles in Kleinbuchstaben um.

Gruß Wolfgang
 
Hallo,

bin gerade erst nach Haus gekommen und sage als erstes mal vielen Dank für eure Antworten!

@phrenicus
> Wie zählt man die Zeichen in einem Text?

Das habe ich bisher recht mühsam "zu Fuß" im Emacs gemacht...
Code:
M+x count-matches
dann Enter und danach das zu zählende Zeichen eingegeben.
Um mir die Gesamtzahl der Zeichen schnell rauszuschmeißen habe ich
dann den regexp
Code:
.
eingegeben. Für kleine Absätze ist das ok,
aber jetzt muss ich größere Textmengen bearbeiten, und dafür ist diese
Vorgehensweise eine echte Sisyphusarbeit *stöhn

Dass ich mit der Shell nicht weit komme, habe ich schon befürchtet.

@Wolfgang
Dein Tipp kommt der Lösung meines Problems glaube ich am nächsten.
Einfach alles in Kleinbuchstaben umwandeln kann ich leider nicht, da es sich
um zwei Listen mit in SAMPA übertragene Wörter handelt - die Listen
enthalten also sowohl Groß- als auch Kleinbuchstaben sowie numerische
Zeichen. Ich werde mir deinen "Schnellschuss" morgen früh in Ruhe
anschauen - auf den ersten Blick kann ich vor allem nichts mit

Code:
-0777
anfangen, aber das werde ich schon noch herausfinden
;-) Zudem muss ich innerhalb der beiden Listen zum Teil 2 Zeichen als eines
Interpretieren (z.B. a: - a und Doppelpunkt gelten also als Einheit) - da
müsste ich also noch irgendwie eine Variable reinschieben, welche diese Fälle
berücksichtigt...

Nun denn - morgen geht's weiter und wenn's läuft werde ich es hier posten.

Vielen Dank nochmal :)
Grüße, Petra
 
Zuletzt bearbeitet:
Hallo
Nur kurz zu meinem Beispiel:
Die Option -0777 veranlasst perl, die gesamte datei einzulesen (EOF ist oktal 777).
Siehe auch perldoc perlrun
Ein a: wird bei meiner Variante einfach zu a gezählt.
Es werden nur alphanumerische Zeichen gezählt.
Dein Problem mit Groß und Kleinbuchstaben ist eigentlich keines. Du kannst es auch getrennt betrachten, wie ich es gemacht habe.

Gruß Wolfgang
 
Da Wolfgang nicht da ist, antworte ich mal. @ ist kein alphanumer. Zeichen. Das sind [A-Za-z0-9].

Ich hab Wolfgangs Lösung entsprechend Deiner Anfrage modifiziert:

Code:
[~]$ cat testtext
Das ist ein Text mit ein paar Zeichen.
Hier kommen noch ein paar zeichen.
a:@b:
[~]$ perl -0777 -ne 's/(?<!a):|[^\w\@:]//g; $h{$_}++ for split /(?!:)/,$_; END{print "$_= $h{$_}\n" for sort keys %h}' testtext
@= 1
D= 1
H= 1
T= 1
Z= 1
a= 5
a:= 1
b= 1
c= 3
e= 10
h= 3
i= 8
k= 1
m= 3
n= 7
o= 2
p= 2
r= 3
s= 2
t= 3
x= 1
z= 1

Gruss, Xanti
 
Hallo,

klasse Code. Wird Zeit, mal perl zu lernen ;)
Allerdings wird bei einem Testtext von mir auch der _ (underscore) mitgezählt. Gilt der als alphanumerisches Zeichen?

X= 28
Y= 12
Z= 259
_= 11
a= 7866
b= 3183
c= 3430

Gruß
 
Stimmt, \w matcht sowohl Alphanumerische Zeichen als auch _. Danke für den Hinweis. Folgender Code müsste richtig sein:

Code:
perl -0777 -ne 's/(?<!a):|[^\p{Alnum}\@:]//g; $h{$_}++ for split /(?!:)/,$_; END{print "$_= $h{$_}\n" for sort keys %h}' testtext

Gruss, Xanti

edit: Nein, _ ist kein alphanumerisches Zeichen.
 
Zuletzt bearbeitet:
Hallo Xanti,

hier ist interessanterweise das @ dabei:

Code:
8        33
9        89
@        67
A        504
B        677

sowie weitere Zeichen am Ende, die ich so nicht zuordnen kann. Durch cat -A gepiped sieht das dann so aus:

Code:
y        396$
z        1923$
M-D        17$
M-V        2$
M-\        50$
M-_        124$
M-d        644$
M-v        370$
M-|        747$

Gruß
 
phrenicus schrieb:
Hallo Xanti,

hier ist interessanterweise das @ dabei:

Das ist richtig, da @ auf Wunsch von Petra gezählt werden soll.

phrenicus schrieb:
sowie weitere Zeichen am Ende, die ich so nicht zuordnen kann. Durch cat -A gepiped sieht das dann so aus:

Code:
y        396$
z        1923$
M-D        17$
M-V        2$
M-\        50$
M-_        124$
M-d        644$
M-v        370$
M-|        747$

Gruß

Hmm, sind vermutlich Steuerzeichen. Diese sollten theoretisch nicht mitgezählt werden. Kannst Du mir bitte einen Teil Deines Textes zum Testen zukommen lassen?

btw, mein cat kennt keine Option -A. Was bewirkt dies?

Gruss, Xanti
 
Hallo,

Code:
man cat:
-A, --show-all
              equivalent to -vET
-v, --show-nonprinting
              use ^ and M- notation, except for LFD and TAB
-E, --show-ends
              display $ at end of each line
-T, --show-tabs
              display TAB characters as ^I

URL zum Download des Textes per PN.

Gruß
 
@ phrenicus:

Das sind Umlaute. :)

Code:
y= 89
z= 303
Ä= 8
Ö= 2
Ü= 4
ß= 8
ä= 95
ö= 52
ü= 130
 
Hallo
Umlaute werden abhängig von der Locale zu alphanumerischen Zeichen gezählt, oder auch nicht. Da ist schon so Mancher drüber gestolpert.
mee to ;)

*klugscheiss Ende ;)

Gruß Wolfgang
 
Hihi :)

Super, hab mich eh schon gefragt, wo in einem LaTeX-Dokument die ganze Steuerzeichen herkommen sollen.
Mit etwas Nachdenken hätte ich auch selbst draufkommen können.

Danke.
 
noch ein bischen Verständnisprobleme

Hallo alle,

erstmal vielen Dank für eure ausführlichen und sehr hilfreichen antworten -
zwar geht es nun vornehmlich um 'perl', da dies aber im Terminal ausgeführt
wird, antworte ich hier weiter.

Wie schon gesagt, hätte ich mich schon total über Anhaltspunkte gefreut -
diese ausführlichen Einzeiler sind natürlich zum Selbstlernen wesentlich
hilfreicher gewesen!

Mein größtes Problem, das Skript auf alle meine Zeichen anwenden zu können,
steckt glaube ich in dem noch fehlenden Verständnis, was genau hier passiert:

Code:
|[^\w\@:]

Ich weiß, dass es sich um eine Option zu dem handelt, was vor dem <|> steht. Aber wie wirkt es sich aus?

Ich muss z.B. auch verschiedene zweiteilige Zeichen zählen, bei denen die <6>
die Stelle des <:> einnimmt. Zunächst wurde mir das <@> noch doppelt so
oft ausgegeben, als es tatsächlich vorkam - das habe ich schon gelöst *froi -
die Anzahl der alleinstehenden <6> stimmt jedoch immer noch nicht, da die
alleinstehenden natürlich von den in doppelter Kombination vorkommenden
abgezogen werden müssen.

Ich bin zunächst so vorgegangen, dass ich mir den gesamten ursprünglichen
Einzeiler laut vorgelesen bzw. durch Recherchen selber erklärt und dies
verschriftlicht habe.

Dies würde ich hier gerne posten, zusätzlich zu dem derzeitigen Stand meiner
Zeile incl. Ergebnis. Da dieser Text aber etwas länger ist möchte ich erst
fragen, ob euch das Recht und anderen hilfreich wäre?

Viele Grüße,
Petra

P.S. Ich möchte dazu noch sagen, dass ich zwei Bücher und diverse Internetquellen zur Hilfe genommen habe, ehe ich mich nochmal hier an euch wende, weil ich es mir einfach nicht zusammenreimen kann!
 
....
Code:
|[^\w\@:]

Ich weiß, dass es sich um eine Option zu dem handelt, was vor dem <|> steht. Aber wie wirkt es sich aus?

Nein, da liegst Du falsch. Ich versuch's das substitute grob zu erklären:

Code:
s/(?<!a):|[^\w\@:]//g;

Das ist ein Substitutionsbefehl (s/muster/replace/option). Damit wird der Text bereinigt, also alle nicht zu zählenden Zeichen werden gelöscht. (?<!a): bedeutet: alle :, die kein a vorstehend haben oder (dieses | bedeutet oder) alle Zeichen, die nicht zu der Menge der Wortzeichen (\w) gehören oder nicht @ sind. Nicht zur Menge wird durch [^...] gekennzeichnet.

Ich muss z.B. auch verschiedene zweiteilige Zeichen zählen, bei denen die <6>
die Stelle des <:> einnimmt. Zunächst wurde mir das <@> noch doppelt so
oft ausgegeben, als es tatsächlich vorkam - das habe ich schon gelöst *froi -
die Anzahl der alleinstehenden <6> stimmt jedoch immer noch nicht, da die
alleinstehenden natürlich von den in doppelter Kombination vorkommenden
abgezogen werden müssen.

Das musst Du nochmal erklären. Wie sehen die Zeichen genau aus?

Ich bin zunächst so vorgegangen, dass ich mir den gesamten ursprünglichen
Einzeiler laut vorgelesen bzw. durch Recherchen selber erklärt und dies
verschriftlicht habe.

Dies würde ich hier gerne posten, zusätzlich zu dem derzeitigen Stand meiner
Zeile incl. Ergebnis. Da dieser Text aber etwas länger ist möchte ich erst
fragen, ob euch das Recht und anderen hilfreich wäre?

Damit haben wir kein Problem und für andere ist es sicherlich hilfreich.

P.S. Ich möchte dazu noch sagen, dass ich zwei Bücher und diverse Internetquellen zur Hilfe genommen habe, ehe ich mich nochmal hier an euch wende, weil ich es mir einfach nicht zusammenreimen kann!

Frag ruhig, wir beissen nicht. ;)

Gruss, Xanti
 
Hi,

oder (dieses | bedeutet oder)

ja genau - und weil gesagt wird, dass das Substitut gelöscht werden soll, bin
ich beim näheren nachdenken total durcheinander gekommen - ganz offensicht-
lich werden die anderen Zeichen ja hinterher doch gezählt ... wo kommen die
denn dann her, wenn sie vorher rausgeschmissen werden :hilfe2:

Aber gut ...
  1. Die Liste von Zeichen, die ich quantitativ auswerten will, sieht (gekürzt) so aus:
    Code:
    fe6traU@n
    genIa:l
    e6liC
    fraIgibiC
    zys
    aUtonOm
    flis@nt
    kla:6
    aufmE6kza:m
    ob@n
    lIg@n
    slefrik
    beraUS@n
    zInlIC
    geSmYkt
    fa6b@nfro
    lIpliC
    fraIhaIt
    g@hob@n
    lustiC
    s2pf@n
    diploma:tiS
    leb@n
    vE:@tfol
    laIs@
  2. Meine derzeitige Zeile so ...
    Code:
    perl -0777 -ne 's/(?<!a|E):|//g; $h{$_}++ for split /(?!:)/,$_; s/(?<!a|E|@|9|U|O|I|Y|2)6|[^\w\6]//g; $h{$_}++ for split /(?!6)/,$_;END{print "$_= $h{$_}\n" for sort keys %h}' testtext
  3. ... die Problemfälle (also Kombinationen, welche als ein Zeichen gelesen/gezählt werden müssen) sind:
    Code:
    ts
    a:
    E:
    aI
    aU 
    OI
    @6
    Y6
    96
    U6
    O6
    a6
    I6
    E6
    26
    E:6

So, ... und hier nun mein Selbst-lern-Text (Standpunkt: bevor ich mich an die eigenen Ergänzungen gemacht habe.):
Code:
##Programmaufruf

perl

####Übergabe von Optionen
##http://www.infodrom.org/~joey/Writing/freeX/tips/newline.html
##[...]Man kann Perl nämlich mitteilen, daß Dateien vollständig 
##eingelesen werden sollen und nicht zeilenweise. Tatsächlich 
##weiß Perl gar nicht, was Zeilen sind,[...]Damit man nicht für 
##jeden Datenstrom untersuchen muß, welches Zeichen nicht 
##enthalten ist, hat Perl den Wert »0777« (oktal für 511) für 
##diesen Zweck reserviert.[...]

-0777

##Filter-Option
##Sorgt dafür, dass alle weiteren Kommandos zwischen '' zusammen-
##gefasst werden:
##http://linuxwiki.de/PerlEinzeiler

-n

##Dateistop-Operator
##Ermöglicht es, perl-Kommandos über die Kommadozeile auszu-
##führen, anstatt ein komplexeres Skript in einer Separaten
##Datei zu schreiben. Schaut nach, ob die Datei, zu der das
##Skript geschrieben ist, überhaupt existiert.
##http://de.selfhtml.org/perl/funktionen/dateiverwaltung.htm

-e

##Zusammenfassen mehrer Kommandos durch einfache
##Anführungstriche

''

##Ersetzungs-Operator
##Sucht nach einem regulären Ausdruck, und ersetzt ihn
##durch ein anderes Muster nach dem Schema
##s/gesuchter_Ausdruck/ersetzender_Ausdruck/Art_der_Ersetzung
##http://de.selfhtml.org/perl/sprache/regexpr.htm#suchen_ersetzen

s/

##Demnach folgt jetzt der gesuchte Ausdruck ...
##
## (?<!a):|[^\w\@:]
##

##Hier sehe ich zunächst in der Mitte den '|'.
##Das ist eine Sequenz und unterteilt den Ausdruck
##in zwei optionale Submuster.
##nämlich

##den Ausdruck
##(?<! )
##wobei es hier

##eine Negation gibt

!

##welche den Gesamtausdruck zu einem s.g.
##negativen 'Lookbehind' werden lässt.
##Will heißen, es handelt sich aufgrund
##der *runden* Klammern um eine Gruppierung
##von Zeichen mit der Aufforderung an perl:
##"schau nicht nach hinten, wenn du auf
##die angegebene Zeichenkette triffst,
##sondern matche nur auf diese Zeichen-
##kette."

(?<! )

##Hier also die Lösung, um auf die SAMPA-Ausdrücke
##für gedehnte Vokale matchen zu können (z.B. <a:> )

(?<! a):

##jetzt das optionale Suchmuster
##[^\w\@:]

##Die *eckigen* Klammern sagen mir, dass es
##sich um eine Zeichenklasse oder ein
##klassenartiges Konstrukt handelt.
##"Reguläre Ausdrücke - O'Reilly"

##Hier wird durch das ^ gesagt
##"Als Option darf *keines* der Zeichen
##aus dieser Liste genommen werden."

[^ ]

##also kein Wortzeichen

\w

## kein at-Zeichen (muss durch den
##Slash markiert werden, da es sich für
##perl sonst um eine Variable handeln würde)

\@

##und kein Doppelpunkt

:

## Wie geschickt und wie plausibel das schon mal ist :)

## So, jetzt noch den Rest bis zum ersten Semikolon

## Ersetzte die Fundstücke durch nix

/

##zähle alle matches zusammen

/g

## und Schluss mit dem ersten Kommando

;

##Wuups - jetzt wird es schon schwieriger...
## $h{$_}++ for split /(?!:)/,$_;

##Eine Variable
##Erstmal wird eine spezielle Variable 'h' definiert
##durch das s.g. Metazeichen 'Dollarzeichen'

$h

##Noch eine Variable
##*Geschweifte* Klammern sind 'Mengenklammern'
##Das Dollarzeichen mit dem Unterstrich ist eine
##Spezialvariable, die s.g. 'default-Variable'.
##Bei nicht expliziter Angabe einer anderen Variable
##wird diese default Variable verwendet.

{$_}

##Will heißen "match auf die Variable 'h', welche
##wiederum aus der Menge von 'default' besteht"
##$h{$_}

##Ein Operator
##Das doppelte Pluszeichen ist ein Inkrement-Operator
##Inkrement ist ein Betrag, um welchen eine
##bestimmte Größe zunimmt - in meinem Fall soll eine
##Größe, nämlich die der Variablen 'h', um genau '1'
##erhöht werden.

++

##Schluck - meine erste Schleife ...

for

##Aufbau der for-Schleife
##(Ah - wichtige Information: in einer for-Schleife stehen
##Anfangs- und Endwert von vorneherein fest - in der
##Schleife wird klar definiert, unter welcher Bedingung
##die Schleife abbrechen soll ... )
##Also:
##Wir haben zuerst die Variable 'h' definiert - jetzt
##wird mit drei Anweisungen gesagt, was wann und wie lange mit dieser
##Variablen passieren soll
## for <Ausdruck 1>  <Ausdruck 2>  <Ausdruck 3>

##(Bin hier irritiert, weil in meiner Literatur die 
##in einer runden Klammer stehen und jeweils durch ein
##Semikolon voneinander getrennt sind - aber ich nehme mal
##an, dass dies sich nur auf Skripte im Textformat bezieht? ... )

##<Ausdruck 1> ist hier ein s.g. Initialisierungs-Ausdruck

split

##eiwei - nun bin ich noch verwirrter :( 'sort' besteht wiederum
##aus Blöcken - sind die nun gleichzeitig die Blöcke
##der for-Schleife??
##split /MUSTER/, AUSDRUCK

##'split' separiert bestimmte Muster voneinander
##es sagt zuerst, *wie* etwas geteilt werden soll

##Ah - hier ist wieder der negative Lookahead 
##Also: "Wenn du an die Stelle mit dem Doppelpunkt
##kommst, trenne an dieser Stelle *nicht* rückwärts"
##mit anderen Worten - spalte genau das Zeichen, welches
##*hinter* einem Doppelpunkt liegt, *nicht von ihm ab.*

/(?!:)/,

##Und genau dies wird auf die default-Variable angewendet
##welche die Inhaltsmenge der Variable 'h' darstellt.

$_

##Und wieder Ende des Kommandos

;

##Und *schwupps - schon ist die Zählarbeit erledigt

END

##und es wird bestimmt, was genau mit dem Ergebnis
##in welcher Art und Weise gemacht werden soll.
##Dies ganze wird in den geschweiften Klammern zusammen-
##gefasst (hm - kann ich den Inhalt von geschweiften Klammern
##ja eigentlich nicht als 'Menge' beschreiben? *schnellnachlesen
## - ah: der Inhalt von geschweiften Klammern beschreibt
##eine *Eigenschaft*)
##Gut - die Eigenschaften von 'END' sind
##{print "$_= $h{$_}\n" for sort keys %h}

##Drucke den ganzen Kram aus

print

##stelle ihn genaus so dar, wie es in den doppelten
##Anführungszeichen beschrieben ist

""

##und zwar ordnest du das Ergebnis der Variablen 'h'

$h($_)

##durch das Gleichheitszeichen

=

##der default-Variablen

$_

##zu - der Inhalt von '$_' soll dem jeweils aktuellen
##Wert entsprechen, bis

##durch den Zeilenumbruch in die nächste Zeile
##gegangen wird

\n

##das war also
##print "$_= $h{$_}\n"

##durch eine weitere for-Schleife wird noch
##gesagt, in welcher Reihenfolge dieser Ausdruck
##zu erfolgen hat
##for sort keys %h

for

##nämlich sortiert

sort

##und nach was soll sortiert werden?
##nach bestimmten Schlüsseln

keys %

##und der Name der Schlüssel ist der jeweils
##aktuelle Wert der Variablen 'h'

h

##So - und diese Kommandos werden nun auf den
##genannten Text angewendet.

P.S. Dieses automatische Erstellen von scrawlbaren Rahmen finde ich übrigens totschick :)


sorry ... fehlende Literaturangaben ...
als Bücher habe ich noch genutzt:

Tony Stubblebine Reguläre Ausdrücke - kurz & gut o'reilly ISBN 3-89721-264-1

RRZN Uni Hannover Perl - Eine Einführung Barcode Nr (hier gibt es keine ISBN!) 4032141001734


ach... und natürlich:

viele Grüße zurück und danke für
Frag ruhig, wir beissen nicht.
 
Zuletzt bearbeitet:
Zurück
Oben