使用Scheme从文件中读取

时间:2013-05-02 10:13:03

标签: file-io scheme

我正在尝试使用方案从文件中读取,并将其内容放入列表中。

问题是如何删除问号,数字和保留单词。我应该每次使用循环来检查吗?如果不是这样的话,我如何才能从“阅读”中获取下一个单词的内容?

我尝试使用此代码解决它,但我找不到一种方法来调用“读取”直到获得文件结束;

(define Project
  (lambda (fileName)
    (if (null? fileName) 
        'error
        (readNext (open fileName) '()))))

(define readNext
  (lambda (fc tmp)
    (if (null? (read fc) "#<eof>")
        tmp
        (readNext fc (cons (read fc) tmp)))))

5 个答案:

答案 0 :(得分:11)

导入文本的最佳推荐方法是编辑文件并将其另存为定义变量的方案文件:

(define data "the text in
mydata.scm here")

然后致电:

(load "mydata.scm")

很多时候,并非每个数据文件都可以编辑并保存为方案文件,并且当换行符自动转义时,双引号不能,这会在加载文件时产生问题。

一些特定于实现的技术是:

;Chicken
(use utils)
(read-all "mydata.txt")

;Racket
(file->string "mydata.txt")

更便携的功能是:

;works in chicken-csi and Racket
(define (readlines filename)
  (call-with-input-file filename
    (lambda (p)
      (let loop ((line (read-line p))
                 (result '()))
        (if (eof-object? line)
            (reverse result)
            (loop (read-line p) (cons line result)))))))

运行可执行编译的chicken-csc将导致错误,因为读取行需要额外的文件。

读取文件的最便携方式是此功能:

;works in Chicken, Racket, SISC
;Read a file to a list of chars
(define (file->char_list path)
 (call-with-input-file path
   (lambda (input-port)
     (let loop ((x (read-char input-port)))
       (cond 
        ((eof-object? x) '())
        (#t (begin (cons x (loop (read-char input-port))))))))))

此功能在各种实现中相当快速且可移植。所需要的只是将char_list转换为字符串。

最简单的方法是:

;may not work if there is limit on arguments
(apply string (file->char_list "mydata.txt"))

catch是一些实现对可以传递给函数的参数数量有限制。 2049个字符列表在鸡肉中不起作用。

另一种方法是:

;works in Chicken, Racket
(foldr (lambda (x y) (string-append (string x) y)) "" (file->char_list "mydata.txt"))

问题是:首先,尽管可以定义折叠器,但它并不是普遍认可的(SISC)。其次,由于追加每个字符,这种方法非常慢。

我编写了接下来的两个函数来将一个字符列表切割成嵌套列表,直到最低级别不超过Chicken中的最大参数计数。第三个函数遍历嵌套的char列表,​​并使用字符串string-append:

返回一个字符串
(define (cleave_at n a)
  (cond
   ((null? a) '())
   ((zero? n) (list '() a))
   (#t 
    ((lambda (x)
      (cons (cons (car a) (car x)) (cdr x)))
     (cleave_at (- n 1) (cdr a))))))

(define (cleave_binary_nest n a)
 (cond
  ((equal? n (length a)) (list a))
  (#t 
   ((lambda (x)
     (cond
      ((> (length (car x)) n) (map (lambda (y) (cleave_binary_nest n y)) x))
      (#t x)))
    (cleave_at (floor (/ (length a) 2)) a)))))

(define (binary_nest_char->string a)
 (cond
  ((null? a) "")
  ((char? (car a)) (apply string a))
  (#t (string-append
    (binary_nest_char->string (car a)) (binary_nest_char->string (cdr a))))))

该函数的调用如下:

;Works in Racket, Chicken, SISC
;faster than foldr method (3x faster interpreted Chicken) (30x faster compiled Chicken) (125x faster Racket gui)
(binary_nest_char->string (cleave_binary_nest 2048 (file->char_list "mydata.txt")))

要减少字母字符和空格,还有两个功能:

(define (alphaspace? x)
 (cond
  ((and (char-ci>=? x #\a) (char-ci<=? x #\z)) #t)
  ((equal? x #\space) #t)
  (#t #f)))

(define (filter pred lis)
  ; if lis is empty
  (if (null? lis)
    ; return an empty list
    '()
    ; otherwise, if the predicate is true on the first element
    (if (pred (car lis))
      ; return the first element concatenated with the
      ; result of calling filter on the rest of lis
      (cons (car lis) (filter pred (cdr lis)))
      ; otherwise (if the predicate was false) just
      ; return the result of filtering the rest of lis
      (filter pred (cdr lis)))))

(define data (file->char_list "mydata.txt"))
(define data_alphaspace (filter alphaspace? data))
(define result (binary_nest_char->string (cleave_binary_nest 2048 data_alphaspace)))

这适用于Racket,Chicken(解释和编译)和SISC(Java)。每种方言都适用于Linux,Mac(OS X)和Windows。

答案 1 :(得分:5)

也许这会让你开始。

(define (file->list-of-chars file)
  (with-input-from-file file
    (lambda ()
      (let reading ((chars '()))
        (let ((char (read-char)))
          (if (eof-object? char)
              (reverse chars)
              (reading (cons char chars))))))))

答案 2 :(得分:1)

我不知道如何能够像其他答案那样谈论可移植性,但如果您使用Racket,它将如下所示:

(file->lines "somefile")

答案 3 :(得分:0)

使用SRFI-42中的“ list-ec”从文件中读取行:

(use srfi-42) ; Chicken
  or
(require srfi/42) ; Racket

(define (file->lines filename)
  (call-with-input-file filename
    (lambda (p)
      (list-ec (:port line p read-line) line))))

使用SRFI-13和SRFI-14解析行:

(use srfi-13) (use srfi-14) ; Chicken
  or
(require srfi/13) (require srfi/14) ; Racket

(string-tokenize "hi; ho")
("hi;" "ho")

(string-tokenize "hi; ho" char-set:letter)
("hi" "ho")

答案 4 :(得分:0)

更新:R7RS

根据新的(2013)标准R7RS (PDF),Scheme现在提供函数read-string,该函数标准化并简化了对该线程问题的回答。让我们在名为mydata.txt的简单测试文件中进行演示:

bash$ cat mydata.txt
"Hello world!"
Is this microphone on?
Testing 1 2 3...

要将整个文件读取为一个字符串,可以在Scheme REPL上使用read-string,如下所示:

> (read-string 100 (open-input-file "mydata.txt"))
"\"Hello world!\"\nIs this microphone on?\nTesting 1 2 3...\n"

第二行当然是REPL,它向您显示1read-string返回的字符串。请注意,引号已正确转义,这是威尔答案中解决的问题之一。

顺便说一句:read-string的第一个参数表示要读取的最大字节数。确保将其设置为一个反映您自己文件实际大小的值,以免它们被截断。

可移植性

我用Scheme的Chibi,Chicken和Gauche实现验证了上述解决方案。至少从理论上讲,它还应与所有其他符合R7RS的Scheme一起使用。声称合规的实现的网站schemers.org maintains a table。显然,我不能保证他们的主张的准确性。

也可能有趣

除了read-string,R7RS sandard及其实现还提供了read-bytevector函数,该函数在二进制文件上的工作方式相同。您可以使用它将二进制文件读入字节向量。

这里要提到的最后一个R7RS函数是read-line,它一次读取一个文本文件。因此,如果您想将文件读入行列表中(例如Python的readlines函数),则可以实现readlines的Scheme版本,如下所示:

(define (readlines file)
 (let ((infile (open-input-file file)))
   (let loop ((lines '())
              (next-line (read-line infile)))
    (if (eof-object? next-line)
        (begin (close-input-port infile) 
               (reverse lines))
        (loop (cons next-line  lines) 
              (read-line infile))))))

让我们在REPL中对其进行测试:

> (define ls (readlines "mydata.txt"))
> (car ls)
"\"Hello world!\""
> (cadr ls)
"Is this microphone on?"
> (caddr ls)
"Testing 1 2 3..."

希望此更新对您有帮助。