Parsen einer Textdatei

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
Alex_1979
User
Beiträge: 3
Registriert: Dienstag 17. Juli 2018, 07:09

Ich würde gerne in einer TXT Datei nach gewissen Wörtern suchen wollen und diese dann ein einer anderen Datei einfügen wollen am liebsten wäre mir als pdf aber ist kein muss. Mein erster fersuch schlägt aber schon fehl kann mir einer sagen was verkehrt ist.

Inhalt TXT:

| | DONE TEST | NAME | PARTS | PARTOF | IMPLEMENTED | DEFINED | TEXT
|D-| 100% 0% | REQ-aliens | spc-aliens | REQ-purpose | | design\purpose.toml | There shall be a way to say hello to aliens, bu...
|D-| 100% 50% | REQ-purpose | REQ-aliens, REQ-world | | | design\purpose.toml | This is a project about saying hello to lots of...
|DT| 100% 100% | REQ-world | SPC-world | REQ-purpose | | design\purpose.toml | There shall be a way to say hello to the world....
|D-| 100% 0% | spc-aliens | | REQ-aliens | C:\Req_neu\src\hello.py[10] | design\purpose.toml | There shall be a way to say hello to aliens.
|DT| 100% 100% | SPC-world | TST-world | REQ-world | C:\Req_neu\src\hello.py[3] | design\purpose.toml | The hello-world function shall print hello
|DT| 100% 100% | TST-world | | SPC-world | C:\Req_neu\tests\hello_test.py[3] | design\purpose.toml | We will make a test later.

Code:

import os
import site

input = open ('report.txt','r')
output = open ('Ergebnisse.txt','w')
x = input.read()
whitespace = ' '

def reg():
i = 0
while i < len(x):
word = x[i:i+3]
if word == 'REQ':
string= x[i:i+10]
print(string)
output.write(string)
output.write(whitespace)
i+= 1
input.close()
output.close()
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Was erwartest Du denn, was passiert? Und was passiert wirklich?

Du hast eine |-separierte Datei, die Du am besten mit dem csv-Modul einliest.
Funktionen sollten alles, was sie brauchen über ihre Argumente bekommen, input, output und x kommen einfach so rein. os und site werden importiert, aber nicht benutzt. `x` ist ein schlechter Name für den gesamten Dateiinhalt als ein String.
Benutzeravatar
__blackjack__
User
Beiträge: 13068
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Alex_1979: Du ignorierst ja komplett die Struktur der Daten. Soll denn wirklich jedes Wort in dem 'REQ' vorkommt mit seinen 10 nächsten Zeichen ausgegeben werden? Falls es nur Worte sein sollen die damit anfangen, dann hast Du Glück gehabt, das es nirgendwo *in* einem Wort vorkommt. Und was ist mit Mehrfachnennungen? Die gleichen Worte mit REQ kommen da ja mehrfach vor. Sollen die auch wirklich mehrfach ausgegeben werden? Oder vielleicht doch nur einmal? Und vielleicht auch nur aus einer bestimmten Spalte? In der TEXT-Spalte am Ende könnte ja auch ein REQ vorkommen, sollte das dort auch berücksichtigt werden?
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Die Aufgabe kann man gut in drei Schritten angehen:
1. Parsen in eine geeignete Struktur
2. Filtern der gewünschten Infos
3. Schreiben in das gewünschte Dateiformat

Für jeden Schritt kann man eine eigene Funktion definieren. Für Schritt 1 wäre das recht trivial:

Code: Alles auswählen

def parse(filename, sep='|'):
    with open(filename) as source:
        for line in source:
            yield [item.strip() for item in line.split(sep)]
Erfordert allerdings schon gewisse Vorkenntnisse in Python, gerade bei der letzten Zeile. Man könnte alternativ auch alles in eine große Liste stecken und .append() verwenden, wenn man das einfacher findet.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hier die etwas anfängerfreundlichere Variante:

Code: Alles auswählen

def parse(filename, sep='|'):
    all_items = []
    with open(filename) as source:
        for line in source:
            row_items = []
            for item in line.split(sep):
                row_items.append(item.strip())
            all_items.append(row_items)
    return all_items
Aber den Rest werde ich nicht vorkauen. Hier bitte etwas Eigeninitiative mitbringen... ;)
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Warum nicht das csv Modul benutzen? Sieht sehr nach NIHS aus.

Code: Alles auswählen

>>> data = '''| | DONE TEST | NAME | PARTS | PARTOF | IMPLEMENTED | DEFINED | TEXT
... |D-| 100% 0% | REQ-aliens | spc-aliens | REQ-purpose | | design\purpose.toml | There shall be a way to say hello to aliens, bu...'''
>>> import csv
>>> import io
>>> r =csv.reader(io.StringIO(data), delimiter="|")
>>> list(r)
[['', ' ', ' DONE TEST ', ' NAME ', ' PARTS ', ' PARTOF ', ' IMPLEMENTED ', ' DEFINED ', ' TEXT'], ['', 'D-', ' 100% 0% ', ' REQ-aliens ', ' spc-aliens ', ' REQ-purpose ', ' ', ' design\\purpose.toml ', ' There shall be a way to say hello to aliens, bu...']]
Benutzeravatar
__blackjack__
User
Beiträge: 13068
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@__deets__: Gegen CSV spricht IMHO das es sich um ausgerichteten Text handelt und es nicht wirklich CSV ist. Original sieht das ja so aus:

Code: Alles auswählen

