2016-03-22 2 views
4

У меня есть программа на Windows (Win7), которая записывает в txt-файл каждые x секунд. Теперь у меня есть скрипт python, который читает этот txt-файл каждые x секунд. Когда скрипт python считывает файл и в то же время другая программа хочет записать в этот файл - программа записи сработает (и отображает ошибку разрешения). Поскольку я не могу изменить способ записи программы в txt-файл, я должен попытаться открыть txt-файл, не блокируя программу записи. Кто-нибудь знает, что я могу сделать в этой ситуации (чтение без блокировки) Я был бы очень рад за каждый совет по этой теме!python read file non blocking on windows

Код для программы, которая пытается читать файл выглядит примерно так:

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

     raw_data_x = raw_data.readlines() 

Я должен открыть файл с «кодеками», потому что его в Юникоде.

+0

Отметьте этот ответ http://stackoverflow.com/a/30172682/735893 – Vader

+0

Спасибо за быстрый ответ! Я уже видел этот пост. Я думаю, что это не для выигрышных систем. – Py42

+0

@Vader: эта ссылка использует 'fcntl', которая является UNIX-специфичной, OP использует Windows. – cdarke

ответ

2

После долгого времени мне удалось создать функцию, которая делает это для вас в ctypes. Имейте в виду, что это будет работать, только если процесс не приобрел «эксклюзивный» доступ. Если это так, вам не повезло и вам нужно будет использовать службу теневого копирования, как показано на рисунке here, или реализовано here.
Во всяком случае, здесь вы идете:

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) 

Функция открывает файл, разделяя чтения и записи обращаться позволяет множественный доступ. Затем он преобразует дескриптор в обычный файл-файл python.
Обязательно закройте файл, когда закончите.

0

Недавно я должен был читать I/O на stdin, stdout в python с кросс-платформенной совместимостью.
Для linux:
для linux мы можем использовать select модуль. Это реализация оболочки posix select. Он позволяет передавать несколько файловых дескрипторов, дожидаясь их готовности. Как только они будут готовы, вы будете уведомлены, и может быть выполнено действие read/write. Вот немного кода, который поможет вам идею
здесь nodejs является докер среда с nodejs изображением

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 

Для окон Этот пример кода имеет как операции чтения и записи с стандартного ввода, стандартный вывод вовлеченного. Теперь этот код не будет работать с ОС Windows, потому что при использовании окон выборка не позволяет stdin, stdout передаваться как аргументы.
Docs говорит:

Файловые объекты на Windows, не являются приемлемыми, но сокеты. В Windows основная функция select() предоставляется библиотекой WinSock и не обрабатывает дескрипторы файлов, которые не происходят из WinSock.

Во-первых, я должен сказать, что есть много библиотек для неблокирующая ввода-вывода для чтения/на окнах, как asyncio (Python 3), gevent (для Python 2.7), msvcrt, а затем есть «s win32eventpywin32 что предупредите вас, если ваш сокет готов к read/write данным.Но ни один из них не позволил мне читать писать на stdin/stdout давая как иных ошибок
An operation is performend on something that is not a socket
Handles only expect integer values и т.д.
Есть некоторые другие библиотеки twister, что я не пробовал.

Теперь, чтобы реализовать такую ​​функциональность, как в вышеупомянутом коде на платформе Windows, я использовал threads. вот мой код:

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 

Так что лучший вариант для меня - это темы. Эта статья будет nice read, если вы хотите узнать больше.