Seite 1 von 2
Textfile auslesen
Verfasst: Freitag 13. Januar 2017, 01:58
von 9ff
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
Re: Textfile auslesen
Verfasst: Freitag 13. Januar 2017, 08:31
von kbr
@9ff: Das geht auch ohne reguläre Ausdrücke und ohne Koma ...
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)
Re: Textfile auslesen
Verfasst: Freitag 13. Januar 2017, 08:33
von sebastian0202
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.
Re: Textfile auslesen
Verfasst: Freitag 13. Januar 2017, 10:06
von harryberlin
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])
Re: Textfile auslesen
Verfasst: Freitag 13. Januar 2017, 10:32
von Sirius3
@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.
Re: Textfile auslesen
Verfasst: Freitag 13. Januar 2017, 10:43
von kbr
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.
Re: Textfile auslesen
Verfasst: Freitag 13. Januar 2017, 10:48
von harryberlin
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?
Re: Textfile auslesen
Verfasst: Freitag 13. Januar 2017, 12:21
von 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?
Re: Textfile auslesen
Verfasst: Freitag 13. Januar 2017, 13:03
von harryberlin
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.
Re: Textfile auslesen
Verfasst: Freitag 13. Januar 2017, 13:32
von 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.
Re: Textfile auslesen
Verfasst: Freitag 13. Januar 2017, 15:50
von harryberlin
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.
Re: Textfile auslesen
Verfasst: Freitag 13. Januar 2017, 17:42
von 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.
Re: Textfile auslesen
Verfasst: Freitag 13. Januar 2017, 18:21
von pyHoax
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.
Re: Textfile auslesen
Verfasst: Samstag 14. Januar 2017, 09:43
von nezzcarth
kbr hat geschrieben:@9ff: Das geht auch ohne reguläre Ausdrücke und ohne Koma ...
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.
Re: Textfile auslesen
Verfasst: Samstag 14. Januar 2017, 10:16
von Sirius3
@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*\)"
Re: Textfile auslesen
Verfasst: Samstag 14. Januar 2017, 11:01
von kbr
@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.
Re: Textfile auslesen
Verfasst: Samstag 14. Januar 2017, 14:18
von pyHoax
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.
Re: Textfile auslesen
Verfasst: Samstag 14. Januar 2017, 15:15
von nezzcarth
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.
Re: Textfile auslesen
Verfasst: Samstag 14. Januar 2017, 15:52
von BlackJack
Über's Ziel hinausschiessen klingt lustig — ich will auch mal.
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()
Re: Textfile auslesen
Verfasst: Samstag 14. Januar 2017, 16:41
von snafu
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()