|  | DONE TEST | NAME         | PARTS                  | PARTOF       | IMPLEMENTED                         | DEFINED              | TEXT
|D-| 100%   0% | REQ-aliens   | spc-aliens             | REQ-purpose  |                                     | design\purpose.toml  | There shall be a way to say hello to aliens, bu...
|D-| 100%  50% | REQ-purpose  | REQ-aliens, REQ-world  |              |                                     | design\purpose.toml  | This is a project about saying hello to lots of...
|DT| 100% 100% | REQ-world    | SPC-world              | REQ-purpose  |                                     | design\purpose.toml  | There shall be a way to say hello to the world....
|D-| 100%   0% | spc-aliens   |                        | REQ-aliens   | C:\Req_neu\src\hello.py[10]         | design\purpose.toml  | There shall be a way to say hello to aliens.
|DT| 100% 100% | SPC-world    | TST-world              | REQ-world    | C:\Req_neu\src\hello.py[3]          | design\purpose.toml  | The hello-world function shall print hello
|DT| 100% 100% | TST-world    |                        | SPC-world    | C:\Req_neu\tests\hello_test.py[3]   | design\purpose.toml  | We will make a test later.
Ich sehe nicht wo die Verarbeitung als CSV hier einen Vorteil gegenüber einfachem splitten bringt.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich sehe nicht, warum das dagegen spricht. Eine Zeile Code vs mehrere. Warum das Rad neu erfinden? Den einzigen echten Grund mit gepaddeten Daten anders umzugehen würde der direkte Zugriff auf Teilstrings über dann ja leicht berechenbare offsets darstellen. Das wäre aber nur in Extremfällen wirklich relevant.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Das ist jetzt aber ein bißchen wie mit den Äpfeln und den Birnen. Als Einzeiler geht es mit weniger Effekt natürlich auch:

Code: Alles auswählen

[line.split('|') for line in data.splitlines()]
Das spart sogar zwei zusätzliche Imports.
Benutzeravatar
__blackjack__
User
Beiträge: 13068
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Welche mehreren Zeilen Code?

Code: Alles auswählen

>>> x = [line.rstrip().split('|') for line in f]
>>> x[:2]
[['',
  '  ',
  ' DONE TEST ',
  ' NAME         ',
  ' PARTS                  ',
  ' PARTOF       ',
  ' IMPLEMENTED                         ',
  ' DEFINED              ',
  ' TEXT'],
 ['',
  'D-',
  ' 100%   0% ',
  ' REQ-aliens   ',
  ' spc-aliens             ',
  ' REQ-purpose  ',
  '                                     ',
  ' design\\purpose.toml  ',
  ' There shall be a way to say hello to aliens, bu...']]
Was spart mir das `csv`-Modul denn hier? Beziehungsweise besser ohne die ganzen Leerzeichen und die erste ”Spalte”:

Code: Alles auswählen

>>> x = [[cell.strip() for cell in line.split('|')][1:] for line in f]
>>> x[:2]
[['',
  'DONE TEST',
  'NAME',
  'PARTS',
  'PARTOF',
  'IMPLEMENTED',
  'DEFINED',
  'TEXT'],
 ['D-',
  '100%   0%',
  'REQ-aliens',
  'spc-aliens',
  'REQ-purpose',
  '',
  'design\\purpose.toml',
  'There shall be a way to say hello to aliens, bu...']]
CSV macht bei CSV-Dateien Sinn, insbesondere weil das Format ja nicht so einfach ist, wie es auf den ersten Blick scheint.

Wenn man hier auf Nummer sicher gehen wollen würde, würde man sich einen „fixed width parser“ schreiben der die Feldgrenzen an den '|' in der Titelzeile erkennt. Denn so wie das Format aussieht, könnte ja sogar '|' in einem Feld vorkommen ohne das man das besonders escapen müsste, solange es in der Titelzeile nur als Trenner verwendet wird.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Naja, dann stellt sich umgekehrt die Frage: warum jemals csv? Mit dem padding hat das doch alles nix was du hier zeigst. Außer eben bei Daten, bei denen Escapes oder in Anführungszeichen gepackte Strings eine Rolle spielen, könnte man dann ja immer mit einer solchen list comprehension arbeiten. Wird hier aber üblicherweise aber trotzdem zum csv Modul geraten. Eben weil plötzlich doch Randfälle auftreten, und weil sowas immer zu Fuß zu machen bedeutet, das ich den Code nachvollziehen muss, statt einfach mental abzuhaken “ok, liest ein csv ein, weiter im Text”.

Mal abgesehen davon, dass DU (und ich, etc) eine solche Vorgehensweise natürlich wählen können. Für den TE erspart die Nutzung des csv Moduls aber eine ganze Reihe von Konzepten zu erlernen, mit denen er offensichtliche noch auf dem Kriegsfuß steht. Das ist auch ein Wert.
Benutzeravatar
__blackjack__
User
Beiträge: 13068
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@__deets__: Warum `csv` habe ich ja geschrieben: Wenn man CSV hat nimmt einem das arbeit ab und produziert auch gültiges CSV das man „round trippen“ kann. Wenn man kein CSV hat, dann macht's a) keinen Sinn und b) könnte es Probleme machen wenn da Sachen vorkommen die in CSV eine Bedeutung haben. Wegen den möglichen '|' *in* Feldern würde ich hier eventuell auch eine eigene Funktion schreiben, denn da würde das `csv`-Modul ja auch drüber stolpern.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten