输入/输出
编辑教程输入/输出
通过前面章节的学习,你已经可以在Scheme的交互式前端中编写并执行程序了。在本章中,我讲介绍如何输入和输出。使用这个特性,你可以从文件中读取数据或向文件中写入数据。
从文件输入
open-input-file,read-char和eof-object?
函数(open-input-file filename)可以用于打开一个文件。此函数返回一个用于输入的端口。
函数(read-char port)用于从端口中读取一个字符。当读取到文件结尾(EOF)时,此函数返回eof-object,你可以使用eof-object?来检查。
函数(close-input-port port)用于关闭输入端口。[代码片段1]展示了一个函数,该函数以字符串形式返回了文件内容。
(define (read-file file-name)
(let ((p (open-input-file file-name)))
(let loop((ls1 '()) (c (read-char p)))
(if (eof-object? c)
(begin
(close-input-port p)
(list->string (reverse ls1)))
(loop (cons c ls1) (read-char p))))))
比如,在[范例1]中展示的结果就是将[代码片段1]应用于文件hello.txt。由于换行符是由'\n'表示的,这就很容易阅读。然而,像格式化输出[范例2],我们也可使用display函数。
Hello world!
Scheme is an elegant programming language.
[范例1]
(cd "C:\\doc")
(read-file "hello.txt")
;Value 14: "Hello world!\nScheme is an elegant programming language.\n"
[范例2]
(display (read-file "hello.txt"))
Hello world!
Scheme is an elegant programming language.
;Unspecified return value
语法call-with-input-file和with-input-from-file
你通过使用语法call-with-input-file和with-input-from-file来打开文件以供读取输入。这些语法是非常方便的,因为它们要处理错误。
(call-with-input-file filename procedure)
该函数将名为filename的文件打开以供读取输入。函数procedure接受一个输入端口作为参数。文件有可能再次使用,因此当procedure结束时文件不会自动关闭,文件应该显式地关闭。[代码片段1]可以按照[代码片段2]那样用call-with-input-file编写。
(define (read-file file-name)
(call-with-input-file file-name
(lambda (p)
(let loop((ls1 '()) (c (read-char p)))
(if (eof-object? c)
(begin
(close-input-port p)
(list->string (reverse ls1)))
(loop (cons c ls1) (read-char p)))))))
(with-input-from-file filename procedure) 该函数将名为filename的文件作为标准输入打开。函数procedure不接受任何参数。当procedure退出时,文件自动被关闭。[代码片段3]展示了如何用with-input-from-file来重写[代码片段1]。
(define (read-file file-name)
(with-input-from-file file-name
(lambda ()
(let loop((ls1 '()) (c (read-char)))
(if (eof-object? c)
(list->string (reverse ls1))
(loop (cons c ls1) (read-char)))))))
read
函数(read port)从端口port中读入一个S-表达式。用它来读诸如”paren.txt”中带括号的内容就很方便。
'(Hello world!
Scheme is an elegant programming language.)
'(Lisp is a programming language ready to evolve.)
[代码片段4]
(define (s-read file-name)
(with-input-from-file file-name
(lambda ()
(let loop ((ls1 '()) (s (read)))
(if (eof-object? s)
(reverse ls1)
(loop (cons s ls1) (read)))))))
下面展示了用s-read读取”paren.txt”的结果。
(s-read "paren.txt")
⇒ ((quote (hello world! scheme is an elegant programming language.))
(quote (lisp is a programming language ready to evolve.)))
练习1
编写函数(read-lines),该函数返回一个由字符串构成的表,分别代表每一行的内容。在Scheme中,换行符是由#\Linefeed表示。下面演示了将该函数用于”hello.txt”的结果。
(read-lines "hello.txt") ⇒ ("Hello world!" "Scheme is an elegant programming language.")
输出至文件
打开一个用于输出的port
输出有和输入类似的函数,比如:
(open-output-file filename)
该函数打开一个文件用作输出,放回该输出端口。
(close-output-port port)
关闭用于输出的端口。
(call-with-output-file filename procedure)
打开文件filename用于输出,并调用过程procedure。该函数以输出端口为参数。
(with-output-to-file filename procedure)
打开文件filename作为标准输出,并调用过程procedure。该过程没有参数。当控制权从过程procedure中返回时,文件被关闭。
用于输出的函数
下面的函数可用于输出。如果参数port被省略的话,则输出至标准输出。
(write obj port) | 该函数将obj输出至port。字符串被双引号括起而字符具有前缀#\。 |
---|---|
(display obj port) | 该函数将obj输出至port。字符串不被双引号括起而字符不具有前缀#\。 |
(newline port) | 以新行起始。 |
(write-char char port) | 该函数向port写入一个字符。 |
练习2
编写函数(my-copy-file)实现文件的拷贝。
练习3
编写函数(print-line),该函数具有任意多的字符作为参数,并将它们输出至标准输出。输出的字符应该用新行分隔。
选择支付方式:
备注:
转账时请填写正确的金额和备注信息,到账由人工处理,可能需要较长时间