racket/collects/scribblings/guide/io.scrbl
Matthew Flatt 39cedb62ed v3.99.0.2
svn: r7706
2007-11-13 12:40:00 +00:00

254 lines
7.6 KiB
Racket

#lang scribble/doc
@require[scribble/manual]
@require[scribble/struct]
@require[scribble/eval]
@require[mzlib/process]
@require["guide-utils.ss"]
@define[(twocolumn a b)
(make-table #f
(list (list (make-flow (list a))
(make-flow (list (make-paragraph (list (hspace 1)))))
(make-flow (list b)))))]
@interaction-eval[(print-hash-table #t)]
@title[#:tag "i/o" #:style 'toc]{Input and Output}
A Scheme @defterm{port} represents an input or output stream, such as
a file, a terminal, a TCP connection, or an in-memory string. More
specifically, an @defterm{input port} represents a stream from which a
program can read data, and an @defterm{output port} represents a
stream for writing data.
@local-table-of-contents[]
@;------------------------------------------------------------------------
@section{Varieties of Ports}
Various functions create various kinds of ports. Here are a few
examples:
@itemize{
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@item{@bold{Files:} The @scheme[open-output-file] function opens a
file for writing, and @scheme[open-input-file] opens a file for
reading.
@interaction-eval[(define old-dir (current-directory))]
@interaction-eval[(current-directory (find-system-path 'temp-dir))]
@interaction-eval[(when (file-exists? "data") (delete-file "data"))]
@examples[
(define out (open-output-file "data"))
(display "hello" out)
(close-output-port out)
(define in (open-input-file "data"))
(read-line in)
(close-input-port in)
]
@interaction-eval[(delete-file "data")]
Instead of having to match @scheme[open-input-file] and
@scheme[open-output-file] calls, most Scheme programmers will instead
use @scheme[call-with-output-file], which takes a function to call
with the output port; when the function returns, the port is closed.
@examples[
(call-with-output-file "data"
(lambda (out)
(display "hello" out)))
(call-with-input-file "data"
(lambda (in)
(read-line in)))
]
@interaction-eval[(delete-file "data")]
@interaction-eval[(current-directory old-dir)]}
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@item{@bold{Strings:} The @scheme[open-output-string] function creates
a port that accumulates data into a string, and @scheme[get-output-string]
extracts the accumulated string. The @scheme[open-input-string] function
creates a port to read from a string.
@examples[
(define p (open-output-string))
(display "hello" p)
(get-output-string p)
(read-line (open-input-string "goodbye\nfarewell"))
]}
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@item{@bold{TCP Connections:} The @scheme[tcp-connect] function
creates both an input port and an output port for the client side of
a TCP communication. The @scheme[tcp-listen] function creates a
server, which accepts connections via @scheme[tcp-accept].
@examples[
(eval:alts (define server (tcp-listen 12345)) (void))
(eval:alts (define-values (c-in c-out) (tcp-connect "localhost" 12345)) (void))
(eval:alts (define-values (s-in s-out) (tcp-accept server))
(begin (define-values (s-in c-out) (make-pipe))
(define-values (c-in s-out) (make-pipe))))
(display "hello\n" c-out)
(read-line s-in)
(close-output-port c-out)
(read-line s-in)
]}
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@item{@bold{Process Pipes:} The @scheme[process] function runs a new
process at the OS level and returns ports that correspond to the
subprocess's stdin, stdout, and stderr. (The first three arguments
can be certain kinds of existing ports to connect directly to the
subprocess, instead of creating new ports.)
@examples[
(eval:alts
(define-values (p stdout stdin stderr)
(subprocess #f #f #f "/usr/bin/wc" "-w"))
(define-values (p stdout stdin stderr)
(values #f (open-input-string " 3") (open-output-string) (open-input-string ""))))
(display "a b c\n" stdin)
(close-output-port stdin)
(read-line stdout)
(close-input-port stdout)
(close-input-port stderr)
]}
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@item{@bold{Internal Pipes:} The @scheme[make-pipe] function returns
two ports that are ends of a pipe. This kind of pipe is internal to
Scheme, and not related to OS-level pipes for communicating between
different processes.
@examples[
(define-values (in out) (make-pipe))
(display "garbage" out)
(close-output-port out)
(read-line in)
]}
}
@;------------------------------------------------------------------------
@section{Default Ports}
For most simple I/O functions, the target port is an optional
argument, and the default is the @defterm{current input port} or
@defterm{current output port}. Furthermore, error messages are written
to the @defterm{current error port}, which is an output port. The
@scheme[current-input-port], @scheme[current-output-port], and
@scheme[current-error-port] functions return the corresponding current
ports.
@examples[
(display "Hi")
(code:line (display "Hi" (current-output-port)) (code:comment #, @t{the same}))
]
If you start the @exec{mzscheme} program in a terminal, then the
current input, output, and error ports are all connected to the
terminal. More generally, they are connected to the OS-level stdin,
stdout, and stderr. In this guide, the examples show output written to
stdout in purple, and output written to stderr in red italics.
@defexamples[
(define (swing-hammer)
(display "Ouch!" (current-error-port)))
(swing-hammer)
]
The current-port functions are actually @tech{parameters}, which means
that their values can be set with @scheme[parameterize].
@moreguide["parameters"]{parameters}
@examples[
(let ([s (open-output-string)])
(parameterize ([current-error-port s])
(swing-hammer)
(swing-hammer)
(swing-hammer))
(get-output-string s))
]
@; ----------------------------------------------------------------------
@section{Reading and Writing Scheme Data}
As noted throughout @secref["datatypes"], Scheme provides two
ways to print an instance of a built-in value:
@itemize{
@item{ @scheme[write], which prints a value in the same way that is it
printed for a @tech{REPL} result; and }
@item{@scheme[display], which tends to reduce a value to just its
character or byte content---at least for those datatypes that
are primarily about characters or bytes, otherwise it falls
back to the same output as @scheme[write].}
}
@twocolumn[
@interaction[
(write 1/2)
(write #\x)
(write "hello")
(write #"goodbye")
(write '|dollar sign|)
(write '("alphabet" soup))
(write write)
]
@interaction[
(display 1/2)
(display #\x)
(display "hello")
(display #"goodbye")
(display '|dollar sign|)
(display '("alphabet" soup))
(display write)
]
]
The @scheme[printf] function supports simple formatting of data and
text. In the format string supplied to @scheme[printf], @litchar{~a}
@scheme[display]s the next argument, while @litchar{~s}
@scheme[write]s the next argument.
@defexamples[
(define (deliver who what)
(printf "Value for ~a: ~s" who what))
(deliver "John" "string")
]
An advantage of @scheme[write] is that many forms of data can be
read back in using @scheme[read].
@examples[
(define-values (in out) (make-pipe))
(write "hello" out)
(read in)
(write '("alphabet" soup) out)
(read in)
(write #hash((a . "apple") (b . "banana")) out)
(read in)
]
@; ----------------------------------------------------------------------
@subsection{Serialization}
@; ----------------------------------------------------------------------
@section{Bytes versus Characters}