Textfile auslesen

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.
9ff
User
Beiträge: 1
Registriert: Freitag 13. Januar 2017, 01:45

Hi Leute

Ich sollte eine txt Datei auslesen in welcher sich die Bildinformationen eines .jpg befinden. Das txt File wurde mit Imagemagick erzeugt und es sieht wie folgt aus:

Code: Alles auswählen

# ImageMagick pixel enumeration: 300,168,255,srgb
0,0: (23,22,2)  #171602  srgb(23,22,2)
1,0: (34,33,13)  #22210D  srgb(34,33,13)
2,0: (18,20,0)  #121400  srgb(18,20,0)
3,0: (32,34,13)  #20220D  srgb(32,34,13)

Mein Ziel ist es für jede Zeile die RGB Werte in separate Variablen zu speichern.
Soweit ich es beurteilen kann muss ich das mit Regex machen. Ist das richtig oder gibt es dazu eine einfachere Lösung?
Die RGB Werte der ersten Zeile sind zum Beispiel (23,22,2) ich müsste wohl mit Hilfe der Regex Funktion den ersten String nach der Klammer bis zum ersten Koma in eine Variable speichern und dann den zweiten String nach dem ersten Koma bis zum darauf folgenden Koma in die zweite Variable speichern und dann den dritten string zwischen dem zweiten Koma und der schliessenden Klammer in die dritte Variable speichern.

Ich habe schon einige Tutorials sowie die Anleitung über Reguläre Ausdrücke durchgelesen aber für mich ist dieses Thema immer noch ein Buch mit sieben Siegeln.

Grüsse und danke
Markus
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@9ff: Das geht auch ohne reguläre Ausdrücke und ohne Koma ... :wink:

Code: Alles auswählen

from __future__ import print_function

def get_rgb(filename):
    with open(filename) as f:
        for line in f:
            if not line.startswith('#'):
                yield line.split()[1][1:-1].split(',')

for r, g, b in get_rgb('rgb.data'):
    print(r, g, b)
sebastian0202
User
Beiträge: 168
Registriert: Montag 9. Mai 2016, 09:14
Wohnort: Berlin

Du könntest auch jede Zeile aufsplitten.
Die Leerzeichen bieten sich dafür super an.
Zur Not kannst du auch doppelte Leerzeichen vorher auf ein Leerzeichen kürzen.
Dann hast du vorher noch eine Liste in der du alle RGB's anhängst.
Fertig.
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

meine überlegung schaut so aus

Code: Alles auswählen

line = '0,0: (23,22,2)  #171602  srgb(23,22,2)'
posx, posy = eval(line.split(':')[0])
red, green, blue = eval(line.split(' ')[1])
empty Sig
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@harryberlin: Deine Überlegungen gehen in die falsche Richtung. Wenn schon, sollte statt eval ast.literal_eval verwendet werden:

Code: Alles auswählen

import ast
line = '0,0: (23,22,2)  #171602  srgb(23,22,2)'
pos, rgb = map(ast.literal_eval, line.split(': '))
Da es sich aber überhaupt nicht um Python-Ausdrücke handelt, sollte man sie auch nicht wie solche behandeln.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

harryberlin hat geschrieben:meine überlegung schaut so aus
Und dann kommt der liebe Kollege und schleust Dir irgendwo die folgende Zeile ein: '7,0: globals().clear() #171602 srgb(23,22,2)'. Falls zuvor irgendwo subprocess importiert wurde, kann ich mir auch noch ganz andere Sachen vorstellen.
Mit der Lösung von Sirius3 bist Da auf der sicheren Seite. Besser aber ist es, Daten nicht als ausführbar zu interpretieren.
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

definiert mal bitte genauer was dann besser ist.
konnte jetzt nur finden, dass es eine exception gibt, wenn was nicht passt. aber was alles zulässig ist, wo steht das?
empty Sig
BlackJack

@harryberlin: `eval()` wertet Python-Ausdrücke aus. *Alle* Python-Ausdrücke. Das steht in der Dokumentation zu `eval()` Wo steht das es nur Tupel auswertet?
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

dann habe ich den wirklichen nutzen von eval() bisher nicht richtig gekannt.
keine ahnung wo es steht, deswegen auch meine frage.
ich war der annahme eval() evaluiert den input ins übliche format.
nutzte es bisher zum "importieren" eines dict.
dass auch actions erkannt werden und zudem auch gleich ausgeführt werden, war mir nicht bekannt. bzw. soweit habe ich nicht gedacht.
empty Sig
BlackJack

