So i have this ocaml code :
let read_file file_name = "/home/test.txt" in
let in_channel = open_in file_name in
try
while true do
let line = input_line in_channel in
print_endline line
done
with End_of_file ->
close_in in_channel
let my_fun()=
let f = "test1.ml" in
read_file f
;;
my_fun ()
But it is printing only the 1st line of the file
Can you help me out ?
Your code shows an error along the lines of "unbound value file_name" on line 2. I just turned read_file into a function, and called it with the path to the file:
let read_file file_name =
let in_channel = open_in file_name in
try
while true do
let line = input_line in_channel in
print_endline line
done
with End_of_file ->
close_in in_channel
let my_fun() =
let f = "home/test.txt" in
read_file f;;
my_fun()
Calling $ ocaml <filename.ml> on the file with this code prints each line from the file in the specified path to stdout.
Edit: Adding some more details on file structure and how to execute:
I have a directory named sample-dir with the following contents:
sample-dir
├── home
│ └── test.txt
└── sample.ml
The code is in sample.ml, the test.txt is in the sub-directory called home. The contents of test.txt are as follows:
one
two
three
I cd into sample-dir and execute the following command:
$ ocaml sample.ml
I get the following output:
one
two
three
Related
I want to write a function that reads a text file line by line. ie, read the first line until EOL and then treat this as a string, and repeat this until the text ends.
I have managed only this much till now:
fun read file =
let val is = TextIO.openIn file
in
end
You can use TextIO.inputLine : instream -> string option for this. Using refs you can do something like
let
val stream = TextIO.openIn file
val done = ref false
in
while not (!done) do
case TextIO.inputLine stream of
SOME s => print s
| NONE => done := true
end
You can also use the functions in TextIO.StreamIO.
I am writing a short script to read each file in folder of script files and print last word its first line - indicating executable file that is run- ocaml e.g. in usr/bin/ocaml
#use "topfind"
#require "str"
(* fn to print binary file being called from hashbang line of script file *)
let rec myfn (afile) =
print_string (afile^": ");
if (Sys.is_directory afile) then print_endline("This is a directory.");
let ic = (open_in afile) in
if in_channel_length(ic)==0 then print_endline("Zero length file."); exit 0
let line = input_line(ic) in (* error here *)
if (Str.first_chars line 0 = "#") then
let linelist = (Str.split (Str.regexp "/") line) in
let lastword = List.nth linelist ((List.length linelist) - 1) in
print_endline(lastword)
else
print_endline("Not a script file.") ;;
(* to check all files in directory *)
let dir = "." in
let files = Sys.readdir dir in
Array.iter myfn files;;
However, it is giving following error:
File "firstline3.ml", line 13, characters 27-29:
Error: Syntax error
I have tried to replace in with ; or no terminator in this line but it does not help. What is the problem and how can it be solved. Thanks for your help.
You are missing parentheses around the then expression:
if in_channel_length ic =0 then ( print_endline "Zero length file."; exit 0 );
otherwise the expression is read by the parser as
(if in_channel_length ic = 0 then print_endline "Zero length file.");
exit 0;
and the exit is thus unconditional.
Note, you should avoid the physical equality operator, ==, (especially on non-mutable values) and use = instead.
EDIT: If you want to return, you just need to add an else branch
if in_channel_length ic = 0 then print_endline "Zero length file."
else ....
I have written an interpreter using ocamllex and ocamlyacc, the lexer and the parser work correctly but currently they only parse the last .txt argument it receives as oppose to all of them in turn. For example, ./interpret one.txt two.txt three.txt only parses three.txt as oppose to parsing one.txt and then two.txt and then three.txt which is what I want. So for example the parse results are as follows:
one.txt -> "1"
two.txt -> "2"
three.txt -> "3"
On calling ./interpret one.txt two.txt three.txt the current output is: 3 but I want it to be 123
Here is my main class which deals with the stdin and stdout
open Lexer
open Parser
open Arg
open Printf
let toParse c =
try let lexbuf = Lexing.from_channel c in
parser_main lexer_main lexbuf
with Parsing.Parse_error -> failwith "Parse failure!" ;;
let argument = ref stdin in
let prog p = argument := open_in p in
let usage = "./interpreter FILE" in
parse [] prog usage ;
let parsed = toParse !argument in
let result = eval parsed in
let _ = parsed in
flush stdout;
Thanks for your time
There's not really enough code here to be able to help.
If I assume that the output is written by eval, then I see only one call to eval. But there's nothing here that deals with filenames from the command line, so it's hard to say more.
If you are planning to read input from files, then there's no reason to be using stdin for anything as far as I can tell.
(I know this is a very minor point, but this code doesn't constitute a class. Other languages use classes for everything, but this is a module.)
Update
Here's a module that works something like the Unix cat command; it writes out the contents of all the files from the command line one after the next.
let cat () =
for i = 1 to Array.length Sys.argv - 1 do
let ic = open_in Sys.argv.(i) in
let rec loop () =
match input_line ic with
| line -> output_string stdout (line ^ "\n"); loop ()
| exception End_of_file -> ()
in
loop ();
close_in ic
done
let () = cat ()
Here's how it looks when you compile and run it.
$ ocamlc -o mycat mycat.ml
$ echo test line 1 > file1
$ echo test line 2 > file2
$ ./mycat file1 file2
test line 1
test line 2
I have a program where if I type "tac Text.txt" (or any file path in quotations, but this is the test text file I made/used), it will print what is in the txt file, but in reverse. But it doesn't work properly. Where it should appear as
No it is not.
Yes-it is.
No it is not.
Hello my name is Jim.
it appears as
["No it is not."; ""; ""; "Yes-it is."; "No it is not."; "Hello my
name is Jim."].
My code currently is
open System
open System.IO
let countLines path =
File.ReadAllLines(path) |> Seq.toList|> List.rev
// File.ReadAllLines reads the file, turns it into a list then reverses the list.
let printFunction lines =
printfn "%A" lines
// This, when called, will print the file in reverse.
let tac path =
if File.Exists(path) then
let lines = countLines path
printFunction lines
else
printfn "File not found."
[<EntryPoint>]
let main argv =
if argv.Length > 0 then
tac argv.[0]
else
printfn "Error - Please enter file path."
0
I'm assuming it's due to converting to a list, is there a way I can print it normally? I'm hoping it's just a small rookie mistake I've made.
UPDATE: I just changed
let countLines path =
File.ReadAllLines(path) |> Seq.toList|> List.rev
to
let countLines path =
File.ReadAllLines(path) |> Array.rev
Same thing happens, but I'm hoping it leads me closer to the result I want.
It's writing out your entire data type, which is a string list.
You need to iterate through the list:
let printFunction lines =
for line in lines do printfn "%s" line
Print an array, list or seq of lines individually:
let printFunction = Seq.iter (printfn "%s")
I know that in order to load a program in OCaml one has to type #use "source_code_file.ml" in toplevel where source_code_file.ml is the file we want to use.
My program reads input from stdin. In the command line i have a txt file that with redirection is used to act as stdin. Can i do this in toplevel? I would like to this because in toplevel i can easily see what type variables have and if things are initialized with the correct values.
If you're on a Unix-like system you can use Unix.dup2 to do almost any kind of input redirection. Here is a function with_stdin that takes an input file name, a function, and a value. It calls the function with standard input redirected from the named file.
let with_stdin fname f x =
let oldstdin = Unix.dup Unix.stdin in
let newstdin = Unix.openfile fname [Unix.O_RDONLY] 0 in
Unix.dup2 newstdin Unix.stdin;
Unix.close newstdin;
let res = f x in
Unix.dup2 oldstdin Unix.stdin;
Unix.close oldstdin;
res
If your function doesn't consume the entire input the leftover input will confuse the toplevel. Here's an example that does consume its entire input:
# let rec linecount c =
try ignore (read_line ()); linecount (c + 1)
with End_of_file -> c;;
val linecount : int -> int = <fun>
# with_stdin "/etc/passwd" linecount 0;;
- : int = 86
#
This technique is too simple if you wanted to interleave interactions with the toplevel with calls to your function to consume just part of its input. I suspect that would make things too complicated to be worth the effort. It would be much easier (and perhaps better overall) to rewrite your code to work with an explicitly specified input channel.