Python 2.x - записать двоичный вывод в стандартный вывод? - PullRequest
34 голосов
/ 03 марта 2010

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

РЕДАКТИРОВАТЬ, Решение : По ссылке ChristopheD ниже:

import sys

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

РЕДАКТИРОВАТЬ: я пытаюсь отправить PDF-файл (в двоичном виде) на стандартный вывод для обслуживания на веб-сервере. Когда я пытаюсь записать файл, используя sys.stdout.write, он добавляет всевозможные возвраты каретки в двоичный поток, что приводит к повреждению файла PDF.

РЕДАКТИРОВАТЬ 2: К сожалению, для этого проекта мне нужно работать на Windows Server, поэтому решения для Linux отсутствуют.

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

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

Ответы [ 5 ]

28 голосов
/ 03 марта 2010

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

Вы можете попробовать этот рецепт , если вы работаете в 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.

8 голосов
/ 18 февраля 2014

Вы можете использовать небуферизованный режим: 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.
6 голосов
/ 03 марта 2010

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

>>> sys.stdout.write(data)

РЕДАКТИРОВАТЬ: я подтвердил ваш опыт.

Я создал один файл, 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 заслуживает похвалы.

5 голосов
/ 04 февраля 2013

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

import argopen
stdout = argopen.argopen('-', 'wb')
stdout.write(some_binary_data)
0 голосов
/ 26 ноября 2014

Я решил это, используя оболочку для дескриптора файла. (Проверено в 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
...