2016-03-22 5 views
4

Ich habe ein Programm auf Windows (Win7), die alle x Sekunden in eine TXT-Datei schreibt. Jetzt habe ich ein Python-Skript, das diese TXT-Datei alle x Sekunden liest. Wenn das Python-Skript die Datei liest und gleichzeitig das andere Programm in diese Datei schreiben möchte - Das Schreibprogramm stürzt ab (und zeigt einen Berechtigungsfehler an). Da ich nicht ändern kann, wie das Programm in die TXT-Datei schreibt, muss ich versuchen, die TXT-Datei zu öffnen, ohne das Schreibprogramm zu blockieren. Weiß jemand was ich in dieser Situation machen könnte (Lesen ohne Blockieren) Ich würde mich sehr über jeden Tipp zu diesem Thema freuen!Python lesen Datei nicht blockiert auf Windows

Der Code für das Programm, das die Datei zu lesen versucht, geht so etwas:

with codecs.open(datapath, "r", 'utf-16') as raw_data: 

     raw_data_x = raw_data.readlines() 

Ich habe die Datei zu öffnen, mit „Codecs“, weil seine in Unicode.

+0

Überprüfen Sie diese Antwort http://stackoverflow.com/a/30172682/735893 – Vader

+0

Vielen Dank für die schnelle Antwort! Ich habe diesen Post schon gesehen. Ich denke, es ist nichts für Gewinnsysteme. – Py42

+0

@Vader: Dieser Link verwendet 'fcntl', was UNIX-spezifisch ist. Das OP verwendet Windows. – cdarke

Antwort

2

Nach einer langen Zeit, ich habe es geschafft, eine Funktion, die es für Sie in Ctypes macht. Beachten Sie, dass dies nur funktioniert, wenn der Prozess keinen "exklusiven" Zugriff erhält. Wenn dies der Fall ist, haben Sie kein Glück und müssen einen Schattenkopie-Dienst wie gezeigt here oder implementiert here verwenden.
Wie auch immer, hier gehen Sie:

import ctypes 
from ctypes import wintypes 
import os 
import msvcrt 

GENERIC_READ = 0x80000000 
GENERIC_WRITE = 0x40000000 

OPEN_EXISTING = 3 
OPEN_ALWAYS = 4 

ACCESS_MODES = { 
    "r": GENERIC_READ, 
    "w": GENERIC_WRITE, 
    "r+": (GENERIC_READ|GENERIC_WRITE) 
} 

OPEN_MODES = { 
    "r": OPEN_EXISTING, 
    "w": OPEN_ALWAYS, 
    "r+": OPEN_ALWAYS, 
} 


def open_file_nonblocking(filename, access): 
    # Removes the b for binary access. 
    internal_access = access.replace("b", "") 
    access_mode = ACCESS_MODES[internal_access] 
    open_mode = OPEN_MODES[internal_access] 
    handle = wintypes.HANDLE(ctypes.windll.kernel32.CreateFileW(
     wintypes.LPWSTR(filename), 
     wintypes.DWORD(access_mode), 
     wintypes.DWORD(2|1), # File share read and write 
     ctypes.c_void_p(0), 
     wintypes.DWORD(open_mode), 
     wintypes.DWORD(0), 
     wintypes.HANDLE(0) 
    )) 

    try: 
     fd = msvcrt.open_osfhandle(handle.value, 0) 
    except OverflowError as exc: 
     # Python 3.X 
     raise OSError("Failed to open file.") from None 
     # Python 2 
     # raise OSError("Failed to open file.") 

    return os.fdopen(fd, access) 

Die Funktion die Datei öffnet, während die Lese- und Schreib teilen Mehrfachzugriff Griff ermöglicht. Anschließend wird das Handle in ein normales Python-Dateiobjekt konvertiert.
Stellen Sie sicher, dass die Datei geschlossen wird, wenn Sie fertig sind.

0

Vor kurzem musste ich E/A-Lesevorgänge auf stdin, stdout in Python mit plattformübergreifender Kompatibilität durchführen.
Für Linux:
für Linux können wir select Modul verwenden. Es ist Wrapper-Implementierung von Posix select Funktion. Sie können mehrere Dateideskriptoren übergeben und darauf warten, dass sie fertig sind. Sobald sie fertig sind, werden Sie benachrichtigt und read/write Aktion kann durchgeführt werden. Hier ist wenig Code, der Ihnen eine Idee
hier erhalten NodeJS ist eine Docker Umgebung mit NodeJS Bild

stdin_buf = BytesIO(json.dumps(fn) + "\n") 
    stdout_buf = BytesIO() 
    stderr_buf = BytesIO() 

    rselect = [nodejs.stdout, nodejs.stderr] # type: List[BytesIO] 
    wselect = [nodejs.stdin] # type: List[BytesIO] 
    while (len(wselect) + len(rselect)) > 0: 
      rready, wready, _ = select.select(rselect, wselect, []) 
      try: 
       if nodejs.stdin in wready: 
        b = stdin_buf.read(select.PIPE_BUF) 
        if b: 
         os.write(nodejs.stdin.fileno(), b) 
        else: 
         wselect = [] 
       for pipes in ((nodejs.stdout, stdout_buf), (nodejs.stderr, stderr_buf)): 
        if pipes[0] in rready: 
         b = os.read(pipes[0].fileno(), select.PIPE_BUF) 
         if b: 
          pipes[1].write(b) 
         else: 
          rselect.remove(pipes[0]) 
       if stdout_buf.getvalue().endswith("\n"): 
        rselect = [] 
      except OSError as e: 
       break 

