11

What's the best way to read input from stdin in racket?

In particular I'd like something like cin from c++ or scanf from c where I specify the types of things I want read and they are returned.

Cam
  • 14,930
  • 16
  • 77
  • 128

4 Answers4

10

read-line is easy. To be portable across Unix and Windows, additional option is required.

(read-line (current-input-port) 'any)

Return and linefeed characters are detected after the conversions that are automatically performed when reading a file in text mode. For example, reading a file in text mode on Windows automatically changes return-linefeed combinations to a linefeed. Thus, when a file is opened in text mode, 'linefeed is usually the appropriate read-line mode.

So, 'any is required to be portable when the input port is not a file (standard input).

Test program:

#lang racket
(let loop ()
    (display "Input: ")
    (define a (read-line (current-input-port) 'any))
    (printf "input: ~a, length: ~a, last character: ~a\n"
        a
        (string-length a)
        (char->integer (string-ref a (- (string-length a) 1))))
    (loop))

In Windows, replace (read-line (current-input-port) 'any) with (read-line) and see what happens.

KIM Taegyoon
  • 1,917
  • 21
  • 18
  • 2
    Actually, `read-line` should be portable across Unix and Windows without setting different options (mentioned further down in the docs). – stchang Apr 06 '16 at 16:59
  • This results in: `; string-ref: contract violation ; expected: exact-nonnegative-integer? ; given: -1 ; argument position: 2nd ; other arguments...: ; "" ; Context: ; stdin:1:1 loop` (Racket 6.10.1) – Zelphir Kaltstahl Apr 22 '18 at 11:36
8

You can do pretty much everything you want to... at the low level, I would suggest (read-line) and (read-bytes). For higher-level processing (as e.g. scanf does), I would suggest a regexp-match on the input. For instance

(regexp-match #px" *([0-9]+)" (current-input-port))
John Clements
  • 16,895
  • 3
  • 37
  • 52
2

I'd use the read procedure for the general case. If the data type to be read is known beforehand, use read-char, read-string, read-bytes.

Also, take a look at this implemenation for reading formatted input - a scanf in Scheme.

Óscar López
  • 232,561
  • 37
  • 312
  • 386
  • Read seems to have problems if there are parentheses in the input – Cam Nov 14 '11 at 19:09
  • What kind of problems? it works fine for me, a parenthesized expression is returned as a list of symbols - as expected. And did you look at the library I linked? it's like `scanf` but in Scheme – Óscar López Nov 14 '11 at 19:55
  • For example, try using this as your input: `( 1 2 3`. Then call `(read) (read) (read) (read)`. Instead of yielding four strings (`(`, `1`, `2`, `3`), it yields an error. The library looks great - thanks! I was hoping to find something built-in so I can see what the standard practice is for this sort of thing. John Clements' answer was what I was looking for. I understand what `read` does - it's just not what I was looking for. – Cam Nov 15 '11 at 03:49
  • @ÓscarLópez bad link :c – cat Apr 05 '16 at 14:06
  • @cat I updated it with a similar link, the original is gone for good :( – Óscar López Apr 05 '16 at 14:34
0

Here's a basis for line-by-line processing in Racket Scheme. It doesn't split an input line into multiple words, or do typed input, but this seems like a good place to put it.

(define (get)
  (read-line (current-input-port)))

(define (put . vs)
  (for-each display vs)
  (displayln ""))

(define (sed fn)
  (let ((line (get)))
    (if (not (eof-object? line))
      (begin
        (fn line)
        (sed fn))
      'true)))

(sed (lambda (line)
  (put "Hello, " line)))

Here's one that does split input, also encodes CSV for good measure.

(define (get)
  (read-line (current-input-port)))

(define split string-split)
(define sep ",")
(define enc identity)

(define (enc-csv s)
  (string-append "\"" (string-replace s "\"" "\"\"") "\""))
(define enc enc-csv)

(define (put . vs)
  (displayln (string-join (map enc vs) sep)))

(define (sed fn)
  (let ((line (get)))
    (if (not (eof-object? line))
      (begin
        (fn line)
        (sed fn))
      'true)))

(sed (lambda (line)
  (apply put (split line))))

This works in Racket. I'm not sure how much of it is specific to Racket. It doesn't seem to work in Chicken or Guile.

Sam Watkins
  • 7,819
  • 3
  • 38
  • 38