@harryberlin: Du hast keine Ahnung das es eine Python-Dokumentation gibt in der die Sprache, die eingebauten Funktionen, und die Standardbibliothek dokumentiert sind? Da gibt es auch eine Beschreibung was eval() macht, inklusive Hinweis auf `ast.literal_eval()` wenn man eine *sichere* Auswertung von literalen Python-Werten braucht.

Wobei das an sich schon ein Warnzeichen sein sollte, denn literale Python-Werte sind kein Format das man zur Datenspeicherung ausserhalb vom Quelltext selbst verwendenden sollte. Du solltest also gar keinen Anwendungsfall dafür haben mit `eval()` bzw. `ast.literal_eval()` Python-Wörterbücher zu parsen. Was immer Du da machst, lös das anders. Zum Beispiel mit JSON.

Den wirklichen Nutzen von `eval()` habe ich auch noch nicht erkannt. Wie bei ``global`` und noch mehr bei ``nonlocal`` ist es mir ein Rätsel warum solche dummen, gefährlichen Sachen existieren.
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

ich schreibe am besten nix mehr, bzw. gebe fehler zu.
deine herablassende interpretationsweise ist irgendwie unsymphatisch.
wie oft haben wir jetzt schon diskutiert, ich weiß dass es eine doku gibt. nur fällt es mir schon etwas schwer diese zu verstehen.
empty Sig
BlackJack

Die Doku zu `eval()` ist nicht besonders lang und enthält „The expression argument is parsed and evaluated as a Python expression […]“ und „See ast.literal_eval() for a function that can safely evaluate strings with expressions containing only literals.“ und ein Beispiel das deutlich macht, dass die Zeichenkette mehr als nur literale Werte enthalten kann. Das Beispiel ist nicht nur eine Addition sondern auch noch eine die zeigt, dass man in der Zeichenkette auf vorher definierte Namen zugreifen kann. Ausdruck (expression) hat einen Eintrag im Glossar: https://docs.python.org/2/glossary.html#term-expression

kbr hat ein Beispiel gegeben was man hier im konkreten Code untergejubelt bekommen könnte. Ein anderes Beispiel was gerne genommen wird, ist etwas was dem Benutzer Daten zerstört. Linux- und MacOS-Benutzer müssen da vor Klassikern wie '__import__("os").system("rm -rf ~")' Angst haben.
Benutzeravatar
pyHoax
User
Beiträge: 84
Registriert: Donnerstag 15. Dezember 2016, 19:17

Hallo Harry aus Berlin,

Es gibt den geläufigen Spruch 'eval is evil' .. dieser gilt nicht nur für Python sondern auch für Javascript (und wohl die meisten Sprachen mit diesem Befehl).
Eval erlaubt die Ausführung von beliebigen Pythoncode aus einen String heraus. Wenn du den String aus einer Usereingabe (auch eine Datei ist in der Regel eine Usereingabe) übernimmst und durch eval() ausführen läßt kann dir der User Code zum Ragnarok (Weltuntergang) unterjubeln.
Ich hoffe du hast Backups deiner Daten und eine Rechtschutzersicherung für die Klagen wegen Kinderpornographie die deine Software unabsichtlich verteilst .. etc... (obwohl bei der Verwendung von eval dann durch Experten wie uns schon Vorstatz unterstellt wird.)

Als professionieller Entwickler kann ich daher nur sagen, verwende kein 'eval' es sei denn du kannst erklären was die Funktion machst.
Da du ein Anfägner bist und nicht weist was eval() macht.. kommt hier der sofort Aufschrei 'No Eval' wie ein Reflex... klinkt komisch ? Ist aber so !

@BlackJack
Ich habe zwar bisher nicht eval sondern die etwas mächtigeren compile und exec verwendet.
Ich musste einen Datenvalidator schreiben (wie ein jsonvalidator nur nicht auf json daten) der typischerweise eine Reihe von sehr kleinen Checkfunktionen ausführt. Der Validator ist aber nun nicht fest verdrahtet sondern wird über einen Json Input mit Validatorregeln konfiguriert.
Um den beachtlichen Overhead von Funktionsaufrufen zu den Validatorfunktionen zu eliminieren überetze ich den Stapel an dynamisch generierten Instruktionen für den Validatorknoten bei der Initialisierung mit compile und führe das ganze mit exec aus.

