I'm currently working on a Prolog program which would, logically, have some kind of "save/load" feature. I've gotten the save part to work, where I'm now (as a start) creating three *.txt files, which will contain a 2D array/list each. However, I'm facing some issues trying to load it back into the program.
What I have right now is something as simple as:
% Initialize globals
?- nb_setval(aisles_global, []).
% Load all previously saved data from the given .txt files
load_all():-
exists_file('C:\\Users\\Xariez\\Desktop\\aisles.txt'),
open('C:\\Users\\Xariez\\Desktop\\aisles.txt', read, InAisles),
read_line_to_codes(InAisles, AisleString),
% read_line_to_string(InAisles, AisleString),
writeln(AisleString),
nb_setval(aisles_global, AisleString),
close(InAisles).
As previously mentioned, the files will have a 2D array each, but as an example:
aisles.txt
[["Beer", "Cider" ], [ "Milk", "Juice" ], ["Light Bread", "Dark Bread"]]
I've tried using both read_line_to_codes/2 and read_line_to_string/2. While it technically works when reading it into codes, I feel like it would quickly become annoying to reconstruct a 2D list/array since it's now got every character as a code. And while reading into a string succeeds in the reading part, we now have a string that LOOKS like a list, but isn't really one (if I've understood this situation correctly?). And hence I'm here.
If anyone got any ideas/help, that'd be greatly appreciated. Thanks!
Prolog has predicates for doing input/output of terms directly. You don't need to roll these yourself. Reading terms is done using read, while for writing there are several options.
Your best shot for writing is probably write_canonical, which will write terms in "canonical" syntax. This means that everything is quoted as needed (for example, an atom 'A' will be printed as 'A' and not as plain A like write would print it), and terms with operators are printed in prefix syntax, which means you get the same term even if the reader doesn't have the same operators declared (for example, x is y is printed as is(x, y)).
So you can write your output like:
dump(Aisles, Filename) :-
open(Filename, write, OutAisles),
write_canonical(OutAisles, Aisles),
write(OutAisles, '.'),
close(OutAisles).
Writing the . is necessary because read expects to read a term terminated by a period. Your reading predicate could be:
load(Aisles, Filename) :-
open(Filename, read, InAisles),
read(InAisles, Aisles),
close(InAisles).
Running this using some example data:
?- aisles(As), dump(As, aisles).
As = [["Beer", "Cider"], x is y, 'A', _G1380, ["Milk", "Juice"], ["Light Bread", "Dark Bread"]].
?- load(As, aisles).
As = [["Beer", "Cider"], x is y, 'A', _G1338, ["Milk", "Juice"], ["Light Bread", "Dark Bread"]].
The contents of the file, as you can check in a text editor, is:
[["Beer","Cider"],is(x,y),'A',_,["Milk","Juice"],["Light Bread","Dark Bread"]].
Note the canonical syntax for is. You should almost certainly avoid writing variables, but this shouldn't be a problem in your case.
Related
How can I quickly save and load data files in Prolog, for use with games?
Save files with save(File_name,Term), where File_name is the file name in folder folder_name and Term is a compound containing the data file.
?- save("file_name.txt",[[["player_name","Harold"],["score",10],["symbol",key_word]],[["player_name","Queenie"],["score",20],["symbol",schema]]]).
Load files with load(File_name,Term), where File_name is the file name in folder folder_name and Term is the contents loaded from the file.
?- load("file_name.txt",Term),writeln1(Term).
Term=[[["player_name","Harold"],["score",10],["symbol",key_word]],[["player_name","Queenie"],["score",20],["symbol",schema]]]
If you only want to save a single term, a basic version of what you want is the following:
save(Filename, Term) :-
open(Filename, write, Stream),
write_canonical(Stream, Term),
write(Stream, '.'),
close(Stream).
load(Filename, Term) :-
open(Filename, read, Stream),
read(Stream, Term),
close(Stream).
For example:
?- save('hello.term', hello(world)).
true.
At this point the file hello.term (the name is arbitrary) contains this:
hello(world).
Then:
?- load('hello.term', Term).
Term = hello(world).
Notes:
The read predicates expect to read a term with a terminating period (.), but the write predicates don't write those out by themselves. Therefore save/2 needs to write it explicitly.
There is no error handling here.
There is also a write/2 predicate that could be used. The difference is relevant if you use operators: The term a + b will be written as a + b by write and as +(a, b) by read. The latter can be read correctly even in cases where the write and the read happen with different operator declarations.
More work is needed if you want to write or read multiple terms.
Using the code below you can save and load data files in Prolog.
% save("file_name.txt",[[["player_name","Harold"],["score",10],["symbol",key_word]],[["player_name","Queenie"],["score",20],["symbol",schema]]]).
save(File_name,Term) :-
string_concat("folder_name/",File_name,Path),
term_to_atom(Term,Atom),
string_atom(String,Atom),
(open_s(Path,write,Stream1),
write(Stream1,String),
close(Stream1)),!.
% load("file_name.txt",Term).
load(File_name,Term) :-
File_name="file_name.txt",
string_concat("folder_name/",File_name,Path),
phrase_from_file_s(string(Codes),Path),
string_codes(String,Codes),
atom_to_term(String,Term,[]).
open_s(File,Mode,Stream) :-
atom_string(File1,File),
open(File1,Mode,Stream),!.
string_atom(String,Atom) :-
atom_string(Atom,String),!.
phrase_from_file_s(string(Output), String) :-
atom_string(String1,String),
phrase_from_file(string(Output), String1),!.
string(String) --> list(String).
list([]) --> [].
list([L|Ls]) --> [L], list(Ls).
writeln1(Term) :-
term_to_atom(Term,Atom),
writeln(Atom),!.
See also string to list (Test 15) in List Prolog, which does the same thing as atom_to_term//3. The predicates above replace the need for using long grammars, however I have posted answers on My long SWI-Prolog grammar keeps on failing and I can't get my Prolog DCG working with atom concat for those who are interested.
I'm not an expert in bash coding and I'm trying to do one interative-like code to help me in my work.
I have a file that contains some numbers (coordinates), and I'm trying to make a code to read some specific numbers from the file and then store them in an array. Modify that array using some arithmetic operation and then replace the numbers in the original file with the modified array. So far I've done everything except replacing the numbers in the file, I tried using sed but it does not change the file. The original numbers are stored in an array called "readfile" and the new numbers are stored in an array called "d".
I'm trying to use sed in this way: sed -i 's/${readfile[$j]}/${d[$k]}/' file.txt
And I loop j and k to cover all the numbers in the arrays. Everything seems to work but the file is not being modified. After some digging, I'm noticing that sed is not reading the value of the array, but I do not know how to fix that.
Your help is really appreciated.
When a file isn't modified by sed -i, it means sed didn't find any matches to modify. Your pattern is wrong somehow.
After using " instead of ' so that the variables can actually be evaluated inside the string, look at the contents of the readfile array and check whether it actually matches the text. If it seems to match, look for special characters in the pattern, characters that would mean something specific to sed (the most common mistake is /, which will interfere with the search command).
The fix for special characters is either to (1) escape them, e.g. \/ instead of just /, or (2) (and especially for /) to use another delimiter for the search/replace command (instead of s/foo/bar/ you can use s|foo|bar| or s,foo,bar, etc - pretty much any delimiter works, so you can pick one that you know isn't in the pattern string).
If you post data samples and more of your script, we can look at where you went wrong.
Good evening!
I am trying to read a text document in Fortran 95 and do some calculations with the values in it. The document has numerous gas names and certain values of 'A' assigned to them. So essentially it look something like this:
Document 1: gas values.
GAS A1 A2
steam 1 2
air 3 4
I want then the user to input a gas name (variable gasNameIn) and implement while loop to keep searching for the gas until it matches the input. So eg. user inputs 'air' and the program starts reading first words until air comes up. It then read values of A1 and A2 and uses them for calculation. What I did for it is that I opened the file as unit 25 and tried the following loop:
do while(gasName .NE. gasNameIn)
read(25, *) gasName
if (gasName .EQ. gasNameIn)
read(25,*) A1, A2
endif
enddo
but I get an error "End of file on unit 25".
Any ideas on how my while loop is wrong? Thank you!
By the first read statement, you read the name correctly, but Fortran then proceeds to the next line (record). Try to use
read(25, *, advance='no') gasName
If your searched gas was on the last line, you get the end of file error. Otherwise you will have an error when reading A1.
you need to read whole lines as strings and process. This is untested but the jist of it:
character*100 wholeline
wholeline=''
do while(index(wholeline,'air').eq.0)
read(unit,'(a)')wholeline
end do
then if you can count on the the first strings taking up ~7 cols like in the example,
read(wholeline(7:),*)ia1,ia2
What happened is that you read the whole line in as "gasName" and tested the whole line to see if it was equivalent to "gasNameIn". It never will be the same if the data is laid out the way you have in your sample, so you will get to the end of your file before you ever get a match. It has been a while since I've written in Fortran, but in the first loop "gasName" will be undefined. Usually that is a no no in programming.
The answer that got in before I could get mine typed in is the way forward with your problem. Give it a go. If you still have some trouble maybe I'll fire up a Fortran compiler and try my hand at Fortran again. (It's been since the early 90's that I've done any.)
CHEERS!
Hi, here is your code modified a bit, you can pitch what you don't need. I just added a couple of lines to make it a self-contained program. For instance, There is a statement which assigns the value "steam" to "gasNameIn". I think you mentioned that you would have the user enter a value for that. I've also added a "write" statement to show that your program has properly read the values. It is a crude kind of "unit test". Otherwise, I've only addressed the part of the program that your question asked about. Check this out:
character (len=5) :: gasName, gasNameIn
gasNameIn = "steam"
open (unit = 25, file = "gasvalues.txt")
do while (gasName .NE. gasNameIn)
read (25, *) gasName
if (gasName .EQ. gasNameIn) then
backspace (25)
read (25,*) gasName, A1, A2
write (*, *) "The gas is: ", gasName, ". The factors are A1: ", A1, "; A2: ", A2
endif
end do
close (25)
end
Ok, here are a couple of notes to help clarify what I've done. I tried to keep your code as intact as I could manage. In your program there were some things that my fortran 95 compiler complained about namely: the "then" is required in the "if" test, and "enddo" needed to be "end do". I think those were the main compiler issues I had.
As far as your code goes, when your program tests that "gasName" is the same as "gasNameIn", then you want to "backup" to that record again so that you can re-read the line. There are probably better ways to do this, but I tried to keep your program ideas the way you wanted them. Oh, yes, one more thing, I've named the input file "gasvalues.txt".
I hope this helps some.
CHEERS!
Ok I use method from here: How to Read only the last line of a text file in Lua?
The problem is that sometimes line can be bigger.
The question is how can i find first word "foo" from the end of file and then use everything after it?
The problem is that sometimes line can be bigger.
Then you just need to seek further back from the end.
The question is how can i find first word "foo" from the end of file and then use everything after it?
Grab a big enough chunk of the file to be sure you've got the last foo, the use .*foo to skip everything up to and including the last "foo" (.* is greedy).
local f = io.open('filename', 'r')
f:seek('end', -1024)
local text = f:read('*a')
local after = string.match(text, ".*foo(.*)")
f:close()
If the file is not too big and you're ready to take the easy way out this might help:
fh=io.open('myfile.txt','rb')
str=fh:read'*a'
pat='foo'
afterFoo=str:match('.*'..pat..'(.*)$')
fh:close()
If you need a more complex, but faster (in run time on large files) solution, my guess would be that you' read in the file in chunks, reverse each of them, and look for your pattern in reverse. Don't forget to look for your pattern across the borders (the chunks must overlap at least the length of the pattern you're seeking in the general case).
For more explanation about the block reading, see my post here.
How do I save on an existing file after adding new data
add_a_link(X,Y) :-
tell('alink.txt'),
write(X),
write('.'),
write(Y),
write('.'),
put(10),
told,
write('data written'),
nl.
this code only re-write the text file.
Use open/3 and stream oriented I/O:
open(file, append, S), write(S, info(X,Y)), put_char(S,.), nl(S), close(S).
Using tell/1 and told is extremely unreliable. It easily happens that the output is written to another file accidentally.
Edit: Here is an example to illustrate the extremely unreliable properties of tell/1 and told.
Say, you write tell(file), X > 3, write(biggervalue), told. This works fine as long as X > 3. But with a smaller value this query fails and nothing is written. That might have been your intention. However, the next output somewhere else in your program will now go into the file. That's something you never want to happen. For this reason ISO-Prolog does not have tell/1 and told but rather open/3 and close/1.