Printing ANSI escapes has unexpected behaviour (bug?)

I have two logically equivalent toy programs; one in C and once in OCaml:

/* clear.c */

#include <stdio.h>
#include <unistd.h>

int main()
{
	for (int n=0 ; n<100 ; n++) {
		puts("\x1b[H\x1b[J");
		printf("%d\n", n);
		sleep(1);
	}
	return 0;
}

and

(* clear.ml *)

#use "topfind";;
#require "unix";;

let () =
for n = 0 to 100 do
    print_endline "\x1b[H\x1b[2J";
    Printf.printf "%d\n" n;
    Unix.sleep 1
done

the C version behaves as expected, the OCaml version clears the screen but seems to fail at updating it. I wonder if this is a bug or an implementation detail of print_* functions.

1 Like

Hi @hyphenrf,

This seems to be due to the buffering that is introduced by the OCaml implementation of printf. Adding %! to the end of the format string in the loop – to explicitly flush the buffer on each iteration – fixes the issue for me.

let () =
  for n = 0 to 100 do
    print_endline "\x1b[H\x1b[2J";
    Printf.printf "%d\n%!" n;
    Unix.sleep 1
  done
1 Like

Just for curiosity’s sake; is buffering applied also to the print_* functions? Or is it just a Printf impl. detail?
Because using print_int instead does still produce this unexpected behaviour.
can flushing be done without Printf (i.e. print_int)?

Providing you switch out the printf usage for the equivalent of print_int and print_endline, it should work as intended:

let () =
  for n = 0 to 100 do
    print_endline "\x1b[H\x1b[2J";
    print_int n;
    print_endline "";
    Unix.sleep 1
  done

This works because print_endline performs a flush. The channels provided by the standard library are buffered, and these buffers can be flushed with either Stdlib.flush or Stdlib.flush_all.

1 Like

Thank you. This clarifies everything

1 Like

Just to add some background info that most probably know already …

C stdio output does use buffering, by default anyway, and to be specific printf() is buffered. It is however “line buffered” for TTY devices or for stderr - when a line feed is output, the buffer is then flushed. For other devices - pipes, disk files - not open on stderr, it’s block buffered, so the flush waits until the buffer fills, is explicitly flushed or the file is closed.

1 Like