Das erklärt natürlich nicht war um eval(), denn ich verwende es eigentlich nur um die Performance eines kritischen Codeteils unter Berücksichtung der Cpython implementations zu optimieren. Es geht natütlich auch ohne .. und mit pypy ist dieser Hack auch sinnlos.
nezzcarth
User
Beiträge: 1635
Registriert: Samstag 16. April 2011, 12:47

kbr hat geschrieben:@9ff: Das geht auch ohne reguläre Ausdrücke und ohne Koma ... :wink:
Ich mag Lösungen, die einzig mit den Python Stringoperationen arbeiten oft nicht so gerne, da sie voraussetzen, dass die Daten immer exakt so strukturiert sind, wie sie im Beispiel vorliegen. Die Verarbeitungslogik ist sozusagen hard-coded und wenig flexibel. Ein Leerzeichen zu viel, und dein Iterator kommt durcheinander. Wenn es darum geht, ein stabiles Skript zu schreiben, wären reguläre Ausdrücke meiner Meinung nach doch eine bessere Wahl.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@nezzcarth: einen flexiblen regulären Ausdruck zu schreiben ist noch unverständlicher, als einen festen. "\s*(\d+)\s*,\s*(\d+)\s*:\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)"
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@nezzcarth: Ich verwende reguläre Ausdrücke eigentlich erst dann, wenn es mit Stringoperationen nicht mehr geht oder zu umständlich wird. Sicher hast Du recht, was Einschränkungen der Formatierung des zu lesenden Datenformats angeht. Wird dieses maschinell erzeugt, so ist das aber kein Problem. Ansonsten betrachte ich es eher als feature, wenn das Programm abbricht, sobald der Input nicht stimmt. Erst wenn das lästig wird, würde ich mir mehr Mühe geben.
Benutzeravatar
pyHoax
User
Beiträge: 84
Registriert: Donnerstag 15. Dezember 2016, 19:17

Hab mich aus reiner Lust auch mal an dieser Aufgabe versucht und etwas ubers Zeil hinaus geschossen...

[codebox=python file=magick.py ]
import re
from collections import namedtuple

Point2D = namedtuple('Point2D', ['x', 'y'])
RGBColor = namedtuple('RGBColor', ['red', 'green', 'blue'])
MagickPixel = namedtuple('MagickPixel', ['coordinate', 'color'])

IMAGEMAGICK_RE = re.compile(r'(\d+)\D+(\d+)\D+(\d+)\D+(\d+)\D+(\d+)\D+')

class PixelEnumerationFormatError(Exception):
def __init__(self, index, line):
self.index = index
self.line = line

def __str__(self):
return "Invalid line #%i:\n\t%s" % (self.index, self.line)

def get_pixel_enumeration(txt):
for index, line in enumerate(txt.split('\n')):
# Wir zeigen uns tollerant bei fuehrenden Leerzeichen und entfernen sie.
line = line.lstrip()
# Wir interessieren uns nur für Zeilen die nicht leer und kein Kommentar sind.
if line and not line.startswith('#'):
try:
posx, posy, r, g, b = IMAGEMAGICK_RE.match(line).groups()
except AttributeError:
raise PixelEnumerationFormatError(index, line)

yield MagickPixel(coordinate=Point2D(x=int(posx), y=int(posy)),
color=RGBColor(red=int(r), green=int(g), blue=int(b)))


with open('image.txt') as f:
for pixel in get_pixel_enumeration(f.read()):
print(pixel.color)
[/code]

Code: Alles auswählen

RGBColor(red=23, green=22, blue=2)
RGBColor(red=34, green=33, blue=13)
RGBColor(red=18, green=20, blue=0)
RGBColor(red=32, green=34, blue=13)
Es gibt übrigens auch Pythonbindings zu ImageMagick.. kann es aber nicht empfehlen wei die Doku nicht zufinden ist.
nezzcarth
User
Beiträge: 1635
Registriert: Samstag 16. April 2011, 12:47

Sirius3 hat geschrieben:@nezzcarth: einen flexiblen regulären Ausdruck zu schreiben ist noch unverständlicher, als einen festen. "\s*(\d+)\s*,\s*(\d+)\s*:\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)"
Gut, da ist sicher was dran :) Mir schwebte eher eine Kompromisslösung mit einfachen regulären Ausdrücken und Stringoperationen
vor (z. B. um über alle Zahlensequenzen vor dem Inline-Kommentar zu iterieren). So ganz überzeugt bin ich davon aber auch nicht.

Welche Lösung schlägst du vor?
kbr hat geschrieben:@nezzcarth: Ich verwende reguläre Ausdrücke eigentlich erst dann, wenn es mit Stringoperationen nicht mehr geht oder zu umständlich wird. Sicher hast Du recht, was Einschränkungen der Formatierung des zu lesenden Datenformats angeht. Wird dieses maschinell erzeugt, so ist das aber kein Problem. Ansonsten betrachte ich es eher als feature, wenn das Programm abbricht, sobald der Input nicht stimmt. Erst wenn das lästig wird, würde ich mir mehr Mühe geben.
Ja, dass das Programm die Arbeit verweigert, wenn das Format nicht passt, ist vielleicht in manchen Fällen besser, als wenn es versucht, sich
fehlerhafte Daten "schön zu parsen" und dem Benutzer vorzugaukeln, alles wäre in Ordnung. Allerdings sollte man dann dem Benutzer auch mitteilen,
was los ist (wenn es zu einer halbwegs verständlichen Exception kommt, reicht das ja manchmal schon; ansonsten sollte man selbst für etwas entsprechendes sorgen). Ansonsten bin ich bei Formaten mitunter etwas skeptisch, da diese gerne mal von verschiedenen Programmen unterschiedlich ausgelegt werden. Man kann aber sicher argumentieren, dass das nicht das Problem des Programmierers ist, oder für ein kleines Skript, das nur für einen konkreten Zweck gebraucht wird nicht relevant ist.
BlackJack

Über's Ziel hinausschiessen klingt lustig — ich will auch mal. :-D

Code: Alles auswählen

#!/usr/bin/env python
from __future__ import absolute_import, division, print_function
import pyparsing as pp


SOURCE = """\
# ImageMagick pixel enumeration: 300,168,255,srgb
0,0: (23,22,2)  #171602  srgb(23,22,2)
1,0: (34,33,13)  #22210D  srgb(34,33,13)
2,0: (18,20,0)  #121400  srgb(18,20,0)
3,0: (32,34,13)  #20220D  srgb(32,34,13)
"""


def make_grammar():
    comma = pp.Suppress(',')
    colon = pp.Suppress(':')
    open_paren = pp.Suppress('(')
    close_paren = pp.Suppress(')')
    number = pp.Word(pp.nums).setParseAction(lambda ts: int(ts[0]))
    position = pp.Group(number('x') + comma + number('y'))
    color = pp.Group(
        open_paren
        + number('r')
        + comma
        + number('g')
        + comma
        + number('b')
        + close_paren
    )
    return pp.LineStart() + position('position') + colon + color('color')


def main():
    for match, _, _ in make_grammar().scanString(SOURCE):
        x, y = match.position
        red, green, blue = match.color
        print(x, y, ':', red, green, blue)
              

if __name__ == '__main__':
    main()
Benutzeravatar
snafu
User
Beiträge: 6741
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

nezzcarth hat geschrieben:Ich mag Lösungen, die einzig mit den Python Stringoperationen arbeiten oft nicht so gerne, da sie voraussetzen, dass die Daten immer exakt so strukturiert sind, wie sie im Beispiel vorliegen. Die Verarbeitungslogik ist sozusagen hard-coded und wenig flexibel. Ein Leerzeichen zu viel, und dein Iterator kommt durcheinander. Wenn es darum geht, ein stabiles Skript zu schreiben, wären reguläre Ausdrücke meiner Meinung nach doch eine bessere Wahl.
Hier kann man sich aber zunutze machen, dass int() Leerzeichen ignoriert. Dadurch wird ein Python-Ausdruck mit String-Operationen IMHO nicht komplexer als ein regulärer Ausdruck. Hier mal mit eingebauten Leerzeichen im Beispiel-Text:

Code: Alles auswählen

from collections import namedtuple

TEST_TEXT = """\
# ImageMagick pixel enumeration: 300,168,255,srgb
0,0: (23,   22, 2  )  #171602  srgb(23,22,2)
1,0: (34,33,  13)  #22210D  srgb(34,33,13)
2,0: (18,20   ,0)  #121400  srgb(18,20,0)
3,0: (32,34 , 13)  #20220D  srgb(32,34,13)"""

RGBColor = namedtuple('RGBColor', 'r,g,b')

def parse_rgb(line):
    r, g, b = (
        int(digits) for digits in
        line.split('(')[1].split(')')[0].split(',')
    )
    return RGBColor(r, g, b)

def main():
    for line in TEST_TEXT.splitlines():
        if not line.startswith('#'):
            print(parse_rgb(line))

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