Clearing old data from sys.stdout in Python - python

There's something I don't get when writing to sys.stdout in Python.What I want is to write some strings into stdout without preprending the stuff which already has been written before that.
So doing this:
while True:
sys.stdout.write("k")
sys.stdout.flush()
sys.stdout.write("d")
After some "k" data has been written into stdout in a loop I need to write some new data afterwards,which is "d" for the sake of example.
I am getting : "k d" in the output."K" from the loop writes is still in stdout.
What I want to get is just "d" .
Why?
How can I clear the stdout before writing new data into it?
Btw,using print() instead still accumulates the old data into final output.
Using python 2.7 on Linux

Terminals don't work that way. flush just flushes the buffer (i.e. characters that may not have been printed to screen yet), it does not clear the screen.
If you want to erase one character, you need to print a literal backspace control character, and then print something over your previous output (like a space):
sys.stdout.write("\b ")
However. Your asking this belies a deep misunderstanding of how terminals work. Think of it like a text processor - there is text on the screen, and a cursor (which is invisible). You need to tell the cursor what to do to manipulate the text on the screen. Since the cursor is always in overwrite mode, a trick that is often useful is to go back to the start of the line:
sys.stdout.write("\r")
and overwrite your data on that line. Note that if your new data is of lesser length than your previous data, this will leave some amount of previous data on the screen.
There are other tricks you can use if neither of these fits your bill, like outputting terminal control sequences to stdout.
(also, unless you have a specific need for using sys.stdout, just use print.)

I do have to print "k" but one line before "d".I want to print "d" in a completely new line.
Simply write a newline between the two:
sys.stdout.write("\n")
Or just use print, which adds an implicit newline at the end:
print "k"
print "d"

Related

How can I edit what has already been printed?

For example, let's say one of the printed things is "Hello World" and the second is "Hello". How would I print "Hello" on the same line as the one that says "Hello World"? This is just an example. Realistically I have no idea how long the printed text will be.
Here is an example script:
x = open("file.txt", "r+").read().split("\n")
for i in x:
print(something)
where something = I don't know. I want the output to be what the first line of the text file says, then what the second line says, and so on except print the second/third/fourth... line over the first line and each line is an unknown length, some shorter than others. Lets say the file.txt says:
Overflow
Stack
I would want it to print "Overflow" then "Stack", except each word gets printed on the first line and once you print "Stack", every part of "Overflow" can't be seen
Keep in mind that print("Hello World", end="\r") won't work because of the length.
You could work around the \r solution by padding each line with spaces according to the previous line:
prev_size = 0
with open("file.txt", "r+") as f:
for line in f:
print(f"{line.strip()}{' '*prev_size}", end='\r')
prev_size = len(line)
You would probably want to add a sleep between prints to be able to actually see the text changing...
When you use the print function in python, you're just pushing characters onto a pipe somewhere (the pipe might be connected to the standard out, but your program doesn't know that). Once its, pushed, there is nothing you can do to un-push it. Afterall, the pipe might not even be connected to a screen. It might be directly connected to the input of another program, or it might be using a physical printer for display. What would un-pushing even mean in those cases?
There are, however, special control characters (such as the "backspace" character) that you push to the pipe to signal that you want to erase characters. However, The terminal you are connected to is free to do what it wants with these characters. It can respect your wishes and erase characters, or it can print the literal '\b' characters to indicate a backspace, or it can completely ignore you and continue to print after the previous letters. That's completely out of your control.
Assuming the terminal that print the characters supports overwriting the characters, you can use the ANSI control sequences. The sequence for moving cursor to beginning of line is '\033[1G' and for erasing the everything from current cursor position to end of line is '\033[0K'. So,
import time
print('hello world', end='', flush=True) # prints "hello world" with no newline at the end
time.sleep(2) # wait 2 seconds
print('\033[1G\033[0K', end='') # moves cursor to beginning of line and erases the line
print('hi') # prints "hi" with newline at the end
flush=True is needed because the print function is buffered by default and doesn't actually print anything until it hits a newline. This tells the function you want to flush the buffer immediately.
Take a look at ANSI escape codes, section on "CSI sequences" to see what other codes are available.

carriage return not working in Terminator but works fine in Terminal in Ubuntu

