LISP - String I/O
Modern Lisp - String I/O
Section titled “Modern Lisp - String I/O”String I/O refers to operations that treat strings as sources or sinks of data. This is a powerful feature in Lisp, enabling easy data serialization, parsing, and dynamic string construction. This is fundamental for web development, configuration file management, and generating formatted text.
Reading from Strings: Creating Input Streams
Section titled “Reading from Strings: Creating Input Streams”Common Lisp provides two primary ways to treat a string as an input source.
with-input-from-string Macro
Section titled “with-input-from-string Macro”This is the most common and flexible method. It creates a temporary character input stream from a string, allowing you to use standard stream functions like read, read-line, and read-char on it. The stream is automatically closed when the macro’s body exits.
;; Example: Parsing a string of numbers(let ((data-string "10 20 30.5 an-atom")) (with-input-from-string (s data-string) (loop for item = (read s nil :eof) until (eq item :eof) do (format t "Read item: ~a (Type: ~a)~%" item (type-of item)))))Output
Section titled “Output”Read item: 10 (Type: (INTEGER 0 4611686018427387903))Read item: 20 (Type: (INTEGER 0 4611686018427387903))Read item: 30.5 (Type: SINGLE-FLOAT)Read item: AN-ATOM (Type: SYMBOL)The (read s nil :eof) pattern is idiomatic Lisp for reading objects until the end of a stream.
read-from-string Function
Section titled “read-from-string Function”This function parses one Lisp object from a string. It’s useful for quick parsing but less flexible than a full stream.
Security Warning: read and read-from-string are powerful because they use the Lisp reader, which can execute code (e.g., #.(+ 1 2)). Never use them on untrusted user input. For untrusted data, parse it line-by-line with read-line and process it safely.
(let* ((str "(list 1 2 3) and more") (obj (read-from-string str))) (print obj))
;; It also returns the position where reading stopped.(multiple-value-bind (expr pos) (read-from-string str) (format t "Expression: ~s~%Stopped at index: ~d~%" expr pos))Output
Section titled “Output”(LIST 1 2 3)Expression: (LIST 1 2 3)Stopped at index: 13Writing to Strings: Creating Output Streams
Section titled “Writing to Strings: Creating Output Streams”Similarly, Lisp provides elegant ways to capture output into a string.
format Function
Section titled “format Function”format is Lisp’s versatile printing and string formatting powerhouse. By passing nil as its first argument, it returns the formatted result as a new string instead of printing to a stream.
(let* ((name "Alice") (items 3) (message (format nil "Hello, ~a! You have ~d item~:p in your cart." name items))) (print message))
;; Demonstrating with 1 item to show the pluralization directive `~:p`(let ((message-singular (format nil "Hello, Bob! You have ~d item~:p." 1))) (print message-singular))Output
Section titled “Output”"Hello, Alice! You have 3 items in your cart.""Hello, Bob! You have 1 item."with-output-to-string Macro
Section titled “with-output-to-string Macro”When you need to build a string incrementally from multiple print, write, or format calls, this macro is the perfect tool. It creates a temporary character output stream and returns everything written to it as a single string.
;; Example: Generating a simple HTML list(let ((items '("Apples" "Bananas" "Cherries"))) (let ((html (with-output-to-string (s) (write-string "<ul>~%" s) (dolist (item items) (format s " <li>~a</li>~%" item)) (write-string "</ul>" s)))) (print html)))Output
Section titled “Output”"<ul> <li>Apples</li> <li>Bananas</li> <li>Cherries</li></ul>"Practical Application and Best Practices
Section titled “Practical Application and Best Practices”readvs.read-line: Usereadwhen your string contains valid Lisp data structures you want to parse directly. Useread-linewhen dealing with plain text, line-oriented data (like logs or CSV files), or untrusted input.- Performance: For building complex strings in a loop,
with-output-to-stringis generally more efficient than repeated string concatenation. - Integration: The beauty of Lisp’s stream-based I/O is its uniformity. A function that takes a stream argument can work on files, strings, or network connections without modification. This makes code highly reusable.
- Safety First: Re-iterating the security warning: never pass raw, un-sanitized input from an external source (like a web request) to
readorread-from-string.