Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python read unicode stdin without batching

If I read input from stdin in python, the for loop will collect a number of lines before the body of the loop is run (at least in cpython).

from __future__ import print_function
import sys

for line in sys.stdin:
    print("Echo:", line.strip())

Outputs:

$ python ../test.py 
foo
bar
Echo: foo
Echo: bar

Lines are handled in some kind of batches. I can avoid it like this:

from __future__ import print_function
import sys

for line in iter(sys.stdin.readline, ''):
    print("Echo:", line.strip())

Outputs:

$ python ../test.py 
foo
Echo: foo
bar
Echo: bar

Which is what I need.

My problem is that I have to read utf-8 input and trick with iter() does not work with codecs.getwriter.

from __future__ import print_function
import sys
import codecs

sys.stdin = codecs.getreader('utf-8')(sys.stdin)
for line in iter(sys.stdin.readline, ''):
    print("Echo:", line.strip())

$ python ../test.py 
foo
bar
Echo: foo
Echo: bar

Is there any way to avoid this batching while reading utf8 data from stdin?


Edit: Added import statements for completeness.

like image 935
bwj Avatar asked Aug 14 '26 04:08

bwj


2 Answers

Using lambda:

for line in iter(lambda: sys.stdin.readline().decode('utf-8'), ''):
    print 'Echo:', line.strip()

or, decoding in loop body:

for line in iter(sys.stdin.readline, ''):
    print "Echo:", line.decode('utf-8').strip()
like image 102
falsetru Avatar answered Aug 16 '26 16:08

falsetru


You should probably use raw_input to get a line of input from stdin.

try:
    while True:
        print("Echo:", raw_input())
except EOFError:
    pass

The problem is that Python 2 just has this kind of buffering. See the documentation for -u on the manpage

-u   Force  stdin,  stdout  and stderr to be totally unbuffered.  On systems
     where it matters, also put stdin, stdout and  stderr  in  binary  mode.
     Note  that there is internal buffering in xreadlines(), readlines() and
     file-object iterators ("for line in sys.stdin") which is not influenced
     by   this   option.   To  work  around  this,  you  will  want  to  use
     "sys.stdin.readline()" inside a "while 1:" loop.

The important part is that using sys.stdin.readline() is the recommended course of action; it's unlikely that there's a good way to forcibly unbuffer file objects.

You should just decode each line as you get it.

like image 36
Veedrac Avatar answered Aug 16 '26 18:08

Veedrac