本节将学习如何从文件中读取内容。若是文件中的内容是以Prolog的语句形式存在的,那么在Prolog中读取这样的文件内容是很容易的。好比文件houses.txt的内容以下:安全
gryffindor. hufflepuff. ravenclaw. slytherin.
下面是Prolog打开文件,读取内容,而且将内容显示在屏幕上的代码:app
main :- open('houses.txt', read, Str), read(Str, House1), read(Str, House2), read(Str, House3), read(Str, House4), close(Str), write([House1, House2, House3, House4]), nl.
上面代码将会以只读模式打开文件,而后使用Prolog内置谓词read/2读取Prolog语句,而后关闭流,最后打印信息到屏幕上去。学习
这种方式是很直接和简单的。可是,read/2谓词须要谨慎地使用。首先,它只能处理Prolog的语句(咱们将会在后面讨论更多这方面的内容),第二,若是流没有任何内容了,它可能会致使运行时错误。有没有一种更加优雅的方式能够克服第二个问题呢?atom
固然有的。内置谓词at_end_of_stream/1可以检查stream是否已经到达了尾端,并且是以一种安全的方式进行使用。对于一个流X,at_end_of_stream(X)当流X已经到达了其尾端时为真(换种说法,文件中全部的语句都已经被读取了)。code
下面的代码是通过修改后的版本,展现了如何使用at_end_of_stream/1这个谓词:字符串
main :- open('houses.txt', read, Str), read_houses(Str, Houses), close(Str), write(Houses), nl. read_houses(Stream, []) :- at_end_of_stream(Stream). read_houses(Stream, [X|L]) :- \+ at_end_of_stream(Stream), read(Stream, X), read_houses(Stream, L).
如今来解决更加麻烦的问题。上面说起read/2只能读取Prolog语句。若是你想要读取任意文件内容,状况可能会变得比较复杂,由于Prolog会迫使读入的内容以字符级别来进行,内置的谓词get_code/2从流中读取下一个存在的字符。字符在Prolog中是使用其整数数字来代替的。好比,get_code/2将会在流中读取字符a,而后返回结果是97。get
一般,咱们不会关心这些整数,而是关心字符自己——或者,由这些字符组成的列表,来表示的原子。咱们如何处理这些字符呢?一种方式是使用内置谓词atom_codes/2,能够将整数列表转换为对应的原子。咱们将使用下一个例子介绍这种方式,例子展现了如何从流中读取单词:it
readWord(InStream, W) :- get_code(InStream, Char), checkCharAndReadRest(Char, Chars, InStream), atom_codes(W, Chars). checkCharAndReadRest(10, [], _) :- !. checkCharAndReadRest(32, [], _) :- !. checkCharAndReadRest(-1, [], _) :- !. checkCharAndReadRest(end_of_file, [], _) :- !. checkCharAndReadRest(Char, [Char|Chars], InStream) :- get_code(InStream, NextChar), checkCharAndReadRest(NextChar, Chars, InStream).
代码是如何工做的?它读取一个字符而后检查这个字符是不是空白(整数为32),是否为分行符(整数为10),或者是流的结尾(整数为-1),以上任意一种状况下都会被当成一个完整单词的结束,不然的话下一个字符将会进行读取。stream
许多的应用程序都须要将输出写入到文件中进行保存,而不只仅是显示在屏幕上。本节咱们将学习如何在Prolog中将输出内容写入到文件中。基础
为了写文件,咱们必须建立一个(或者打开一个)文件而且将一个流与其相关联。你能够认为流就是文件的链接。在Prolog中,流的表现形式很不友好,名字都是相似“$stream(1833680)”这样可读性不好的。幸运的是,你不会直接使用流的名字,虽然Prolog在内部为其分配了名字,你能够经过使用Prolog的合一去匹配流名字和一个变量,而后使用变量操做流,而不是Prolog中内部分配给流的名字。
若是你想要输出字符串“Hogwarts”到文件hogwarts.txt中,能够这么作:
... open('hogwarts.txt', write, Stream), write(Stream, 'Hogwarts'), nl(Stream), close(Stream), ...
如何理解上面的代码?首先,内置的谓词open/3将会建立新的文件:hogwarts.txt;open/3的第二个参数指出咱们想要打开一个新的文件(或者覆盖任何已经存在的,相同名字的文件);open/3的第三个参数返回流的名字。其次,咱们将“Hogwarts”写入到流中,而且加入新的一行。最后,咱们使用内置谓词close/1关闭掉流。
这就是写文件的操做。正如以前承诺的,咱们对流的名字不感兴趣——咱们使用合一的变量操做流。还有须要注意的是,谓词write/2是一个更加基础的版本,由于在第九章中使用了write/1将内容输出到屏幕。
若是你不但愿复写已经存在的文件,而是在已经存在的文件中添加新的内容呢?能够选择打开文件的方式(再也不是write方式)来作到,使用append做为open/3的第二个参数。若是给定名字的文件还不存在,这种模式将会建立一个新的文件。