2016-05-05 7 views
1

Ich versuche, die folgende Textdatei in ein Wörterbuch zu legen, aber ich möchte, dass jeder Abschnitt mit '#' beginnt oder leere Zeilen ignoriert werden.Python: Textdatei in dict lesen und Kommentare ignorieren

Meine Textdatei sieht wie folgt aus etwas:

# This is my header info followed by an empty line 

Apples   1    # I want to ignore this comment 
Oranges   3    # I want to ignore this comment 

#~*~*~*~*~*~*~*Another comment~*~*~*~*~*~*~*~*~*~* 

Bananas   5    # I want to ignore this comment too! 

Meine gewünschte Ausgabe wäre:

myVariables = {'Apples': 1, 'Oranges': 3, 'Bananas': 5} 

Mein Python-Code lautet wie folgt:

filename = "myFile.txt" 
myVariables = {} 

with open(filename) as f: 
    for line in f: 
     if line.startswith('#') or not line: 
      next(f) 

     key, val = line.split() 
     myVariables[key] = val 
     print "key: " + str(key) + " and value: " + str(val) 

Der Fehler, den ich bekommen :

Traceback (most recent call last): 
    File "C:/Python27/test_1.py", line 11, in <module> 
    key, val = line.split() 
ValueError: need more than 1 value to unpack 

Ich verstehe den Fehler, aber ich verstehe nicht, was mit dem Code falsch ist.

Vielen Dank im Voraus!

+0

Haben Sie die Kontrolle über das Textdateiformat? wenn ja, vielleicht PyYAML verwenden? –

+0

Leider nicht. Das Textformat ist das, wozu ich beauftragt wurde. – SilverEyes9

+0

Haben Sie die Antworten überprüft? Ein paar gute Ansätze wurden gegeben. Gehen Sie durch sie und fragen Sie, ob Sie weitere Informationen benötigen. – Pouria

Antwort

0

Dieser Ihre Fehler nicht genau reproduzieren, aber es gibt ein Problem mit Ihrem Code:

>>> x = "Apples\t1\t# This is a comment" 
>>> x.split() 
['Apples', '1', '#', 'This', 'is', 'a', 'comment'] 
>>> key, val = x.split() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
ValueError: too many values to unpack 

Stattdessen versuchen:

key = line.split()[0] 
val = line.split()[1] 

Edit: und ich denke, Sie „mehr als 1 Wert brauchen auspacken "kommt aus den leeren Zeilen. Außerdem bin ich nicht vertraut mit next() wie folgt. Ich glaube, ich würde so etwas wie:

if line.startswith('#') or line == "\n": 
    pass 
else: 
    key = line.split()[0] 
    val = line.split()[1] 
+0

Ich habe gerade next() benutzt, weil jemand es in einem anderen Post vorgeschlagen hat. Ich werde versuchen und pass stattdessen verwenden. Vielen Dank! – SilverEyes9

0

Sie müssen sich mit # Spaltung entweder die restlichen Zeilen nach der Trennung auf # oder mit rfind wie unten schneiden die Zeichenfolge, eine leere Zeile, die Leerzeilen und Zeilen ignorieren wird eine neue Linie, so dass Sie and line.strip() für ein überprüfen müssen, können Sie nicht nur auf die Leerzeichen geteilt und entpacken Sie, wie Sie mehr als zwei Elemente nach der Trennung haben einschließlich dessen, was im Kommentar ist:

with open("in.txt") as f: 
    d = dict(line[:line.rfind("#")].split() for line in f 
       if not line.startswith("#") and line.strip()) 
    print(d) 

Ausgang:

{'Apples': '1', 'Oranges': '3', 'Bananas': '5'} 

Eine weitere Option ist zweimal und in Scheiben schneiden zu spalten:

with open("in.txt") as f: 
    d = dict(line.split(None,2)[:2] for line in f 
       if not line.startswith("#") and line.strip()) 
    print(d) 

oder Spaltung zweimal und Auspacken eine explizite Schleife:

with open("in.txt") as f: 
    d = {} 
    for line in f: 
     if not line.startswith("#") and line.strip(): 
      k, v, _ = line.split(None, 2) 
      d[k] = v 

Sie auch itertools.groupby zur Gruppe verwenden können die Linien, die Sie wollen.

from itertools import groupby 
with open("in.txt") as f: 
    grouped = groupby(f, lambda x: not x.startswith("#") and x.strip()) 
    d = dict(next(v).split(None, 2)[:2] for k, v in grouped if k) 
    print(d) 

zu behandeln, in denen wir mehrere Wörter in einfache Anführungszeichen haben, können wir aufzuspalten shlex verwenden:

import shlex 
with open("in.txt") as f: 
    d = {} 
    for line in f: 
     if not line.startswith("#") and line.strip(): 
      data = shlex.split(line) 
      d[data[0]] = data[1] 

print(d) 

So Änderung der Bananen-Linie:

Bananas   'north-side disabled'    # I want to ignore this comment too! 

Wir erhalten:

{'Apples': '1', 'Oranges': '3', 'Bananas': 'north-side disabled'} 

Und das gleiche wird für das Aufschneiden arbeiten:

with open("in.txt") as f: 
    d = dict(shlex.split(line)[:2] for line in f 
       if not line.startswith("#") and line.strip()) 
    print(d) 
+0

