[gelöst] Newbe brauch Hilfe

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
rethus
User
Beiträge: 26
Registriert: Montag 7. August 2006, 19:53

Montag 7. August 2006, 20:07

Hallo, ich bin ein absoluter Newbe in Sachen python, und würde mich freuen, wenn Ihr mir bei einem kleinen Script unter die Arme greifen könntet....

Ich habe einen ellen langen Text, der schematisch immer gleich aufgebaut ist.
Eine Headline (ausschließlich in Großbuchstaben),
gefolgt von x Zeilen Beschreibung.

Also folgendes Textkonstrukt soll mal als Beispiel dienen:
ABSORTPOS
Attribut:KLPO_AbsortierPosition
Definiert durch Basisattribut:AbsortierPosition
Definition Basisattribut:Positionsnummer einer Absortiernummer (ABSORTNR). Während die Absortier-nummer vom HOST kommt, wird die Positionsnummer fortlaufend zu einer Absortier-nummer vom LVS erzeugt. Positionstrennung während KommVorbereitung.
Einschränkung Wertebereich:Fortlaufende Positionsnummer zu einer Absortiernummer.
Diese Textbausteine sollten dann im Endeffekt in eine Tabelle gepackt werden:
<table>
<tr><th colspan=2> <Überschrift> </th></tr>
<tr>
<td><Beschreibung1></td>
<td><Text1></td>
</tr>
<tr>
<td><Beschreibung2></td>
<td><Text2></td>
</tr>
</table>
die einzigen verläßlichen anhaltspunkte sind die doppelpunkte als Zeilentrenner, und die ausschließlich groß geschriebene Headline, sowie der Umstand, das bei einer neuen Beschreibung zwingend ein \n vorangeht....

Problematisch wird die Sache allerdings, weil teilweise die Beschreibungen (oben als <Text1> und <Text2> angegeben) mehrzeilig sind, und dummerweise - zwar selten, aber es kommt vor - auch Zeilenumbrüche (also \n) enthalten.

Bisher bin ich noch nicht soweit....

Folgendes hab ich bereits zusammengetragen:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso8859-1 -*-

from sys import argv, exit
from os import getcwd
import re

if len(argv) < 4:
    print '\nBitte geben Sie die Namen der Ein- und Ausgabedateien in dem Format "convert.py text1.txt text2.txt" ein,\n gefolgt von dem Tabellennamen (TNLxxx); Abbruch.\n'
    exit(1)

f=file(getcwd()+"/"+argv[1],"r")
a=f.readlines()
f.close()

c=""

for i in a:

#    if i.find("Einschränkung Wertebereich") != -1:
#       i += "</td></tr></table><table><tr><td>\n"
#    c += i 
    
f=file(getcwd()+"/"+argv[2],"w")
f.write(c)
f.close()
Wäre super, wenn Ihr mir da helfen könntet.
Zuletzt geändert von rethus am Dienstag 8. August 2006, 14:08, insgesamt 1-mal geändert.
Joghurt
User
Beiträge: 877
Registriert: Dienstag 15. Februar 2005, 15:07

Montag 7. August 2006, 20:44

Wer das Format verbrochen hat, gehört bestraft...

Hier ist ein schnell zusammengehackte Lösung, vielleicht hilft sie dir :

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: latin-1 -*-

import sys

class Formatter(object):
    def __init__(self, out):
        self.out = out
        self.in_description = False
        self.in_table = False
    def format(self, line):
        if line.isupper():
            # KOMPLETT GROSSBUCHSTABEN
            self.finish_block()
            if self.in_table:
                self.out.write("</tr></table>\n")
            self.in_table = True
            self.out.write("""
<table>
 <tr>
  <th colspan="2">%s</th>
 </tr>
 <tr>""" % line.capitalize())
        elif ":" in line:
            self.finish_block()
            self.in_description = True
            beschreibung, text = line.split(":")
            self.out.write("<td>%s</td><td>%s" % (beschreibung, text))
        else:
            self.out.write(line)
    def finish_block(self):
        if self.in_description:
            self.out.write("</td></tr>")
        self.in_description = False
    def finish(self):
        self.finish_block()
        if self.in_table:
            self.out.write("</tr></table>\n")
    def __done__(self):
        self.finish()


infile = open(sys.argv[1], "rt")
outfile = open(sys.argv[2], "wt")

formatter = Formatter(outfile)

for line in infile:
    formatter.format(line)

formatter.finish()
infile.close()
outfile.close()

BlackJack

Montag 7. August 2006, 23:35

rethus hat geschrieben:

Code: Alles auswählen

f=file(getcwd()+"/"+argv[1],"r")
`getcwd()` ist nicht notwendig, es schadet sogar wenn man das Programm mit einem absoluten Pfad als Argument startet! Angenommen ich befinde mich in `/home/marc` und rufe das Programm mit `/tmp/test.txt` auf, dann versucht es `/home/marc//tmp/test.txt` zu öffnen.

Hier mal ein Anfang zur Problemlösung:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division
import sys

def is_headline(line):
    """Tests if line is a headline."""
    return line.upper() == line


class BlockIterator(object):
    """Iterator over blocks delimited by headlines.
    
    Returned items are tuples with headline and iterator over lines of the body
    until the next headline.
    
    lines : iterator over strings
        Lines with headlines and body lines.
    headline : string
        The current headline.
    stop : bool
        Flag that indicates that this iterator is exhausted.
    """
    def __init__(self, lines):
        self.lines = iter(lines)
        self.headline = self.lines.next()
        assert is_headline(self.headline)   # First line is headline?
        self.stop = False
    
    def __iter__(self):
        return self
    
    def next(self):
        def iter_body():
            while True:
                try:
                    line = self.lines.next()
                except StopIteration:
                    self.stop = True
                    raise
                if is_headline(line):
                    self.headline = line
                    break
                else:
                    yield line

        if self.stop:
            raise StopIteration()
        else:
            return (self.headline, iter_body())


def iter_description(lines):
    """Iterates over lines and returns iterable over key, value tuples.
    
    Lines are splitted at colons into keys and values.  If a line doesn't
    contain a colon the entire line will be added to the last value.
    """
    key, value = None, None
    for line in lines:
        splitted = line.split(':', 1)
        if len(splitted) == 1:
            value += line
        else:
            if key is not None:
                yield key, value
            key, value = splitted
    if key is not None:
        yield key, value


def write_table(blocks, out_file):
    out_file.write('<table border="1">')
    for headline, body in blocks:
        out_file.write('<tr><td colspan="2">%s</td></tr>' % headline)
        for key, value in iter_description(body):
            out_file.write('<tr><td>%s</td><td>%s</td></tr>' % (key, value))
    out_file.write('</table>')


def main():
    in_file = open('test.txt')
    write_table(BlockIterator(in_file), sys.stdout)
    in_file.close()


if __name__ == '__main__':
    main()
Ich muss sagen Du hast Dir da etwas ausgesucht, was nicht besonders Anfängerfreundlich ist. :-)
rethus
User
Beiträge: 26
Registriert: Montag 7. August 2006, 19:53

Dienstag 8. August 2006, 07:54

Vielen Dank euch beiden schon mal für die ultra schnelle Hilfe.
Ich werde mich direkt mal dran setzen und das ausprobieren.

@BlackJack:
Naja, ausgesucht hab ich es mir nicht unbedingt, ich brauche das für die Umformatierung eines Handbuchs... Hätte auch lieber mit was einfacherem angefangen.... Aber ich bin zuversichtlich, das ich mir da mit Eurer Hilfe gut durchfuchsen kann.... und natürlich nebenbei noch einiges lerne....

Also ich meld mich dann gleich nochmal, ob alles geklappt hat...
rethus
User
Beiträge: 26
Registriert: Montag 7. August 2006, 19:53

Dienstag 8. August 2006, 08:24

So, hier nun die Rückmeldungen....

@Jogurt:
Beim starten des Scripts gibt es folgenden Fehler:
./con.py text2.txt text3.txt
Traceback (most recent call last):
File "./con.py", line 49, in ?
formatter.format(line)
File "./con.py", line 27, in format
beschreibung, text = line.split(":")
ValueError: too many values to unpack
################geändert###############
@BlackJack:
Dein Script startet und gibt auf "stdout" den modifizierten Text aus.
1. Am ende der Ausgabe erscheint folgende Fehlermeldung:
Traceback (most recent call last):
File "./con1.py", line 88, in ?
main()
File "./con1.py", line 83, in main
write_table(BlockIterator(in_file), sys.stdout)
File "./con1.py", line 76, in write_table
for key, value in iter_description(body):
File "./con1.py", line 63, in iter_description
value += line
TypeError: unsupported operand type(s) for +=: 'NoneType' and 'str'

