简体   繁体   中英

SWI-Prolog predicate for reading in lines from input file

I'm trying to write a predicate to accept a line from an input file. Every time it's used, it should give the next line, until it reaches the end of the file, at which point it should return false. Something like this:

database :-
    see('blah.txt'),
    loop,
    seen.

loop :-
    accept_line(Line),
    write('I found a line.\n'),
    loop.

accept_line([Char | Rest]) :-
    get0(Char),
    C =\= "\n", 
    !,
    accept_line(Rest).
accept_line([]).

Obviously this doesn't work. It works for the first line of the input file and then loops endlessly. I can see that I need to have some line like "C =\\= -1" in there somewhere to check for the end of the file, but I can't see where it'd go.

So an example input and output could be...

INPUT
this is
an example

OUTPUT
I found a line.
I found a line.

Or am I doing this completely wrong? Maybe there's a built in rule that does this simply?

In SWI-Prolog, the most elegant way to do this is to first use a DCG to describe what a "line" means, and then use library(pio) to apply the DCG to a file.

An important advantage of this is that you can then easily apply the same DCG also on queries on the toplevel with phrase/2 and do not need to create a file to test the predicate.

There is a DCG tutorial that explains this approach, and you can easily adapt it to your use case.

For example:

:- use_module(library(pio)).

:- set_prolog_flag(double_quotes, codes).

lines --> call(eos), !.
lines --> line, { writeln('I found a line.') }, lines.

line --> ( "\n" ; call(eos) ), !.
line --> [_], line.

eos([], []).

Example usage:

?- phrase_from_file(lines, 'blah.txt').
I found a line.
I found a line.
true.

Example usage, using the same DCG to parse directly from character codes without using a file:

?- phrase(lines, "test1\ntest2").
I found a line.
I found a line.
true.

This approach can be very easily extended to parse more complex file contents as well.

If you want to read into code lists, see library(readutil) , in particular read_line_to_codes/2 which does exactly what you need.

You can of course use the character I/O primitives , but at least use the ISO predicates. "Edinburgh-style" I/O is deprecated, at least for SWI-Prolog. Then:

get_line(L) :-
    get_code(C),
    get_line_1(C, L).

get_line_1(-1, []) :- !. % EOF
get_line_1(0'\n, []) :- !. % EOL
get_line_1(C, [C|Cs]) :-
    get_code(C1),
    get_line_1(C1, Cs).

This is of course a lot of unnecessary code; just use read_line_to_codes/2 and the other predicates in library(readutil).

Since strings were introduced to Prolog, there are some new nifty ways of reading. For example, to read all input and split it to lines, you can do:

read_string(user_input, _, S),
split_string(S, "\n", "", Lines)

See the examples in read_string/5 for reading linewise.

PS. Drop the see and seen etc. Instead:

setup_call_cleanup(open(Filename, read, In),
        read_string(In, N, S), % or whatever reading you need to do
        close(In))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM