我想读取一个纯文本文件并对每一行应用一个谓词(谓词包含write
输出的内容)。我该怎么做?
5 回答
您可以使用read
来读取流。请记住调用at_end_of_stream
以确保没有语法错误。
例子:
读取文件.pl
main :-
open('myFile.txt', read, Str),
read_file(Str,Lines),
close(Str),
write(Lines), nl.
read_file(Stream,[]) :-
at_end_of_stream(Stream).
read_file(Stream,[X|L]) :-
\+ at_end_of_stream(Stream),
read(Stream,X),
read_file(Stream,L).
我的文件.txt
'line 0'.
'line 1'.
'line 2'.
'line 3'.
'line 4'.
'line 5'.
'line 6'.
'line 7'.
'line 8'.
'line 9'.
因此,通过调用main
您将收到输出:
?- main.
[line 0,line 1,line 2,line 3,line 4,line 5,line 6,line 7,line 8,line 9]
true
只需配置main
. 当然,这里的输出是使用 的示例write
。配置以匹配您的请求。
我认为这个原则可以用来回答你的问题。祝你好运。
在 SWI-Prolog 中,最简洁的解决方案是编写一个 DCG 来描述什么是“行”,然后为每一行调用一个谓词。使用 library(pio) 将 DCG 应用于文件。
编辑:根据要求,考虑:
:- use_module(library(pio)).
lines([]) --> call(eos), !.
lines([Line|Lines]) --> line(Line), lines(Lines).
eos([], []).
line([]) --> ( "\n" ; call(eos) ), !.
line([L|Ls]) --> [L], line(Ls).
示例用法:?- phrase_from_file(lines(Ls), 'your_file.txt').
在性能解决方案中,有更多的可能性和更合理的解决方案,可以从文件中获取未解释的即纯文本行:
SWI-序言:
read_line(S, X) :-
read_line_to_codes(S, L),
read_line2(L, X).
read_line2(end_of_file, _) :- !, fail.
read_line2(L, X) :-
atom_codes(X, L).
Jekejeke序言:
:- use_module(library(stream/console)).
下面是一些时间,读取一个 655 行的文件:
test :-
open('<path>', read, Stream),
test(Stream),
close(Stream).
test(Stream) :-
read_line(Stream, _), !,
test(Stream).
test(_).
SWI-序言:
̀?- time((between(1,100,_), test, fail; true)).
% 328,300 inferences, 0.125 CPU in 0.143 seconds (88% CPU, 2626400 Lips)
true.
Jekejeke序言:
?- time((between(1,100,_), test, fail; true)).
% Up 121 ms, GC 2 ms, Thread Cpu 94 ms (Current 05/07/19 17:19:05)
Yes
我猜想读入字符串而不是读入原子的 SWI-Prolog 解决方案可能会更快。但是在上面我们将 atom 与 atom 读取进行比较。
de SWI-Prolog 文档中有一个很好的例子:
file_line(File, Line) :-
setup_call_cleanup(open(File, read, In),
stream_line(In, Line),
close(In)).
stream_line(In, Line) :-
repeat,
( read_line_to_string(In, Line0),
Line0 \== end_of_file
-> Line0 = Line
; !,
fail
).
来源:https ://www.swi-prolog.org/pldoc/man?predicate=read_string/5
鉴于这里的响应,我创建了这个,它更像是 python "with" :
?- read_file('test.txt', tokenize,5,L). %first 5 lines
?- read_file('test.txt', tokenize,L). %the whole file
?- read_file('test.txt', split,5,L). %just split
?- open('test.txt',read,S), read_lines(S,split,5,L), close(S).
代码 :
:- module(files,[read_line/3, read_file/3, read_file/4, read_lines/3, read_lines/4, split/2, split/3, split/4]).
:- use_module(library(pcre)).
string2atoms(Strings, Atoms) :- maplist(atom_string, Atoms, Strings).
split(Str, Lst) :- split_string(Str, " ", "", Lst).
split(Str, Separator, Lst) :- split_string(Str, Separator, "", Lst).
split(Str, Separator, Pad, Lst) :- split_string(Str, Separator, Pad, Lst).
is_empty(Str) :- re_match(Str, '^\s*$').
non_empty(Str) :- ( is_empty(Str) -> false ; true).
tokenize(String,Tokens) :- split(String,Lst), string2atoms(Lst,Tokens).
%read a line and execute a Goal on it
read_line(Stream,Goal,Args) :-
\+ at_end_of_stream(Stream), read_line_to_string(Stream,Str),
%% \+ isempty(Str), call(Goal,Str,Args).
( is_empty(Str) -> true ; call(Goal,Str,Args)).
% given Stream execute Goal on every line. with the option to process only N lines
read_lines(Stream, _, _,_) :- at_end_of_stream(Stream), !. %is EOF
read_lines(_, _, 0,_) :- !. % only N lines
read_lines(Stream, Goal, N, [Res|T]) :-
N0 is N - 1, read_line(Stream, Goal, Res), writeln(Res),
read_lines(Stream, Goal, N0, T).
%read the whole file
read_lines(Stream, Goal, LoL) :- read_lines(Stream, Goal, 1000000, LoL).
%given file name execute Goal on every line
read_file(File, Goal, N, Res) :-
open(File, read, Stream), read_lines(Stream, Goal, N, Res), close(Stream).
read_file(File, Goal, Res) :- read_file(File, Goal, 1000000, Res).