2017-11-03 4 views
5

Ich habe einen Code entwickelt, der für das Lesen der Wörter einer TXT-Datei verantwortlich ist, in meinem Fall "elquijote.txt", um dann ein Wörterbuch {key: value} zu verwenden erscheinen und ihre Vorkommen.Wörter aus der txt-Datei lesen - Python

Zum Beispiel für eine Datei „test1.txt“ mit den folgenden Worten:

hello hello hello good bye bye 

Der Ausgang meines Programms ist:

hello 3 
good 1 
bye 2 

, eine andere der Optionen, die das Programm hat, ist dass es jene Wörter zeigt, die eine größere Anzahl von Malen erscheinen als eine Zahl, die von uns durch ein Argument eingeführt wurde.

Wenn wir in der Shell den folgenden Befehl "python readingwords.py text.txt 2" eingeben, zeigt die in der Datei "test1.txt" enthaltenen Wörter an, die öfter als die von uns eingegebene Zahl erscheinen in diesem Fall 2

Ausgang:

hello 3 

Jetzt können wir ein drittes Argument der gemeinsamen Wörter wie Determinanten Konjunktionen einführen, die, da sie so generisch, wollen wir nicht in unserem gezeigt oder eingeführt werden Wörterbuch.

Mein Code funktioniert ordnungsgemäß, das Problem ist, dass die Verwendung von großen Dateien wie "elquijote.txt" lange dauert, um den Vorgang abzuschließen.

Ich habe nachgedacht und es ist wegen der Verwendung meiner Hilfslisten für die Beseitigung von Wörtern.

Ich habe als eine Lösung gedacht, nicht in meine Listen diejenigen Wörter, die in der txt-Datei erscheinen, die durch Argument eingegeben wird, die die zu verwerfenden Wörter enthält, einzuführen.

Hier ist mein Code:

def contar(aux): 
    counts = {} 
    for palabra in aux: 
    palabra = palabra.lower() 
    if palabra not in counts: 
     counts[palabra] = 0 
    counts[palabra] += 1 
    return counts 

def main(): 

    characters = '!?¿-.:;-,><=*»¡' 
    aux = [] 
    counts = {} 

    with open(sys.argv[1],'r') as f: 
    aux = ''.join(c for c in f.read() if c not in characters) 
    aux = aux.split() 

    if (len(sys.argv)>3): 
    with open(sys.argv[3], 'r') as f: 
     remove = "".join(c for c in f.read()) 
     remove = remove.split() 

    #Borrar del archivo 
    for word in aux: 
     if word in remove: 
     aux.remove(word) 

    counts = contar(aux) 

    for word, count in counts.items(): 
    if count > int(sys.argv[2]): 
     print word, count 

if __name__ == '__main__': 
    main() 

Contar Funktion, um die Wörter im Wörterbuch einführt.

Und Hauptfunktion führt in einer "Aux" -Liste diejenigen Wörter ein, die keine symbolischen Zeichen enthalten, und löscht dann diese "verbotenen" Wörter, die aus einer anderen .txt-Datei geladen wurden, aus derselben Liste.

Ich denke, die richtige Lösung wäre, die verbotenen Wörter zu verwerfen, wo ich Symbole verwerfen, die nicht akzeptiert werden, aber nach mehreren Versuchen habe ich es nicht richtig geschafft.

Hier können Sie meine Online-Test: https://repl.it/Nf3S/54 Dank.

+0

warum ein normales Wort nicht zählt möglicherweise mit ** collections.Counter ** und dann beseitigt Ihre unerwünschten Worte nach? Verschiebt den langsamen Code in eine kleinere Volumenschleife. –

+0

Haben Sie ein Speicherproblem? "elquijote.txt" könnte eine sehr lange Datei sein. Wenn es das vollständige Buch ist, hat es 381.104 Wörter aus einer Menge von 22.939 verschiedenen Wörtern und mehr als 2 Millionen Zeichen. Batch-Verarbeitung des Buches sollte eine gute Idee sein. – Adirio

Antwort

1

Hier gibt es ein paar Ineffizienzen. Ich habe Ihren Code neu geschrieben, um einige dieser Optimierungen zu nutzen. Die Begründung für jede Änderung sind in den Kommentaren/doc Strings:

# -*- coding: utf-8 -*- 
import sys 
from collections import Counter 


def contar(aux): 
    """Here I replaced your hand made solution with the 
    built-in Counter which is quite a bit faster. 
    There's no real reason to keep this function, I left it to keep your code 
    interface intact. 
    """ 
    return Counter(aux) 

def replace_special_chars(string, chars, replace_char=" "): 
    """Replaces a set of characters by another character, a space by default 
    """ 
    for c in chars: 
     string = string.replace(c, replace_char) 
    return string 

def main(): 
    characters = '!?¿-.:;-,><=*»¡' 
    aux = [] 
    counts = {} 

    with open(sys.argv[1], "r") as f: 
     # You were calling lower() once for every `word`. Now we only 
     # call it once for the whole file: 
     contents = f.read().strip().lower() 
     contents = replace_special_chars(contents, characters) 
     aux = contents.split() 

    #Borrar del archivo 
    if len(sys.argv) > 3: 
     with open(sys.argv[3], "r") as f: 
      # what you had here was very ineffecient: 
      # remove = "".join(c for c in f.read()) 
      # that would create an array or characters then join them together as a string. 
      # this is a bit silly because it's identical to f.read(): 
      # "".join(c for c in f.read()) === f.read() 
      ignore_words = set(f.read().strip().split()) 
      """ignore_words is a `set` to allow for very fast inclusion/exclusion checks""" 
      aux = (word for word in aux if word not in ignore_words) 

    counts = contar(aux) 

    for word, count in counts.items(): 
     if count > int(sys.argv[2]): 
      print word, count 


