文件I/O
来源:互联网 发布:开淘宝店经验 编辑:程序博客网 时间:2024/06/06 03:46
Common Lisp 为读写数据提供了一个流的抽象和一个称为路径名(pathname)的抽象,他们以一种跟操作系统无关的方式来管理文件
特有:读写S表达式
S基本元素是列表和原子(非列表或空列表),列表是括号包围,并且包含任意数量的以空格分开的元素。我们平常说的列表(1 2 3)就是一个S表达式。
一:读取文件数据
Open 基于字符的输入流,你可以将它传给函数以便读取一个或多个字符。你打一个文件就会返回给你一个抽象的流,首先把这个流接收,然后我们对这个流进行操作。
Read-char 读取单个字符
Read-line 读取一行文本,去掉行结束符后作为一个字符串返回。
Read 读取单一的s表达式并返回一个lisp对象。
注意点:
1:有一种情况就是,当你打开的文件不存在时,我们可以设置关键字:if-does-not-exist来指定不同行为,有3个可能的值. :error 报错(默认),:create继续并创建,:NIL返回NIL代替一个流。
2:当不同的格式输出的时候,显示不一样,~s用一种可以再读回去的格式显示输出
CL-USER> (let ((in (open "d:/emacs/io3.db"))) (loop for c = (read-char in nil) while c do(format t "~a" c)) (close in))heal the worldmay TCL-USER> (let ((in (open "d:/emacs/io3.db"))) (loop for c = (read-char in nil) while c do(format t "~s" c)) (close in))#\h#\e#\a#\l#\ #\t#\h#\e#\ #\w#\o#\r#\l#\d#\Return#\Newline#\m#\a#\y#\ T
3:Read-char,read-line,read函数都接受一个可选参数,如下面的(read-line in nil).默认情况下为真,用于指示当遇到文件尾的时候是否报错。一般情况下都设置为NIL,返回他们第三个参数的值。
4:Read方式他跟repl中读取器R的函数是同一个,用于读取lisp源代码,每次调用他会读取单一的S表达式(列表或者非列表及空列表)并跳过空格和注释。然后返回由S表达式代表的lisp对象。字符串中的对象d:/slime/保持原样,而其他的都变成大写。
你如果用read-line读取lisp源文件的话,中文字符会被显示成乱码,因为open的时候是按字符进行读取的流。
CL-USER> (let ((in (open "d:/emacs/.emacs"))) (when in (loop for line = (read in nil) while line do(format t "~a~%" line)) (close in)))(ADD-TO-LIST 'LOAD-PATH D:/slime/)(SETQ INFERIOR-LISP-PROGRAM D:/sbcl/sbcl.exe)(REQUIRE 'SLIME-AUTOLOADS)(SLIME-SETUP '(SLIME-FANCY))T二:读取二进制流
刚才我们已经说了open是返回字符流,也就是一个字节的读,因为最底层肯定都是字节,而现在他是根据一定的编码规则把字节转换为字符,然后作为流再输出来。现在如果你想看最原始的字节,你需要为open指定一个值为'(unsigned-byte 8)的:element-type参数。然后将打开得到的流给read-byte.它将在每次调用时返回0-255的整数,它本身是一个二进制流了,是二进制01格式,只是我们在输出显示的时候将它转化为了易读的十进制数,但是你可以转换成01格式了。
CL-USER> (let ((in (open "d:/emacs/io3.db" :element-type '(unsigned-byte 8)))) (loop for c = (read-byte in nil)while cdo(format t "~a" c)) (close in))10910132116111111131098101T
三:批量读取
用read-sequence可以接受字符流和二进制流。他接受一个序列和流,他会试着用来自流的数据来填充该序列。它返回序列中第一个没有被填充的元素的索引,或是在完全填充的情况下返回该序列的长度。
(read-sequence (list) stream)
四:文件输出
首先还是需要先打开一个文件,只是现在你要标明它是用于output的,:direction :output 来设置。默认情况下他会假设这个文件不存在,如果存在的话,就报错,你可以通过设置符号:if-exists来改变这个行为。如果没有的话,就建立,注意下面四个命令只是对文件不存在时而言。
:supersede 替换以前的文件
:append 保文件写到文件尾
:overwrite 从文件头覆盖原文件
NIL 如果文件存在的话,返回NIL而不是流。
Write-char 回向流中写入一个单一字符
Write-line 写个字符并且紧跟一个换行符。
Write-string 写入一个字符串而不会添加任何行结束符
Prinl generates output for programs,
princ generates output for people.
有两个不同函数只打印一个换行:
Terpri "终止打印" (terminal print)的简称,即无条件打印一个换行。
Fresh-line 打印一个换行,除非流已经在文件一行的开始处。流在文件一行的开始处,就是说你现在往里写,光标在文件的一行的开始。因为其实流就是一个文件的抽象,比方说(read-line in)他会从流中读一行数据,实际上就是从一个文件中读取一行数据,然后每读取一条文件中的光标就执行回车回到下一行的开头,所以我们说(read-line in)会返回一条记录,而对于写的话,我们肯定是往流里面写,也就相当与写到了文件里面。比如执行完(write-string "sb" stream) 字符串sb就到输出文件中了,然后光标在文件中sb的后面。所以一旦碰到换行符,光标就会跳到文件的下一行。
CL-USER> (prin1 "hello world")"hello world""hello world"CL-USER> (princ "hello world")hello world"hello world"
注意当你进行输出的时候千万别忘了关闭当前流,否则你打开文件里面没有任何内容。
CL-USER> (let ((stream (open "d:/emacs/out.db" :direction :output :if-exists :supersede))) (write-string "sb" stream)) "sb"CL-USER> (let ((stream (open "d:/emacs/out.db" :direction :output :if-exists :supersede))) write-string "sb" stream) (close stream)) TCL-USER> (let ((stream (open "d:/emacs/out.db" :direction :output :if-exists :supersede :element-type '(unsigned-byte 8)))) (write-byte 97 stream) (close stream))T 并且文件中显示a五:宏封装文件流
通过上面的例子,你应该明白了,对于文件的处理关键是得到流。像上面每一次的结束都得自己手动关闭流,否则会造成文件句柄不够。并且有的时候没有执行到文件的close程序出错了,就会造成文件同样没有被安全关闭。于是Common lisp定义了一个特殊操作符Unwind-protect来解决了这个问题。With-open-file是Unwind-protect上面的的宏。得到流以后你就可以往里面write和从里面read了。
CL-USER> (with-open-file (stream "d:/emacs/io.db"))NILCL-USER> (with-open-file (stream "d:/emacs/io.db" :direction :output))六:其他IO
Common lisp还支持其他IO,string-stream从一个字符串中读取或写入数据。
make-string-input-stream 里面包含一个string对象,然后返回一个指向这个对象的stream
make-string-output-stream 不需要参数,返回一个stream
你比如下面的在创建一个流的时候,对于文件IO,你要么有个地方可以读到数据如文件,或者你可以直接传递给数据也行,相当于一个水管直接接到了string对象。input的话,可以把string作为对象,然后生成一个指向他的流,但是现在对于output,你没有文件中可以存放数据的地方,那么你把这个流指向什么地方呢?现在的处理就是调用上面的两个构造函数,返回一个流指向所给string,对于这个流无论你写了什么,字符串输出流都将被积累到字符串中,你随后必须调用函数get-output-stream-string来获取该串。我感觉这种类型肯定只是针对小数据而言,肯定有个上限的。
CL-USER> (let ((s (make-string-input-stream "1.23"))) (unwind-protect (read s) (close s)))1.23CL-USER> (let ((s (make-string-output-stream))) (format s "string") (format t "~s" (get-output-stream-string s))) "string"
宏封装的string stream 现在关键是with-output-to-string会返回由get-output-stream-string返回的值。
CL-USER> (with-output-to-string (out) (format out " hello world") (format out "~s" (list 1 3)))" hello world(1 3)"CL-USER> (with-input-from-string (s "1.23") (read s))1.23七:流的拼接技术
提供多种形式的流拼接技术,允许你以几乎任何配置将流拼接在一起
make-broadcast-stream 输出流 make-broadcast-stream构造流
concatenated-stream 输入流 make-concatenated-stream 接受任何数量的输入流作为参数
CL-USER> (let ((in (open "d:/emacs/io.db"))(in2 (open "d:/emacs/io2.db"))) (let ((merge (make-concatenated-stream in in2))) (loop for line = (read-char merge nil)while linedo(format t "~a" line)) (close in) (close out) (close merge)))hello worldi am ryuthanks.good bye.(+ 1 3)ni haomaduo xieqi duo
- 文件I/O与标准I/O
- 标准I/O、文件I/O
- 文件I/O与标准I/O
- 异步文件 I/O
- 多媒体文件I/O
- 文件I/O
- 文件I/O操作
- 文件I/O操作
- 文件I/O操作
- 文件I/O
- I/O文件库
- 文件I/O
- 文件I/O
- 文件I/O
- 高级文件I/O
- unbuffered 文件I/O
- unix----文件I/O
- 文件I/O
- TableCell自适应表格高度
- 读取数字的循环
- void与void*指针
- omap移植systemtap
- C++类别转换之char*与CString的互转换
- 文件I/O
- throw &throws
- C++ typedef用法小结
- ubuntu下安装memcache
- 求质数因子
- dos下有关目录的部分命令
- IOS 很多控件的讲解
- (34)数组的相关处理函数(上)(35)常用的数组相关操作函数(下)
- 博弈论学习总结(一)