第九章 輸入/輸出

2018-02-24 15:45 更新

9.1?簡介

通過前面章節(jié)的學(xué)習(xí),你已經(jīng)可以在Scheme的交互式前端中編寫并執(zhí)行程序了。在本章中,我講介紹如何輸入和輸出。使用這個(gè)特性,你可以從文件中讀取數(shù)據(jù)或向文件中寫入數(shù)據(jù)。

9.2?從文件輸入

open-input-file,read-char和eof-object?

函數(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

9.2.2?語法call-with-input-file和with-input-from-file

你通過使用語法call-with-input-filewith-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)))))))

9.2.3?read

函數(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.")

9.3?輸出至文件

9.3.1?打開一個(gè)用于輸出的port

輸出有和輸入類似的函數(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)閉。

9.3.2?用于輸出的函數(shù)

下面的函數(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)該用新行分隔。

9.4?小結(jié)

因?yàn)镾cheme的IO設(shè)施非常的小,所以本章也十分短。下一章中,我會(huì)講解賦值。

9.5?習(xí)題解答

9.5.1?答案1

(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." "")

9.5.2?答案2

(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)))))))

9.5.3?答案3

(define (print-lines . lines)
  (let loop((ls0 lines))
    (if (pair? ls0)
        (begin
         (display (car ls0))
         (newline)
         (loop (cdr ls0))))))
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)