Printf.printf "%d\n" (a - b));; On the fi" /> Printf.printf "%d\n" (a - b));; On the fi" /> Printf.printf "%d\n" (a - b));; On the fi"/>

Receiving Stdlib.Scanf.Scan_failure : character '\\n'

158 Views Asked by At

I downloaded and executed utop as guided here, and I ran the following code:

Scanf.scanf "%d %d" (fun a b -> Printf.printf "%d\n" (a - b));;

On the first time I input 3 1, it worked fine, giving 2 - : unit = (),

but after the second try with the same input, it keeps on giving the message:

Exception:
Stdlib.Scanf.Scan_failure
"scanf: bad input at char number 3: character '\\n' is not a decimal digit".
3

There are 3 best solutions below

1
octachron On

Scanf consumes as little input as possible.

If you evaluate

Scanf.scanf "%d %d" (fun a b -> Printf.printf "%d\n" (a - b))

and send to the standard input

3 1\n

Scanf reads and consumes the 3 1 prefix and leaves the newline character \n in the input buffer. Then the next call to

Scanf.scanf "%d %d" (fun a b -> Printf.printf "%d\n" (a - b))

will be stuck on this remaining character and fail with

Stdlib.Scanf.Scan_failure
"scanf: bad input at char number 3: character '\\n' is not a decimal digit".

In this situation, you can consume this \n character with either

Scanf.scanf "\n" ();
Scanf.scanf "%d %d" (-);;

or

Scanf.scanf "\n%d %d" (-);;

However, a better solution is probably to add a newline to your input format:

Scanf.scanf "%d %d\n" (fun a b -> Printf.printf "%d\n" (a - b))
0
coredump On

To extend on the existing answer, I think it is better if the scanner doesn't expect a newline at the beginning ("\n%d %d") or the end ("%d %d\n") of the pattern, because typically you read data from a stream that doesn't start with a newline and may not end with a newline (e.g. end of file).

I'd suggest splitting the input into lines and scanning them individually. Also, it is preferable to define smaller functions that do one thing each instead of trying to mix everything in a single big function.

For example, first let's define a function that scans two integers:

# let scan_couple str =
     Scanf.sscanf str "%d %d" (fun a b -> (a, b))

val scan_couple : string -> int * int = <fun>

It works as follows:

# scan_couple "42 69";;
- : int * int = (42, 69)

Nice, now let's define a function that scans the next couple from a channel, assuming couples are entries separated by newlines:

# let scan_next_couple c =
    match (In_channel.input_line c) with
      None -> None
    | Some line -> Some (scan_couple line);;

val scan_next_couple : In_channel.t -> (int * int) option = <fun>

Hopefully each definition is a bit simpler to understand. You still have to handle exceptions if an entry does not match the scanner format, etc. You may want to handle all the possible exceptions listed in the Scanf manual:

let scan_next_couple c =
  match (In_channel.input_line c) with
  | None -> None
  | Some line ->
    try Some (scan_couple line) with
    | Scanf.Scan_failure _
    | Failure _
    | End_of_file
    | Invalid_argument _ -> None

But then you cannot distinguish between an end of file and a single line that is malformed, which can be a problem. Depending on how much effort you want to spend on it, you can be more or less robust to errors here (e.g. maybe wrap option values in a result type, or define another type).

0
Murilo Perrone On

Unlike the scanf from C/C++/Go, which can ignore the \n and \r characters, the OCaml version of scanf is sensible to line breaks. You may try adding \n to the end of your scanf format (like "%d %d\n") and that may work, but frequently the input may lack the line break of it's last line, and we may not have control over it.

Hence, what I prefer is to properly read lines individually with read_line() and then parse that string with sscanf (instead of scanf).

let line = read_line()
let (a, b) = Scanf.sscanf line "%d %d" (fun a b -> (a, b));;
(*                 ^^^^^^            ^ no line break needed *)

And in case you need to read a file until EOF is reached, read_line() also allows the code to be simpler:

let maybe_read_line () =
  try Some(read_line())
  with End_of_file -> None

let quit_loop = ref false;;
while not !quit_loop  do
    match maybe_read_line () with
    | None -> quit_loop := true;
    | Some (line) ->
        let (a, b) = Scanf.sscanf line "%d %d" (fun a b -> (a, b)) in
        Printf.printf "%02d:%02d\n" a b;
done