Hallo Padraic! Ihre 'Split-Double und Slice'-Methode hat am besten für mich funktioniert. Ich habe vergessen zu erwähnen, dass einige zweite Spaltenwerte manchmal einfache Anführungszeichen um einen String haben, wie zum Beispiel: 'Nordseite deaktiviert'. – SilverEyes9

+0

@ SilverEyes9, shlex kümmert sich darum für Sie, siehe die Bearbeitung. –

+0

Danke, Sir! Das hat total geholfen! Das Werkzeug funktioniert! – SilverEyes9

1

Gegeben Ihren Text:

text = """ 
# This is my header info followed by an empty line 

Apples   1    # I want to ignore this comment 
Oranges   3    # I want to ignore this comment 

#~*~*~*~*~*~*~*Another comment~*~*~*~*~*~*~*~*~*~* 

Bananas   5    # I want to ignore this comment too! 
""" 

Wir dies auf 2 Arten tun können. Verwenden Sie regex oder Python-Generatoren verwenden. Ich würde letzteres wählen (unten beschrieben), da regex in solchen Fällen nicht besonders schnell (er) ist.

die Datei zu öffnen:

with open('file_name.xyz', 'r') as file: 
    # everything else below. Just substitute `for line in lines` with 
    # `for line in file.readline()` 

nun ein ähnliches zu schaffen, können wir die Zeilen aufgeteilt, und erstellen Sie eine Liste:

lines = text.split('\n') # as if read from a file using `open`. 

Hier ist, wie wir alle wollen Sie in ein paar Zeilen:

# Discard all comments and empty values. 
comment_less = filter(None, (line.split('#')[0].strip() for line in lines)) 

# Separate items and totals. 
separated = {item.split()[0]: int(item.split()[1]) for item in comment_less} 

Lets Test:

>>> print(separated) 
{'Apples': 1, 'Oranges': 3, 'Bananas': 5} 

Hoffe, das hilft.

0

Kommentare strippen, könnten Sie str.partition() verwenden, die funktioniert, ob das Kommentarzeichen vorhanden ist oder nicht in der Zeile ist:

for line in file: 
    line, _, comment = line.partition('#') 
    if line.strip(): # non-blank line 
     key, value = line.split() 

line.split() kann heben eine Ausnahme in diesem Code zu-es passiert, wenn es eine nicht ist -Blank-Zeile, die nicht genau zwei durch Leerzeichen getrennte Wörter enthält - es hängt von der Anwendung ab, was Sie in diesem Fall tun möchten (ignorieren Sie solche Zeilen, drucken Sie Warnungen usw.).

+0

Netter Ansatz. Ich hätte nicht an 'str.partition() 'für dieses spezielle Problem gedacht. – Pouria

0

Wenn das Format der Datei korrekt definiert ist, können Sie eine Lösung mit regulären Ausdrücken versuchen. Hier ist nur eine Idee:

import re 

fruits = {} 
with open('fruits_list.txt', mode='r') as f: 
    for line in f: 
     match = re.match("([a-zA-Z0-9]+)[\s]+([0-9]+).*", line) 
     if match: 
      fruit_name, fruit_amount = match.groups() 
      fruits[fruit_name] = fruit_amount 


print fruits 

AKTUALISIERT: ich die Art und Weise des Lesens Linie geändert von großen Dateien zu kümmern. Jetzt lese ich Zeile für Zeile und nicht alles in einem. Dies verbessert die Speichernutzung.

+0

Nicht wirklich eine gute Idee. Zu Beginn kann es im Text Sonderzeichen geben. Selbst wenn das nicht der Fall ist, ist "Regex" normalerweise nicht der beste Weg, um diese Probleme anzugehen, da es in solchen Fällen langsamer ist als die Alternativen (entgegen der allgemeinen Ansicht). Der Durchschnitt von 10k-Schleifen, die diese Lösung verwenden, benötigt 12,5 Mikrosekunden für den gegebenen Text, und bei Verwendung meiner Lösung (Python-Generatoren) dauert es 8,5 Mikrosekunden. Für einen längeren Text würde der Unterschied viel auffälliger werden. Etwas zum Nachdenken? – Pouria

+0

Ja, ich habe es tatsächlich Zeile für Zeile gemessen, da ich es auf 'text.split ('\ n')' gemacht habe, wobei 'text' das Stück Zeichenfolge ist, das ich von hier kopiert habe. Also bleibt meine Beschwerde bestehen. Obwohl das Lesen Zeile für Zeile ein anderes Problem einführte (nicht dass ich denke, dass es in diesem Zusammenhang von Bedeutung wäre); Das ist die Tatsache, dass Python die Datei nach 30 Sekunden automatisch schließt. Ich denke also, eine andere Lösung für große Daten wäre, die Datei in Blöcken zu lesen und gleichzeitig extrahierte Daten in eine CSV oder JSON zu schreiben. Die neue Datei wäre kleiner (keine Kommentare oder Leerzeichen) und wesentlich schneller zu parsen. Nur ein Gedanke! – Pouria

+0

@PouriaHadjibagheri Wo finde ich Informationen zum Problem des automatischen Schließens? Tritt es immer noch ein, wenn das Skript innerhalb von 30 Sekunden aus der Datei liest? Oder kurz nach 30 sek. von Inaktivität? Das Lesen von Stücken klingt in sehr großen Dateien gut! – Hamlett