Habe festgestellt, das der oben beschriebene Fehler aufgetreten ist, weil in den Quelldaten ein ":" hinter Attribut fehlte.... das scheint das Script gesprengt zu haben.... wenn ich den ":" hinzufüge, läuft es sauber durch....
###############################
2. Das Tabellenkonstukt das ausgegeben wurde, ist schon sehr nah dran, allerdings ist da ein Fehler....
Die Beschreibung der letzten Zeile einer Tabelle enthält manchmal (nicht immer) noch einen Zeilenumbruch. Dadurch schreibt dann das Script die abschließenden Sätze der letzten Zeile als Tabellen-Header (<th></th>) in die neue Tabelle. Ausserdem kann in einer BEschreibung auch hier und da mal ein ausschließlich groß geschriebenes Wort vorkommen.
Zuletzt geändert von rethus am Dienstag 8. August 2006, 12:02, insgesamt 4-mal geändert.
rethus
User
Beiträge: 26
Registriert: Montag 7. August 2006, 19:53

Dienstag 8. August 2006, 10:45

also hinter jedem Textabsatz kommt noch ein \n...
Das wird leider als eigene Zeile jeder neuen Tabelle vorangestellt, so das ich erst die leere Zeile habe, und dann die Tabellenüberschrift....

Kann man das noch rausziehen?

@BlackJack
Was ich noch nicht kanz kapiere ist folgendes:
wenn ich die Zeile:

Code: Alles auswählen

out_file.write('</table>')
gegen

Code: Alles auswählen

out_file.write('</table><a href="#top">oben</a>')
ersetze, macht er diese Ersetzung nicht bei jeder Tabelle, sondern nur ganz am Ende... aber einen anderen Tag "</table>" hab ich nirgends in deinem Script gefunden?!?
rethus
User
Beiträge: 26
Registriert: Montag 7. August 2006, 19:53

Dienstag 8. August 2006, 14:31

Nach etwas hin und her tüftelei habe ich nun meine Probleme gelöst.
Ich danke Euch für die Hilfe....

Phyton ist schon ne feine Sache... ..ich denke, da werde ich mich mal etwas mehr mit beschäftigen.

Vielleicht könnt Ihr mir noch einen Tip geben:
Ich möchte jede Tabellenüberschrift (diese großgeschriebenen Wörter) am Anfang der Seite Verlinken.
Also am Anfang der Seite ist dann eine Tabelle in der nur alle Überschriften stehen, die man dann anklicken kann, um zum entsprechenden Text zu kommen.

Die Verlinkungen (also Anker bzw. Sprungmarken) hab ich schon eingefügt:

Code: Alles auswählen

def write_table(blocks, out_file):
    
    for headline, body in blocks:
        out_file.write('<br><table class="table" border="0"><a name="%s"></a> <col width="128*"> <col width="400*">' % headline)
        out_file.write('<tr><th colspan="2">%s</th></tr>' % headline)
        for key, value in iter_description(body):
            out_file.write('<tr><td>%s</td><td>%s</td></tr>' % (key, value))
        out_file.write('</table><p align="right"><a href="#top">nach oben</a></p>')
In diesem Programmblock kann ich ja dann "headline" herrausziehen, und in eine art globalen Array speichern, den ich dann in das Tabellenkonstrukt parse.

Ich würde dann zuvor die Anzahl der Überschriften im array zählen, und dementsprechend viele Spalten machen, in dem die Überschriften nach der Reihenfolge (q. Spalte, 2.Spalte etc.) aufgelistet werden.
Joghurt
User
Beiträge: 877
Registriert: Dienstag 15. Februar 2005, 15:07

Dienstag 8. August 2006, 17:47

rethus hat geschrieben:Beim starten des Scripts gibt es folgenden Fehler:
./con.py text2.txt text3.txt
Traceback (most recent call last):
File "./con.py", line 49, in ?
formatter.format(line)
File "./con.py", line 27, in format
beschreibung, text = line.split(":")
ValueError: too many values to unpack
Dieser Fehler tritt auf, wenn zwei Doppelpunkte in einer Zeile sind (was aus deinen Daten nicht ersichtlich war). Du kannst split aber sagen, nach dem ersten Doppelpunkt aufzuhören.

Code: Alles auswählen

beschreibung, text = line.split(":", 1)
Antworten