noch eine encoding-Frage

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
Horst-Olaf
User
Beiträge: 4
Registriert: Mittwoch 27. Februar 2019, 12:39

Hallo zusammen,

Ich schreibe an einem Programm, das eine Textdatei einliest und die einzelnen Wörter zählt. Da habe ich jetzt folgendermaßen angefangen:

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-
from pylab import *
with open ('bsp.txt', "r") as myfile:
    data = myfile.readlines()
    
    def analyse(data):
    abc = "abcdefghijklmnopqrstuvwxyzöäüß"
    wordlist = []
    word = ""
    signs = ".,?!_-;:)(<>"
    for line in data:
        for character in line:
            characters = character.lower()
            if characters in abc:
                word += characters
            else:
                if len(word) >0:
                    wordlist.append(word)
                if letters in signs:
                    wordlist.append(characters)
                word = ""
    return wordlist
Nach Erstellung einer Statistik über Worthäufigkeiten, werden Wörter mit Umlauten dort getrennt, wo eigentlich der Umlaut hingehört.
Es hat ja eine Umstrukturierung von Python 2 zu Python 3 gegeben, so dass Decoding eigentlich nicht mehr nötig ist (wenn ich das richtig verstanden habe).
Nun scheint Python den eingegebenen Text als UTF-8 zu interpretieren - wie kann ich ihm das abgewöhnen? Bzw. an welcher Stelle muss ich zu ISO8859-1 und wieder zurück wechseln?

Das ist wahrscheinlich eine typische Anfängerfrage, aber ich sitze seit gestern an dem Problem und google wild herum ohne an eine Lösung zu kommen.

Lieben Gruß
Sirius3
User
Beiträge: 18269
Registriert: Sonntag 21. Oktober 2012, 17:20

Benutzt Du jetzt Python2 oder Python3. Die shebang-Zeile sagt Python2. Das würde auch erklären, warum das mit den Umlauten nicht funktioniert, denn Du verarbeitest Binar-Daten in ISO8859-1-Kodierung mit UTF8-Codierten Buchstabenbytes.
In Python3 mußt Du beim öffnen der Datei das richtige Encoding angeben.
Das `def analyse` steht bei Dir ziemlich unmotiviert mitten im Code. Warum ist `characters` ein Kleinbuchstabe? Was soll das `s` am Ende sagen? `letters` wird nirgends definiert.

Das ganze Aufspalten geht mit regulären Ausdrücken deutlich einfacher (mit Python3):

Code: Alles auswählen

with open('bsp.txt', encoding='ISO8859-1') as text:
    wordlist = re.findall('[a-zäöüß]+|[-.,?!_;:)(<>]+', text.read().lower())
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Welcher Text genau hat denn latin1 als Encoding? Der Quellcode? Das waere ungewoehnlich, da solltest du dir das "coding: ..." Geraffel sparen, und den als UTF-8 speichern und laden. Das ist seit Python3 der angenommene Default, und die beste Option.

Oder geht es dir um bsp.txt? Dazu gibt es den neuen Parameter encoding an die open-Funktion.

Code: Alles auswählen

with open('bsp.txt', 'r', encoding='latin1') as inf:
      ...
Dinge wie abc und signs sollten natuerlich Konstanten sein, also gross geschrieben, und auf Modul-Ebene. Wenn du

Code: Alles auswählen

for line in data.lower():
schreibst, wird der Code etwas kompakter, weil du dann nicht jedes Zeichen extra speichern musst.

Last but not least bietet sich fuer deinen Fall wahrscheinlich das re-Modul an, insbesondere re.split.
Horst-Olaf
User
Beiträge: 4
Registriert: Mittwoch 27. Februar 2019, 12:39

Danke für die schnellen Antworten:)

Ich benutze Python 3, ich wusste nicht, ob ich den Coding-Cookie am Anfang trotzdem brauche. Aber gut, dann nehm ich den raus.

Hmjaa, das ist leider kein eleganter Code...:D Die Funktion ist eigentlich nicht eingerückt, "characters" sind alle Zeichen, die in einem Text vorkommen und mit "letters" meinte ich "characters".

Nun habe ich den Code

Code: Alles auswählen

with open('bsp.txt', 'r', encoding='latin1') as inf:
       wordlist = re.findall('[a-zäöüß]+|[-.,?!_;:)(<>]+', inf.read().lower())
eingefügt und dafür die Umlaute aus dem ABC entfernt.
Nun läuft das Programm auch durch, aber es ändert nichts im Ergebnis. Die Wörter werden immernoch auf die selbe Weise getrennt.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Kannst du ggf. mal eine Beispieltext posten, den man benutzen kann? Und wie du dir das Ergebnis wuenschst? Mir ist das noch nicht klar geworden.
Benutzeravatar
snafu
User
Beiträge: 6866
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Statt lower() kann man auch einfach re.IGNORECASE als Flag mitgeben. Dann bleiben die Wörter in ihrer Originalschreibweise erhalten.

Sähe dann so aus:

Code: Alles auswählen

import re

with open('bsp.txt', 'r', encoding='latin1') as inf:
    words = re.findall('[a-zäöüß]+|[-.,?!_;:)(<>]+', inf.read(), re.IGNORECASE)
Horst-Olaf
User
Beiträge: 4
Registriert: Mittwoch 27. Februar 2019, 12:39