Für Fenster Dieses Codebeispiel hat sowohl Lese- und Schreiboperationen mit stdin, stdout beteiligt. Nun würde dieser Code nicht mit Windows OS funktionieren, da Windows-Implementierungen in Windows nicht erlaubt, stdin, stdout als Argumente zu übergeben.
Docs sagt:

Datei-Objekte unter Windows nicht akzeptabel sind, aber Steckdosen sind. Unter Windows wird die zugrunde liegende Funktion select() von der WinSock-Bibliothek bereitgestellt und behandelt keine Dateideskriptoren, die nicht von WinSock stammen.

Zunächst muss ich erwähnen, dass es viele Bibliotheken für nicht-blockierende I/O-Lese auf Windows wie asyncio (Python 3), gevent (für Python 2.7), msvcrt und dann gibt es pywin32 ‚s win32event dass warnt Sie, wenn Ihr Socket bereit für read/write Daten ist.Aber keiner von ihnen konnte ich schreiben auf stdin/stdout geben Fehlern durchsuchen lesen wie
An operation is performend on something that is not a socket
Handles only expect integer values usw.
Es gibt einige andere Bibliotheken wie twister, die ich noch nicht ausprobiert.

Jetzt, um eine Funktionalität wie in oben genannten Code auf Windows-Plattform zu implementieren, habe ich threads. Hier ist mein Code:

stdin_buf = BytesIO(json.dumps(fn) + "\n") 
    stdout_buf = BytesIO() 
    stderr_buf = BytesIO() 

    rselect = [nodejs.stdout, nodejs.stderr] # type: List[BytesIO] 
    wselect = [nodejs.stdin] # type: List[BytesIO] 
    READ_BYTES_SIZE = 512 

    # creating queue for reading from a thread to queue 
    input_queue = Queue.Queue() 
    output_queue = Queue.Queue() 
    error_queue = Queue.Queue() 

    # To tell threads that output has ended and threads can safely exit 
    no_more_output = threading.Lock() 
    no_more_output.acquire() 
    no_more_error = threading.Lock() 
    no_more_error.acquire() 

    # put constructed command to input queue which then will be passed to nodejs's stdin 
    def put_input(input_queue): 
     while True: 
      sys.stdout.flush() 
      b = stdin_buf.read(READ_BYTES_SIZE) 
      if b: 
       input_queue.put(b) 
      else: 
       break 

    # get the output from nodejs's stdout and continue till otuput ends 
    def get_output(output_queue): 
     while not no_more_output.acquire(False): 
      b=os.read(nodejs.stdout.fileno(), READ_BYTES_SIZE) 
      if b: 
       output_queue.put(b) 

    # get the output from nodejs's stderr and continue till error output ends 
    def get_error(error_queue): 
     while not no_more_error.acquire(False): 
      b = os.read(nodejs.stderr.fileno(), READ_BYTES_SIZE) 
      if b: 
       error_queue.put(b) 

    # Threads managing nodejs.stdin, nodejs.stdout and nodejs.stderr respectively 
    input_thread = threading.Thread(target=put_input, args=(input_queue,)) 
    input_thread.start() 
    output_thread = threading.Thread(target=get_output, args=(output_queue,)) 
    output_thread.start() 
    error_thread = threading.Thread(target=get_error, args=(error_queue,)) 
    error_thread.start() 

    # mark if output/error is ready 
    output_ready=False 
    error_ready=False 

    while (len(wselect) + len(rselect)) > 0: 
     try: 
      if nodejs.stdin in wselect: 
       if not input_queue.empty(): 
        os.write(nodejs.stdin.fileno(), input_queue.get()) 
       elif not input_thread.is_alive(): 
        wselect = [] 
      if nodejs.stdout in rselect: 
       if not output_queue.empty(): 
        output_ready = True 
        stdout_buf.write(output_queue.get()) 
       elif output_ready: 
        rselect = [] 
        no_more_output.release() 
        no_more_error.release() 
        output_thread.join() 

      if nodejs.stderr in rselect: 
       if not error_queue.empty(): 
        error_ready = True 
        stderr_buf.write(error_queue.get()) 
       elif error_ready: 
        rselect = [] 
        no_more_output.release() 
        no_more_error.release() 
        output_thread.join() 
        error_thread.join() 
      if stdout_buf.getvalue().endswith("\n"): 
       rselect = [] 
       no_more_output.release() 
       no_more_error.release() 
       output_thread.join() 
     except OSError as e: 
      break 

So die beste Option für mich stellte sich heraus, dass Threads. Dieser Artikel wäre nice read, wenn Sie mehr wissen möchten.