Подтвердить что ты не робот

Python 2.x - записать двоичный вывод в stdout?

Есть ли способ записать двоичный вывод в sys.stdout в Python 2.x? В Python 3.x вы можете просто использовать sys.stdout.buffer(или отсоединить stdout и т.д.), Но я не смог найти решения для Python 2.5/2.6.

EDIT, Решение: От ссылки ChristopheD, ниже:

import sys

if sys.platform == "win32":
    import os, msvcrt
    msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)

EDIT: Я пытаюсь нажать PDF файл (в двоичной форме) на stdout для обслуживания на веб-сервере. Когда я пытаюсь написать файл с помощью sys.stdout.write, он добавляет все виды возвратов каретки в двоичный поток, что приводит к тому, что PDF файл становится поврежденным.

EDIT 2: для этого проекта мне, к сожалению, нужно запускать на Windows Server, так что Linux-решения не работают.

Просто Dummy Example (чтение из файла на диске вместо генерации "на лету", так что мы знаем, что код генерации не является проблемой):

file = open('C:\\test.pdf','rb') 
pdfFile = file.read() 
sys.stdout.write(pdfFile)
4b9b3361

Ответ 1

На какой платформе вы работаете?

Вы можете попробовать этот рецепт, если вы находитесь в Windows (ссылка указывает на то, что это зависит от Windows).

if sys.platform == "win32":
    import os, msvcrt
    msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)

В Интернете есть некоторые ссылки, в которых должна была/должна быть функция в Python 3.1 для повторного открытия sys.stdout в двоичном режиме, но я действительно не знаю, есть ли более эффективная альтернатива, чем указано выше для Python 2.x.

Ответ 2

Вы можете использовать небуферизованный режим: python -u script.py.

-u     Force  stdin,  stdout  and stderr to be totally unbuffered.
       On systems where it matters, also put stdin, stdout and stderr
       in binary mode.

Ответ 3

В Python 2.x все строки являются двоичными массивами символов по умолчанию, поэтому я считаю, что вы должны иметь возможность просто

>>> sys.stdout.write(data)

EDIT: Я подтвердил ваш опыт.

Я создал один файл gen_bytes.py

import sys
for char in range(256):
    sys.stdout.write(chr(char))

И еще один файл read_bytes.py

import subprocess
import sys

proc = subprocess.Popen([sys.executable, 'gen_bytes.py'], stdout=subprocess.PIPE)
res = proc.wait()
bytes = proc.stdout.read()
if not len(bytes) == 256:
    print 'Received incorrect number of bytes: {0}'.format(len(bytes))
    raise SystemExit(1)
if not map(ord, bytes) == range(256):
    print 'Received incorrect bytes: {0}'.format(map(ord, bytes))
    raise SystemExit(2)
print "Everything checks out"

Поместите их в один каталог и запустите read_bytes.py. Разумеется, похоже, что Python фактически преобразовывает новые строки на выходе. Я подозреваю, что это происходит только в ОС Windows.

> .\read_bytes.py
Received incorrect number of bytes: 257

Следуя примеру ChristopheD и изменяя gen_bytes на следующее, исправляется проблема.

import sys

if sys.platform == "win32":
    import os, msvcrt
    msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)

for char in range(256):
    sys.stdout.write(chr(char))

Я включаю это для полноты. ChristopheD заслуживает внимания.

Ответ 4

Вы можете использовать argopen.argopen(), он обрабатывает тире как stdin/stdout и исправляет двоичный режим в Windows.

import argopen
stdout = argopen.argopen('-', 'wb')
stdout.write(some_binary_data)

Ответ 5

Я решил это, используя оболочку для файлового дескриптора. (Протестировано в Python 3.2.5 на Cygwin)

class BinaryFile(object):
    ''' Wraps a file-descriptor to binary read/write. The wrapped
    file can not be closed by an instance of this class, it must
    happen through the original file.

    :param fd: A file-descriptor (integer) or file-object that
        supports the ``fileno()`` method. '''

    def __init__(self, fd):
        super(BinaryFile, self).__init__()
        fp = None
        if not isinstance(fd, int):
            fp = fd
            fd = fp.fileno()
        self.fd = fd
        self.fp = fp

    def fileno(self):
        return self.fd

    def tell(self):
        if self.fp and hasattr(self.fp, 'tell'):
            return self.fp.tell()
        else:
            raise io.UnsupportedOperation(
                'can not tell position from file-descriptor')

    def seek(self, pos, how=os.SEEK_SET):
        try:
            return os.lseek(self.fd, pos, how)
        except OSError as exc:
            raise io.UnsupportedOperation('file-descriptor is not seekable')

    def write(self, data):
        if not isinstance(data, bytes):
            raise TypeError('must be bytes, got %s' % type(data).__name__)
        return os.write(self.fd, data)

    def read(self, length=None):
        if length is not None:
            return os.read(self.fd, length)
        else:
            result = b''
            while True:
                data = self.read(1024)
                if not data:
                    break
                result += data
            return result