Das Programm ist Teil eines größeren Projekts.
Insgesamt soll ein Zufallsgenerator anhand der Worthäufigkeiten einen Zufallstext erstellen. Dabei ist die Idee, dass jeder beliebige Text eingespeist werden kann und dann im Stil dieses Texts ein neuer Text entsteht - zumindest grob und irgendwie.
Man gibt also einen Beispieltext ein (zum Beispiel ein Gedicht) und lässt die Wörter als Einzelstrings ausgeben. Das macht ja das (etwas holprige) Programm, das ich oben gepostet habe. Dann wird eine Statistik erstellt:

Code: Alles auswählen

def statistics(wordlist):
    
    occur = dict()
    
    for n in range(len(wordlist)): 
        word = wordlist[n]
        if word in occur:
            occur[word]+= 1
        else:
            occur[word]= 1
        
    return occur
und

Code: Alles auswählen

statistics(analyse(data))
gibt dann

Code: Alles auswählen

{'denk': 2,
  'dir': 2,
  'ein': 2,
  'tr': 2,
  'ffelschwein': 2,
  ',': 6,
  'denks': 2,
  'wieder': 2,
  'weg': 1,
  ...
  
aus. Dass es die Satzzeichen als Wörter interpretiert, ist so gedacht. Aber es fehlen die Umlaute und die Wörter werden an unnützen Stellen voneinander getrennt.
Benutzeravatar
__blackjack__
User
Beiträge: 14040
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Horst-Olaf: Das `occur` (ist ein obskurer Name) würde man besser mit einem `collections.Counter` lösen. Wenn man das so löst wie Du das da machst, dann bitte nicht mit ``for n in range(len(wordlist)):``. Das ist in Python ein „anti pattern“, weil man direkt über die Elemente iterieren kann, ohne den unnötigen Umweg über einen Index: ``for word in wordlist:``. Wobei Grunddatentypen nichts in Namen zu suchen haben. Einfach nur `words` statt `wordlist`. Das einzige was die Funktion davon fordert ist iterierbar zu sein.

Bei der Eingabedatei die Du weiter oben `inf` genannt hast, habe ich sofort an `math.inf` gedacht. Ist also auch kein guter Name. Keine kryptischen Abkürzungen.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Sirius3
User
Beiträge: 18269
Registriert: Sonntag 21. Oktober 2012, 17:20

@Horst-Olaf: das Problem kann ich nicht nachvollziehen, sollte die Eingabedatei das richtige Encoding haben. Also hast Du da noch ein Problem.

Code: Alles auswählen

def statistics(wordlist):
    result = dict()
    for word in wordlist:
        result[word] = result.get(word, 0) + 1
    return result
oder kurz

Code: Alles auswählen

result = collections.Counter(wordlist)
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

@__blackjack__ das inf ist von mir, und fuer mich heisst das input-file. Das hat der TE sich also nicht ausgedacht. Und Kontext matters.

@Horst-Olaf: fuer ein solches Vorhaben eignen sich neuronale Netze denke ich besser. Die erreichen erstaunlich realistische nonsense-Texte. Oder hidden markov chains wenn ich mich recht erinnere.
Benutzeravatar
__blackjack__
User
Beiträge: 14040
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Für mich heist das erst einmal gar nichts, aber `inf` aus `math` kenne ich halt. Ausserhalb ist das eine kryptische Abkürzung für mich.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Benutzeravatar
snafu
User
Beiträge: 6866
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Da die Einführung und Verwendung von inf direkt hintereinander erfolgen, sollte man auch mit der Abkürzung zurecht kommen. Schwieriger fände ich es, wenn 10 Zeilen dazwischenliegen. So oder so täte es aber nicht weh, das inf einfach als infile oder input_file auszuschreiben.
Horst-Olaf
User
Beiträge: 4
Registriert: Mittwoch 27. Februar 2019, 12:39

Hui, da bin ich euch schonmal sehr dankbar. Ich habe jetzt alles nochmal über den Haufen geworfen und euren Ratschlägen folgend überarbeitet. Das ist jetzt nur noch in Drittel des Codes, wie schön. Die Wörter werden mit dem split-Operator nicht mehr getrennt, die Umlaute sind aber immernoch im Weg. Ich habe jetzt beschlossen, sie einfach zu ersetzen. Das funktioniert aber nicht, indem ich "ü" durch "ue" ersetze, sondern nur mit:

Code: Alles auswählen

with open('bsp.txt', 'r') as myfile:
      wordlist = myfile.read().replace("ü","ue").split()
Gibt es da eine elegantere Lösung? Nach dem "split()" ist ja eine Liste erstellt worden. Kann ich dann überhaupt noch Elemente innerhalb eines Strings ersetzen?
Benutzeravatar
__blackjack__
User
Beiträge: 14040
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Horst-Olaf: Die Lösung ist beim öffnen der Datei die Kodierung anzugeben, nur dann können die Bytes aus der Datei korrekt zu Zeichen dekodiert werden. Und dann ist ein ü auch ein ü. Und man muss das dann auch nicht ersetzen, denn dann funktionieren auch die regulären Ausdrücke mit Umlauten.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Sirius3
User
Beiträge: 18269
Registriert: Sonntag 21. Oktober 2012, 17:20

@Horst-Olaf: dann war Deine Vermutung, dass es sich bei der Datei um eine ISO8859-1 kodierte handelt, offensichtlich falsch, und Du mußt nur das richtige Encoding angeben:

Code: Alles auswählen

with open('bsp.txt', 'r', encoding='utf-8') as input_file:
    words = re.findall('[a-zäöüß]+|[-.,?!_;:)(<>]+', input_file.read(), re.IGNORECASE)
Antworten