How to read space delimited words from a stream?

In Common Lisp, is there an easy way to read a single space-delimited word from an input stream? In fact, I'm looking for something that is equivalent to the C scanf("%s", somevar);

.

I came up with the following:

(defun read-word-from-stream (in)
  (peek-char t in) ; skip initial whitespace
  (do ((str (make-array 16 :fill-pointer 0 :element-type 'standard-char :adjustable t) 
            (progn (vector-push-extend (read-char in) str) str)))
      ((let ((c (peek-char nil in))) 
            (or (char= c #\Newline) (char= c #\Space))) str)))

      

... which, while being functional for my limited needs, feels a bit kludgey for an operation so simple. Ideally, I would have a way to do this for me, but what would be the cleanest and shortest correct way to do this using the shared Lisp libraries available (preferably something that works with flexi streams)?

+3


source to share


1 answer


Using peek-char to detect whitespace

Although I posted in a comment that there is no standard way to do this, in part because there is no universal concept of whitespace. (Your version includes Space and Newline, but what about Tab, Vertical Tab, Carriage Return, etc.?). However, your use of peek-char reminded me that peek-char takes an optional argument of type peek indicating whether to skip spaces or not. If you are using both types of peeking, then when they disagree, you must have typed a whitespace character. This means that you can read up to a space character (where the exact meaning of the space character is implementation-defined) with a function like this:

(defun read-string (&optional (stream *standard-input*))
  (loop
     for c = (peek-char nil stream nil nil)              ; include whitespace
     while (and c (eql c (peek-char t stream nil nil)))  ; skip whitespace
     collect (read-char stream) into letters
     finally (return (coerce letters 'string))))

      

CL-USER> (read-string)
this is some input
"this"

      

I used string (forced letter string) to get the string here, but you can also use with-output-to-string :

(defun read-string (&optional (stream *standard-input*))
  (with-output-to-string (out)
    (loop
       for c = (peek-char nil stream nil nil)
       while (and c (eql c (peek-char t stream nil nil)))
       do (write-char (read-char stream) out))))

      

CL-USER> (read-string)
some more input
"some"

      



Using a blank glossary for spaces

The glossary dictionary for spaces reads:

whitespace n. 1. one or more characters, which are either graphic # \ Space or non-graphic characters such as # \ Newline which only move the print position. 2. a. n. syntax type character that delimits the marker. See section 2.1.4.7 for details. (Whitespace characters). b. adjusted (character) having whitespace [2a] syntax type [2]. from. p. space character [2b].

Based on the first definition, it's easy to define a rough approximation (this doesn't check print position, I'm not sure if there is a portable way to check this):

(defun whitespace-char-p (x)
  (or (char= #\space x)
      (not (graphic-char-p x))))

      

Then this is easy to do:

(defun read-until (test &optional (stream *standard-input*))
  (with-output-to-string (out)
    (loop for c = (peek-char nil stream nil nil)
       while (and c (not (funcall test c)))
       do (write-char (read-char stream) out))))

      

CL-USER> (read-until 'whitespace-char-p)
this is some input
"this"

      

+3


source







All Articles