8.6 I/O Patterns

► |  | The Racket Guide

1 | Welcome to Racket
2 | Racket Essentials
3 | Built-In Datatypes
4 | Expressions and Definitions
5 | Programmer-Defined Datatypes
6 | Modules
7 | Contracts
8 | Input and Output
9 | Regular Expressions
10 | Exceptions and Control
11 | Iterations and Comprehensions
12 | Pattern Matching
13 | Classes and Objects
14 | Units
15 | Reflection and Dynamic Evaluation
16 | Macros
17 | Creating Languages
18 | Concurrency and Synchronization
19 | Performance
20 | Parallelism
21 | Running and Creating Executables
22 | More Libraries
23 | Dialects of Racket and Scheme
24 | Command-Line Tools and Your Editor of Choice
| Bibliography
| Index

▼ | 8 | Input and Output

8.1 | Varieties of Ports
8.2 | Default Ports
8.3 | Reading and Writing Racket Data
8.4 | Datatypes and Serialization
8.5 | Bytes, Characters, and Encodings
8.6 | I/ O Patterns

top contents ← prev up next →

#### 8.6`` I/O Patterns🔗ℹ

> > > (require racket/port) is needed for #lang racket/base.

For these examples, say you have two files in the same directory as your program, "oneline.txt" and "manylines.txt".

> "oneline.txt"

> > I am one line, but there is an empty line after this one.

> "manylines.txt"

> > I am
> > a message
> > split over a few lines.

If you have a file that is quite small, you can get away with reading in the file as a string:

Examples:

> > (define file-contents
> (port->string (open-input-file "oneline.txt") #:close? #t))
> > (string-suffix? file-contents "after this one.")
> #f
> > (string-suffix? file-contents "after this one.\n")
> #t
> > (string-suffix? (string-trim file-contents) "after this one.")
> #t

We use port->string from racket/port to do the reading to a string: the #:close? #t keyword argument ensures that our file is closed after the read.

We use string-trim from racket/string to remove any extraneous whitespace at the very beginning and very end of our file. (Lots of formatters out there insist that text files end with a single blank line).

See also read-line if your file has one line of text.

If, instead, you want to process individual lines of a file, then you can use for with in-lines:

> > (define (upcase-all in)
> (for ([l (in-lines in)])
> (display (string-upcase l))
> (newline)))
> > (upcase-all (open-input-string
> (string-append
> "Hello, World!\n"
> "Can you hear me, now?")))
> HELLO, WORLD!
> CAN YOU HEAR ME, NOW?

You could also combine computations over each line. So if you want to know how many lines contain “m”, you could do:

Example:

> > (with-input-from-file "manylines.txt"
> (lambda ()
> (for/sum ([l (in-lines)]
> #:when (string-contains? l "m"))
> 1)))
> 2

Here, with-input-from-file from racket/port sets the default input port to be the file "manylines.txt" inside the thunk. It also closes the file after the computation has been completed (and in a few other cases).

However, if you want to determine whether “hello” appears in a file, then you could search separate lines, but it’s even easier to simply apply a regular expression (see Regular Expressions) to the stream:

> > (define (has-hello? in)
> (regexp-match? #rx"hello" in))
> > (has-hello? (open-input-string "hello"))
> #t
> > (has-hello? (open-input-string "goodbye"))
> #f

If you want to copy one port into another, use copy-port from racket/port, which efficiently transfers large blocks when lots of data is available, but also transfers small blocks immediately if that’s all that is available:

> > (define o (open-output-string))
> > (copy-port (open-input-string "broom") o)
> > (get-output-string o)
> "broom"

top contents ← prev up next →