if __name__ == '__main__': 
    main() 
+1

Sieht aus, als hätten wir sehr ähnliche Ideen, aber du hast mich geschlagen. –

+0

In der Tat, aber ich bin froh: Sie haben mich versehentlich auf eine neue Methode eingeführt: 'translate()'. Ich bin mir nicht sicher, ob ich es hier benutzen würde (abhängig von den Daten: schlechte Interpunktion/fehlender Abstand um Interpunktion würde es brechen), aber ich kann definitiv Plätze dafür finden. Prost! – smassey

+1

Ich habe in meinem Beispiel "naiv" 'translate' verwendet, um es einfach zu halten, aber Sie könnten eine Übersetzungstabelle erstellen, die die aufgelisteten Zeichen gegen ein Leerzeichen tauscht, anstatt sie zu entfernen, wenn dies die gewünschte Funktionalität ist. –

2

Hier sind ein paar Optimierungen:

  • Verwenden Sammlungen.Counter() zum Zählen von Elementen in contar()
  • Verwenden Sie string.translate() zum Entfernen unerwünschter Zeichen
  • Pop-Elemente aus der Ignorierwortliste nach der Zählung, anstatt sie aus dem ursprünglichen Text zu entfernen.

Beschleunigt die Dinge ein wenig, aber nicht um eine Größenordnung.

#!/usr/bin/python 
# -*- coding: utf-8 -*- 
import sys 
import os 
import collections 

def contar(aux): 
    return collections.Counter(aux) 

def main(): 

    characters = '!?¿-.:;-,><=*»¡' 
    aux = [] 
    counts = {} 

    with open(sys.argv[1],'r') as f: 
    text = f.read().lower().translate(None, characters) 
    aux = text.split() 

    if (len(sys.argv)>3): 
    with open(sys.argv[3], 'r') as f: 
     remove = set(f.read().strip().split()) 
    else: 
    remove = [] 

    counts = contar(aux) 
    for r in remove: 
    counts.pop(r, None) 

    for word, count in counts.items(): 
    if count > int(sys.argv[2]): 
     print word, count 

if __name__ == '__main__': 
    main() 
1

Ein paar Änderungen und Argumentation:

  1. Parse Befehlszeilenargumente unter __name__ == 'main': Auf diese Sie Modularität des Codes erzwingen, weil es nur für Zeilenargumente Befehl fragt, wenn Sie dieses Skript ausführen im Gegensatz zum Importieren der Funktion von einem anderen Skript.
  2. Verwenden Sie Regex zum Herausfiltern von Wörtern mit Zeichen, die Sie nicht möchten: Mit Regex können Sie entweder sagen, welche Zeichen Sie wollen oder welche Zeichen Sie nicht wollen, je nachdem, was kürzer ist. In diesem Fall ist das Festcodieren jedes Sonderzeichens, das Sie nicht wollen, eine ziemlich langweilige Aufgabe, verglichen mit der Erklärung, welche Zeichen Sie in einem einfachen Regex-Muster haben wollen. Im folgenden Skript filtere ich Wörter, die nicht alphanumerisch sind, mit dem Muster [aA-zZ0-9]+.
  3. Bitten Sie um Verzeihung vor der Erlaubnis: Da die minimale Anzahl Befehlszeilenargument optional ist, ist es offensichtlich nicht immer vorhanden. Daher können wir Python sein, indem wir tryexcept Blöcke verwenden, um zu versuchen, die minimale Anzahl als sys.argv[2] zu definieren und die Ausnahme von IndexError zu finden, um die minimale Anzahl standardmäßig auf 0 zu setzen.

Python-Skript:

# sys 
import sys 
# regex 
import re 

def main(text_file, min_count): 
    word_count = {} 

    with open(text_file, 'r') as words: 
     # Clean words of linebreaks and split 
     # by ' ' to get list of words 
     words = words.read().strip().split(' ') 

     # Filter words that are not alphanum 
     pattern = re.compile(r'^[aA-zZ0-9]+$') 
     words = filter(pattern.search,words) 

     # Iterate through words and collect 
     # count 
     for word in words: 
      if word in word_count: 
       word_count[word] = word_count[word] + 1 
      else: 
       word_count[word] = 1 

    # Iterate for output 
    for word, count in word_count.items(): 
     if count > min_count: 
      print('%s %s' % (word, count)) 

if __name__ == '__main__': 
    # Get text file name 
    text_file = sys.argv[1] 

    # Attempt to get minimum count 
    # from command line. 
    # Default to 0 
    try: 
     min_count = int(sys.argv[2]) 
    except IndexError: 
     min_count = 0 

    main(text_file, min_count) 

Textdatei:

hello hello hello good bye goodbye !bye bye¶ b?e goodbye 

Befehl:

python script.py text.txt 

Ausgang:

bye 1 
good 1 
hello 3 
goodbye 2 

Mit Minimalzählwert Befehl:

python script.py text.txt 2 

Ausgang:

hello 3 
Verwandte Themen