I am using '\r' in print function in python 2.7. It works fine in terminal but not in terminator.
For example:
I am using a decrement counter which decrements from n to 0. In the Terminal, the count values are updated at the same line while in terminator, it count values get printed on new line. I am using terminator for various reasons such as its advanced features like tab partitioning etc. Below is the code snapshot
import sys
import time
if __name__ == "__main__":
sleep_time_in_sec = 15
time_to_go_back_mns = 10
for remaining in range(sleep_time_in_sec, -1, -1):
sys.stdout.write("\r")
sys.stdout.write(
"{:2d} seconds remaining to read last {:2d} minutes of data and perform prediction .....".format(remaining,
time_to_go_back_mns))
sys.stdout.flush()
time.sleep(1)
Can someone please suggest a fix
Simply place the \r at the end of your write statement like so:
sys.stdout.write(
"{:2d} seconds remaining to read last {:2d} minutes of data and perform prediction .....\r".format(remaining,
time_to_go_back_mns))
New data will then overwrite the line, you can use flush() to clear the line before writing to it again:
sys.stdout.flush()
Make sure that on the terminator side of things your profile settings are clean. Be sure to check out the compatibility tab as it mentions weird application behaviour.
note that carriage return stops functioning if the terminal size is too small. It will only return to the first character of the LAST line
Original answer
\r actually means "carriage return" whereas \n is a line feed.
In Linux (Unix) you'd usually use either a \n or \r\n.
So if your goal is to go to a new line you should use \n instead.
The reason it might work in the "terminal" (whichever one that might be) is because some terminals (notably gnome's terminal) catches the \r and treats it as a \r\n.
The answer for this post goes to #Rick van Lieshout.
In my case, I was using multiple tabs in the same window as a result of which \r was not working. It worked fine when I used it in a separate window or a tab with full horizontal width.

sys.stdin.readline() reads without prompt, returning 'nothing in between'

I have a function that executes the following (among other things):
userinput = stdin.readline()
betAmount = int(userinput)
Is supposed to take input integer from stdin as a string and convert it to an integer.
When I call the function, however, it returns a single newline character (it doesn't even wait for me to input anything).
Earlier in the program I get some input in the form of:
stdin.read(1)
to capture a single character.
Could this have something to do with it? Am I somehow writing a newline character to the next line of stdin?
How can I fix this?
stdin.read(1) reads one character from stdin. If there was more than one character to be read at that point (e.g. the newline that followed the one character that was read in) then that character or characters will still be in the buffer waiting for the next read() or readline().
As an example, given rd.py:
from sys import stdin
x = stdin.read(1)
userinput = stdin.readline()
betAmount = int(userinput)
print ("x=",x)
print ("userinput=",userinput)
print ("betAmount=",betAmount)
... if I run this script as follows (I've typed in the 234):
C:\>python rd.py
234
x= 2
userinput= 34
betAmount= 34
... so the 2 is being picked up first, leaving the 34 and the trailing newline character to be picked up by the readline().
I'd suggest fixing the problem by using readline() rather than read() under most circumstances.
Simon's answer and Volcano's together explain what you're doing wrong, and Simon explains how you can fix it by redesigning your interface.
But if you really need to read 1 character, and then later read 1 line, you can do that. It's not trivial, and it's different on Windows vs. everything else.
There are actually three cases: a Unix tty, a Windows DOS prompt, or a regular file (redirected file/pipe) on either platform. And you have to handle them differently.
First, to check if stdin is a tty (both Windows and Unix varieties), you just call sys.stdin.isatty(). That part is cross-platform.
For the non-tty case, it's easy. It may actually just work. If it doesn't, you can just read from the unbuffered object underneath sys.stdin. In Python 3, this just means sys.stdin.buffer.raw.read(1) and sys.stdin.buffer.raw.readline(). However, this will get you encoded bytes, rather than strings, so you will need to call .decode(sys.stdin.decoding) on the results; you can wrap that all up in a function.
For the tty case on Windows, however, input will still be line buffered even on the raw buffer. The only way around this is to use the Console I/O functions instead of normal file I/O. So, instead of stdin.read(1), you do msvcrt.getwch().
For the tty case on Unix, you have to set the terminal to raw mode instead of the usual line-discipline mode. Once you do that, you can use the same sys.stdin.buffer.read(1), etc., and it will just work. If you're willing to do that permanently (until the end of your script), it's easy, with the tty.setraw function. If you want to return to line-discipline mode later, you'll need to use the termios module. This looks scary, but if you just stash the results of termios.tcgetattr(sys.stdin.fileno()) before calling setraw, then do termios.tcsetattr(sys.stdin.fileno(), TCSAFLUSH, stash), you don't have to learn what all those fiddly bits mean.
On both platforms, mixing console I/O and raw terminal mode is painful. You definitely can't use the sys.stdin buffer if you've ever done any console/raw reading; you can only use sys.stdin.buffer.raw. You could always replace readline by reading character by character until you get a newline… but if the user tries to edit his entry by using backspace, arrows, emacs-style command keys, etc., you're going to get all those as raw keypresses, which you don't want to deal with.
stdin.read(1)
will not return when you press one character - it will wait for '\n'. The problem is that the second character is buffered in standard input, and the moment you call another input - it will return immediately because it gets its input from buffer.
If you need just one character and you don't want to keep things in the buffer, you can simply read a whole line and drop everything that isn't needed.
Replace:
stdin.read(1)
with
stdin.readline().strip()[:1]
This will read a line, remove spaces and newlines and just keep the first character.
Try this ...
import sys
buffer = []
while True:
userinput = sys.stdin.readline().rstrip('\n')
if userinput == 'quit':
break
else:
buffer.append(userinput)
import sys
userinput = sys.stdin.readline()
betAmount = int(userinput)
print betAmount
This works on my system. I checked int('23\n') would result in 23.

How can I edit a string that was printed to stdout?

How can I edit a string that I just printed? For example, for a countdown counter (First prints 30, then changes it to 29 and so on)
Thanks.
Print a carriage return \r and it will take the cursor back to the beginning on the line. Ensure you don't print a newline \n at the end, because you can't backtrack lines. This means you have have to do something like:
import time
import sys
sys.stdout.write('29 seconds remaining')
time.sleep(1)
sys.stdout.write('\r28 seconds remaining')
(As opposed to using print, which does add a newline to the end of what it writes to stdout.)
If you're targeting Unix/Linux then "curses" makes writing console programs really easy. It handles color, cursor positioning etc. Check out the python wrapper:
http://docs.python.org/library/curses.html
If you're on a xterm-like output device, the way you do this is by OVERWRITING the output. You have to ensure that when you write the number you end with a carriage-return (without a newline), which moves the cursor back to the start of the line without advancing to the next line. The next output you write will replace the current displayed number.
You can not change what you printed. What's printed is printed. But, like bradley.ayers said you can return to the beginning of the line and print something new over the old value.
You can use the readline module, which can also provide customized completion and command history.

Python print statement prints nothing with a carriage return

I'm trying to write a simple tool that reads files from disc, does some image processing, and returns the result of the algorithm. Since the program can sometimes take awhile, I like to have a progress bar so I know where it is in the program. And since I don't like to clutter up my command line and I'm on a Unix platform, I wanted to use the '\r' character to print the progress bar on only one line.
But when I have this code here, it prints nothing.
# Files is a list with the filenames
for i, f in enumerate(files):
print '\r%d / %d' % (i, len(files)),
# Code that takes a long time
I have also tried:
print '\r', i, '/', len(files),
Now just to make sure this worked in python, I tried this:
heartbeat = 1
while True:
print '\rHello, world', heartbeat,
heartbeat += 1
This code works perfectly. What's going on? My understanding of carriage returns on Linux was that it would just move the line feed character to the beginning and then I could overwrite old text that was written previously, as long as I don't print a newline anywhere. This doesn't seem to be happening though.
Also, is there a better way to display a progress bar in a command line than what I'm current trying to do?
Try adding sys.stdout.flush() after the print statement. It's possible that print isn't flushing the output until it writes a newline, which doesn't happen here.
Handling of carriage returns in Linux differs greatly between terminal-emulators.
Normally, one would use terminal escape codes that would tell the terminal emulator to move the virtual "carriage" around the screen (think full-screen programs running over BBS lines). The ones I'm aware of are the VT100 escape codes:
\e[A: up
\e[B: down
\e[C: right
\e[D: left
\e[1~: home
\e[4~: end
Where \e is the escape character, \x1b.
Try replacing all \r's with \e[1~
Also see this post
If your terminal is line-buffered, you may need a sys.stdout.flush() to see your printing if you don't issue a linefeed.

Categories