Python 2.x - Write binary output to stdout?
Is there any way to write binary output to sys.stdout in Python 2.x? In Python 3.x, you can just use sys.stdout.buffe开发者_JAVA百科r (or detach stdout, etc...), but I haven't been able to find any solutions for Python 2.5/2.6.
EDIT, Solution: From ChristopheD's link, below:
import sys
if sys.platform == "win32":
import os, msvcrt
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
EDIT: I'm trying to push a PDF file (in binary form) to stdout for serving up on a web server. When I try to write the file using sys.stdout.write, it adds all sorts of carriage returns to the binary stream that causes the PDF to render corrupt.
EDIT 2: For this project, I need to run on a Windows Server, unfortunately, so Linux solutions are out.
Simply Dummy Example (reading from a file on disk, instead of generating on the fly, just so we know that the generation code isn't the issue):
file = open('C:\\test.pdf','rb')
pdfFile = file.read()
sys.stdout.write(pdfFile)
Which platform are you on?
You could try this recipe if you're on Windows (the link suggests it's Windows specific anyway).
if sys.platform == "win32":
import os, msvcrt
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
There are some references on the web that there would/should be a function in Python 3.1 to reopen sys.stdout
in binary mode but I don't really know if there's a better alternative then the above for Python 2.x.
You can use unbuffered mode: 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.
You can use argopen.argopen(), it handles dash as stdin/stdout, and fixes binary mode on Windows.
import argopen
stdout = argopen.argopen('-', 'wb')
stdout.write(some_binary_data)
In Python 2.x, all strings are binary character arrays by default, so I believe you should be able to just
>>> sys.stdout.write(data)
EDIT: I've confirmed your experience.
I created one file, gen_bytes.py
import sys
for char in range(256):
sys.stdout.write(chr(char))
And another 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"
Put them in the same directory and run read_bytes.py. Sure enough, it appears as if Python is in fact converting newlines on output. I suspect this only happens on a Windows OS.
> .\read_bytes.py
Received incorrect number of bytes: 257
Following the lead by ChristopheD, and changing gen_bytes to the following corrects the issue.
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))
I include this for completeness. ChristopheD deserves the credit.
I solved this using a wrapper for a file-descriptor. (Tested in Python 3.2.5 on 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
精彩评论