通過前面章節(jié)的學(xué)習(xí),你已經(jīng)可以在Scheme的交互式前端中編寫并執(zhí)行程序了。在本章中,我講介紹如何輸入和輸出。使用這個(gè)特性,你可以從文件中讀取數(shù)據(jù)或向文件中寫入數(shù)據(jù)。
函數(shù)(open-input-file filename)
可以用于打開一個(gè)文件。此函數(shù)返回一個(gè)用于輸入的端口。函數(shù)(read-char port)
用于從端口中讀取一個(gè)字符。當(dāng)讀取到文件結(jié)尾(EOF)時(shí),此函數(shù)返回eof-object
,你可以使用eof-object?
來檢查。函數(shù)(close-input-port port)
用于關(guān)閉輸入端口。[代碼片段1]展示了一個(gè)函數(shù),該函數(shù)以字符串形式返回了文件內(nèi)容。
[代碼片段1] 以字符串的形式返回文件內(nèi)容
(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]中展示的結(jié)果就是將[代碼片段1]應(yīng)用于文件hello.txt。由于換行符是由'\n'
表示的,這就很容易閱讀。然而,像格式化輸出[范例2],我們也可使用display
函數(shù)。
[文件]hello.txt
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
來打開文件以供讀取輸入。這些語法是非常方便的,因?yàn)樗鼈円幚礤e(cuò)誤。
(call-with-input-file?filename?procedure)
該函數(shù)將名為
filename
的文件打開以供讀取輸入。函數(shù)procedure
接受一個(gè)輸入端口作為參數(shù)。文件有可能再次使用,因此當(dāng)procedure
結(jié)束時(shí)文件不會(huì)自動(dòng)關(guān)閉,文件應(yīng)該顯式地關(guān)閉。[代碼片段1]可以按照[代碼片段2]那樣用call-with-input-file
編寫。
[代碼片段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)
?該函數(shù)將名為filename
的文件作為標(biāo)準(zhǔn)輸入打開。函數(shù)procedure
不接受任何參數(shù)。當(dāng)procedure
退出時(shí),文件自動(dòng)被關(guān)閉。[代碼片段3]展示了如何用with-input-from-file
來重寫[代碼片段1]。
[代碼片段3] with-input-from-file版本
(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)))))))
函數(shù)(read port)
從端口port
中讀入一個(gè)S-表達(dá)式。用它來讀諸如”paren.txt”中帶括號(hào)的內(nèi)容就很方便。
'(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”的結(jié)果。
(s-read "paren.txt")
? ((quote (hello world! scheme is an elegant programming language.))
(quote (lisp is a programming language ready to evolve.)))
練習(xí)1
編寫函數(shù)
(read-lines)
,該函數(shù)返回一個(gè)由字符串構(gòu)成的表,分別代表每一行的內(nèi)容。在Scheme中,換行符是由#\Linefeed
表示。下面演示了將該函數(shù)用于”hello.txt”的結(jié)果。
(read-lines "hello.txt") ? ("Hello world!" "Scheme is an elegant programming language.")
輸出有和輸入類似的函數(shù),比如:
(open-output-file filename)
該函數(shù)打開一個(gè)文件用作輸出,放回該輸出端口。
(close-output-port port)
關(guān)閉用于輸出的端口。
(call-with-output-file?filename?procedure)
打開文件filename
用于輸出,并調(diào)用過程procedure
。該函數(shù)以輸出端口為參數(shù)。
(with-output-to-file?filename?procedure)
打開文件filename
作為標(biāo)準(zhǔn)輸出,并調(diào)用過程procedure
。該過程沒有參數(shù)。當(dāng)控制權(quán)從過程procedure
中返回時(shí),文件被關(guān)閉。
下面的函數(shù)可用于輸出。如果參數(shù)port
被省略的話,則輸出至標(biāo)準(zhǔn)輸出。
(write obj port)
該函數(shù)將obj
輸出至port
。字符串被雙引號(hào)括起而字符具有前綴#\
。
(display obj port)
該函數(shù)將obj
輸出至port
。字符串不被雙引號(hào)括起而字符不具有前綴#\
。
(newline port)
以新行起始。
(write-char char port)
該函數(shù)向port
寫入一個(gè)字符。
練習(xí)2
編寫函數(shù)
(my-copy-file)
實(shí)現(xiàn)文件的拷貝。練習(xí)3
編寫函數(shù)
(print-line)
,該函數(shù)具有任意多的字符作為參數(shù),并將它們輸出至標(biāo)準(zhǔn)輸出。輸出的字符應(yīng)該用新行分隔。
因?yàn)镾cheme的IO設(shè)施非常的小,所以本章也十分短。下一章中,我會(huì)講解賦值。
(define (group-list ls sep)
(letrec ((iter (lambda (ls0 ls1)
(cond
((null? ls0) (list ls1))
((eqv? (car ls0) sep)
(cons ls1 (iter (cdr ls0) '())))
(else (iter (cdr ls0) (cons (car ls0) ls1)))))))
(map reverse (iter ls '()))))
(define (read-lines file-name)
(with-input-from-file file-name
(lambda ()
(let loop((ls1 '()) (c (read-char)))
(if (eof-object? c)
(map list->string (group-list (reverse ls1) #\Linefeed)) ; *
(loop (cons c ls1) (read-char)))))))
示例:
(group-list '(1 4 0 3 7 2 0 9 5 0 0 1 2 3) 0)
;Value 13: ((1 4) (3 7 2) (9 5) () (1 2 3))
(read-lines "hello.txt")
;Value 14: ("Hello world!" "Scheme is an elegant programming language." "")
(define (my-copy-file from to)
(let ((pfr (open-input-file from))
(pto (open-output-file to)))
(let loop((c (read-char pfr)))
(if (eof-object? c)
(begin
(close-input-port pfr)
(close-output-port pto))
(begin
(write-char c pto)
(loop (read-char pfr)))))))
(define (print-lines . lines)
(let loop((ls0 lines))
(if (pair? ls0)
(begin
(display (car ls0))
(newline)
(loop (cdr ls0))))))
更多建議: