本節將學習如何從文件中讀取內容。若是文件中的內容是以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的第二個參數。若是給定名字的文件還不存在,這種模式將會建立一個新的文件。