Probleme beim sys.stdin in Datei schreiben

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
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Ich habe einen virtuellen Druckeranschluss (Windows), der beim Drucken die Druckdaten sammelt, dann einen Kindprozess startet und die Daten per stdin an den Prozess übergibt. Ich möchte nichts weiter machen als die Daten mit Python in eine Datei zu schreiben.

Das gelingt mir nur zur Hälfte. Es werden nicht alle Bytes geschrieben. Wieso? Hört die Schleife vielleicht zu zeitig auf? Hat das was mit Buffer zu tun?

Ich drucke aus notepad.exe über einen PCL-Treiber den Text "Hello world!". Wenn ich den Druck manuell in eine Datei umleite, erhalte ich 8.654 Bytes. Nutze ich den Python-Code unten, werden nur 3.923 Bytes geschrieben.

Ich benutze (gekürzt auf Hauptfunktion):

Code: Alles auswählen

import sys
import os
import time
import tempfile

def stdin_to_file(jobRoot):
    temp = tempfile.mkstemp(prefix=time.strftime("%Y-%m-%d_%H-%M-%S_"), dir=jobRoot)
    f = open(temp[1], "wb")
    for line in sys.stdin:
        f.write(line)
    f.close()

def main():
    if len(sys.argv) <= 1:
        print "usage: %s <DIR>" % os.path.basename(sys.argv[0])
        sys.exit(1)
    stdin_to_file(sys.argv[1])

if __name__ == "__main__":
    main()
Das Logfile des RedMon-Druckeranschlusses sieht wie folgt aus (Mein Python-Script läuft als py2exe .exe Datei):

Code: Alles auswählen

RedMon - Redirection Port Monitor
Copyright (C) 1997-2001, Ghostgum Software Pty Ltd.  All Rights Reserved.
2001-10-28  Version 1.7
Environment:
  [...a list of environment variables...]
  REDMON_PORT=RPT1:
  REDMON_JOB=8
  REDMON_PRINTER=My Printer
  REDMON_MACHINE=\\PC123
  REDMON_USER=Administrator
  REDMON_DOCNAME=test.txt - Editor
  REDMON_FILENAME=
  REDMON_SESSIONID=0
  
REDMON StartDocPort: returning 1
  "C:\test\rpt.exe" C:\test\rpt.log y pcl n C:\test C:\test C:\WINDOWS\notepad.exe {{out}}
  Printer=My Printer
  JobId=8

REDMON WriteThread: started
  Level=1
    DocumentName="test.txt - Editor"
    OutputFile="(null)"
    Datatype="RAW"
  output=0 show=0 delay=300 runuser=0

REDMON WritePort: about to write 8654 bytes to port.
[...The 8.654 bytes are written here...]

REDMON WritePort: OK  count=8654 written=8654
REDMON EndDocPort: starting

REDMON WriteThread: ending
(3, 'C:\\test\\2010-12-20_16-13-05_5pjkvj.pcl')
2010/12/20 16:13:07: INFO
The program's output messages: 
2010/12/20 16:13:07: INFO
The program's error messages: 
REDMON EndDocPort: process finished after 2 seconds

REDMON EndDocPort: 0 bytes written to printer
REDMON EndDocPort: ending
BlackJack

@droptix: Ich weiss nicht ob es daran liegt, aber Du benutzt `mkstemp()` nicht ganz wie's gedacht ist. Das liefert schon eine geöffnete *Datei* und Du öffnest Die unter dem gleichen Namen noch einmal!?

Und binäre Dateien haben eigentlich keine Zeilen, also könnte auch das iterieren über Zeilen ein Problem darstellen. Ich würde da entweder alles auslesen -- wenn man davon ausgehen kann, dass da nie so viele Daten reinkommen, dass das ein Problem wird -- oder Blockweise kopieren.

Ansonsten könntest Du ja auch mal die Daten vergleichen. Fehlt da am Anfang etwas, oder am Ende, oder mitten drin...
Benutzeravatar
b.esser-wisser
User
Beiträge: 272
Registriert: Freitag 20. Februar 2009, 14:21
Wohnort: Bundeshauptstadt B.

Kommt in den 8kB ein ^Z ("\x1c" SUB, Dateiendezeichen) vor? stdin ist ja normalerweise im Textmode göffnet.
Also musst du wahrscheinlich sys.stdin im Binärmodus 'öffnen' (Google sagt: "os.fdopen()" oder, wenn das nicht geht, "msvcrt.setmode()"

hth, Jörg
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

mkstemp() returns a tuple containing an OS-level handle to an open file (as would be returned by os.open()) and the absolute pathname of that file, in that order.
D.h. der erste Teil vom Tupel ist ein Filehandle? Weil wenn ich das per `print` ausdrucke, dann erhalte ich einen Integer-Wert und kein Python-like Filehandle... ich kann damit nicht weiter arbeiten, siehe hier:

Code: Alles auswählen

import tempfile
import time

handle, path = tempfile.mkstemp(prefix=time.strftime("%Y-%m-%d_%H-%M-%S_"), dir="D:\\")
handle.close()
Ergebnis:

Code: Alles auswählen

Traceback (most recent call last):
  File "C:/Users/tog/Desktop/mkstemp", line 5, in <module>
    handle.close()
AttributeError: 'int' object has no attribute 'close'
b.esser-wisser hat geschrieben:Kommt in den 8kB ein ^Z ("\x1c" SUB, Dateiendezeichen) vor? stdin ist ja normalerweise im Textmode göffnet.
Keine Ahnung, wie kann ich das prüfen?
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

droptix hat geschrieben:
mkstemp() returns a tuple containing an OS-level handle to an open file (as would be returned by os.open()) and the absolute pathname of that file, in that order.
D.h. der erste Teil vom Tupel ist ein Filehandle? Weil wenn ich das per `print` ausdrucke, dann erhalte ich einen Integer-Wert und kein Python-like Filehandle... ich kann damit nicht weiter arbeiten
Da steht ja auch ``os.open`` und nicht das Built-In ``open``. Das ist eine offene Datei, darin kannst du mit ``os.read`` direkt lesen. Wenn du auf ``os.open`` in der von dir zitierten Dokumentation klickst kommst du auf diese Notiz:
Python documentation hat geschrieben:This function is intended for low-level I/O. For normal usage, use the built-in function open(), which returns a “file object” with read() and write() methods (and many more). To wrap a file descriptor in a “file object”, use fdopen().
Und von dort nach ``os.fdopen``. Da hast du jetzt dein ``file``-Objekt.

Edit: ``"\x1c" in deine_daten`` ist ja jetzt echt naheliegend....
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Musste erstmal testen, welche Funktion was macht, hab's kapiert:

Code: Alles auswählen

import os
import tempfile

fd, path = tempfile.mkstemp(dir="D:\\")
print fd, path
h = os.fdopen(fd, "a")
h.write("Hello")
h.close()
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Leonidas hat geschrieben:``"\x1c" in deine_daten`` ist ja jetzt echt naheliegend....
Habe mit Notepad++ gesucht und im erweiterten Suchmodus "\x1c" eingegeben. Es werden 16 Ergebnisse gefunden. Bis zur ersten Fundstelle sind es 2.503 Zeichen (Bytes) einschl. Steuerzeichen. Das passt nicht so ganz zu den 3.923 Bytes die Python schreibt...

Nochmal zurück zum Lösungsansatz: wie kann ich sys.stdin binär öffnen?
BlackJack

@droptix: `NamedTemporaryFile` dürfte hier vielleicht einfacher zu verwenden sein als `mkstemp()`. Das ist weniger "low level".
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

droptix hat geschrieben:
Leonidas hat geschrieben:``"\x1c" in deine_daten`` ist ja jetzt echt naheliegend....
Habe mit Notepad++ gesucht und im erweiterten Suchmodus "\x1c" eingegeben. Es werden 16 Ergebnisse gefunden. Bis zur ersten Fundstelle sind es 2.503 Zeichen (Bytes) einschl. Steuerzeichen. Das passt nicht so ganz zu den 3.923 Bytes die Python schreibt...
Ich hätte halt gedacht direkt im Python-Code...
droptix hat geschrieben:Nochmal zurück zum Lösungsansatz: wie kann ich sys.stdin binär öffnen?
Hat doch b.esser-wisser schon gesagt:

Code: Alles auswählen

import sys, msvcrt, os
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
(Ich frag mich ob dir schon aufgefallen ist, dass ich deine ganzen Fragen durch Lesen dieses Threads und der Dokumentation beantworte *hint*)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Leonidas hat geschrieben: (Ich frag mich ob dir schon aufgefallen ist, dass ich deine ganzen Fragen durch Lesen dieses Threads und der Dokumentation beantworte *hint*)

Code: Alles auswählen

Leonidas.Arschigkeit += 1
SCNR ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Hyperion hat geschrieben:

Code: Alles auswählen

Leonidas.Arschigkeit += 1
SCNR ;-)
Dacht ich mir hier auch eben ;)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Leonidas hat geschrieben:Ich hätte halt gedacht direkt im Python-Code...
Wenn Python an diesem Steuerzeichen aussteigt, passiert das Zeichen ja meinen Code evtl. gar nicht erst. Manchmal fragt man halt lieber mal nach bevor man stundenlang sucht. Dafür sind diese Foren ja da, richtig?
Leonidas hat geschrieben:

Code: Alles auswählen

import sys, msvcrt, os
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
Danke, so perfekt stand es dort aber nicht drin. Ich mache sonst nie was mit stdin, daher glänze ich hier mit Unwissenheit...
Leonidas hat geschrieben:(Ich frag mich ob dir schon aufgefallen ist, dass ich deine ganzen Fragen durch Lesen dieses Threads und der Dokumentation beantworte *hint*)
Ja ist es. Du machst das so locker, weil du weißt wo du suchen musst. Ich ignoriere mal euren `Leonidas`-Counter, aber mir fällt auch auf dass die Stimmung heute etwas aggro ist...

Also sag ich zum Abschluss nochmal Danke!

P.S. Der Binary Mode für stdin brachte die Lösung, wie fein! Nun werden alle Bytes durch Python geschrieben.
Antworten