2

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".
glennsl
  • 28,186
  • 12
  • 57
  • 75

2 Answers2

3

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))
octachron
  • 17,178
  • 2
  • 16
  • 23
3

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).

coredump
  • 37,664
  • 5
  • 43
  • 77