2011-04-25 44 views

Trả lời

17

Nếu bạn không có Extlib cài đặt (và dường như bạn không dựa trên các thông báo lỗi ở trên), sau đó nói chung là nó được thực hiện một cái gì đó như thế này:

let read_file filename = 
let lines = ref [] in 
let chan = open_in filename in 
try 
    while true; do 
    lines := input_line chan :: !lines 
    done; !lines 
with End_of_file -> 
    close_in chan; 
    List.rev !lines ;; 

Nếu bạn có Extlib:

let read_file filename = 
    let chan = open_in filename in 
    Std.input_list chan 

... đó là khá nhiều những gì bạn có.

Nếu bạn có Batteries-included thư viện bạn có thể đọc một tập tin vào một Enum.t và lặp trên nó như sau:

let filelines = File.lines_of filename in 
Enum.iter (fun line -> (*Do something with line here*)) filelines 
+0

Cảm ơn bạn rất nhiều! – Travis

+2

phát hiện rò rỉ fd trong biến thể extlib: kênh đầu vào không được đóng – ygrek

+0

Bạn có thể giải thích tại sao bạn đang chuỗi vòng lặp trong khi có danh sách trống không? Tôi nghĩ rằng nó không bao giờ đạt được. – Rizo

0

Std.input_list rõ ràng đòi hỏi Extlib, mà bạn nên cài đặt trên hệ thống của bạn (libextlib-ocamllibextlib-ocaml-dev trên hệ thống Debian).

1

Dưới đây là một giải pháp đệ quy sử dụng scanf:

let read_all_lines file_name = 
    let in_channel = open_in file_name in 
    let rec read_recursive lines = 
    try 
     Scanf.fscanf in_channel "%[^\r\n]\n" (fun x -> read_recursive (x :: lines)) 
    with 
     End_of_file -> 
     lines in 
    let lines = read_recursive [] in 
    let _ = close_in_noerr in_channel in 
    List.rev (lines);; 

Cách sử dụng:

let all_lines = read_all_lines "input.txt";; 

Tuy nhiên, tôi muốn để dòng line-by-line:

let make_reader file_name = 
    let in_channel = open_in file_name in 
    let closed = ref false in 
    let read_next_line = fun() -> 
    if !closed then 
     None 
    else 
     try 
     Some (Scanf.fscanf in_channel "%[^\r\n]\n" (fun x -> x)) 
     with 
     End_of_file -> 
      let _ = close_in_noerr in_channel in 
      let _ = closed := true in 
      None in 
    read_next_line;; 

Cách sử dụng:

let read_next = make_reader "input.txt";; 
let next_line = read_next();; 

Và có thể là một chút đóng băng:

type reader = {read_next : unit -> string option};; 

let make_reader file_name = 
    let in_channel = open_in file_name in 
    let closed = ref false in 
    let read_next_line = fun() -> 
    if !closed then 
     None 
    else 
     try 
     Some (Scanf.fscanf in_channel "%[^\r\n]\n" (fun x -> x)) 
     with 
     End_of_file -> 
      let _ = close_in_noerr in_channel in 
      let _ = closed := true in 
      None in 
    {read_next = read_next_line};; 

Cách sử dụng:

let r = make_reader "input.txt";; 
let next_line = r.read_next();; 

Hope this helps!

+0

[http://caml.inria.fr/pub/docs/manual-ocaml/libref/Scanf.html#space](http://caml.inria.fr/pub/docs/manual-ocaml/libref/Scanf .html # space) _ ** ... Tương tự, ký tự nguồn cấp dữ liệu trong chuỗi định dạng khớp với một nguồn cấp dữ liệu đơn hoặc trả về dòng theo sau là nguồn cấp dữ liệu dòng ** _ Vì vậy, giải pháp này "nên" làm việc cho cả hai dòng kết thúc kiểu "\ r \ n" và "\ n" (chỉ được thử nghiệm trên hệ thống Linux). –

+0

tất cả các đoạn mã không đóng kênh đầu vào, do đó mô tả tệp bị rò rỉ – ygrek

+0

@ygrek: Cảm ơn! Đã sửa. Có thể tốt hơn là để cho bên gọi điện thoại thực hiện việc mở/đóng doanh nghiệp và thay vào đó, hãy thực hiện các chức năng này. –

0

Một kiểu khác để đọc các dòng từ một tệp bằng Scanf "chuỗi chỉ thị" và ký tự không có chiều rộng bằng 0. Nó giống như phong cách truyền thống bắt buộc.

open Scanf 
open Printf 

(* little helper functions *) 
let id x = x 
let const x = fun _ -> x 
let read_line file = fscanf file "%[email protected]\n" id 
let is_eof file = try fscanf file "%0c" (const false) with End_of_file -> true 

let _ = 
    let file = open_in "/path/to/file" in 

    while not (is_eof file) do 
    let s = read_line file in 
    (* do something with s *) 
    printf "%s\n" s 
    done; 

    close_in file 

LƯU Ý:

  1. read_line bỏ qua một dấu \ n, vì vậy nếu ký tự cuối cùng của tập tin của bạn là \ n, nó có thể có vẻ như bạn đã bỏ qua các dòng trống cuối cùng.
  2. khi sử dụng Scanf, do đệm, không trộn lẫn các mức đọc thấp khác trên cùng một kênh, nếu không nó sẽ dẫn đến hành vi lạ.
15

Nếu bạn có thư viện OCaml Lõi cài đặt, sau đó nó cũng đơn giản như:

open Core.Std 
let r file = In_channel.read_lines file 

Nếu bạn có corebuild cài đặt, sau đó bạn có thể chỉ cần biên dịch mã của bạn với nó:

corebuild filename.byte 

nếu mã của bạn nằm trong tệp có tên filename.ml.

Nếu bạn không có lõi OCaml, hoặc không muốn cài đặt hoặc thực hiện một số thư viện chuẩn khác, thì tất nhiên, bạn có thể triển khai nó bằng thư viện chuẩn của vanilla OCaml. Có một hàm input_line, được định nghĩa trong mô-đun Pervasives, được tự động mở trong tất cả các chương trình OCaml (nghĩa là tất cả các định nghĩa của nó có thể truy cập được mà không cần làm rõ thêm với tên mô-đun). Hàm này chấp nhận giá trị loại in_channel và trả về một dòng, được đọc từ kênh. Sử dụng chức năng này, bạn có thể thực hiện chức năng được yêu cầu:

let read_lines name : string list = 
    let ic = open_in name in 
    let try_read() = 
    try Some (input_line ic) with End_of_file -> None in 
    let rec loop acc = match try_read() with 
    | Some s -> loop (s :: acc) 
    | None -> close_in ic; List.rev acc in 
    loop [] 

Thực hiện này sử dụng đệ quy và tự nhiên hơn với lập trình OCaml.

+0

Cảm ơn bạn đã thực hiện đệ quy! Tôi dường như hiểu hầu hết mã của bạn ngoại trừ 'List.rev acc in loop []'. Bạn có thể vui lòng giải thích chính xác những gì đang xảy ra? – nambvarun

+1

dữ liệu vào danh sách được thêm vào trước, do đó, thực sự chúng ta có thể nghĩ về 'acc' như một chồng. Khi chúng tôi kết thúc vòng lặp (tức là, nhấn điều kiện dừng), chúng tôi sẽ đảo ngược ngăn xếp của mình để chức năng của chúng tôi trả về dữ liệu theo thứ tự bình thường – ivg

1

này đọc dòng của tập tin và in mỗi trong số họ:

open Core.Std 

let handle_line line = 
    printf "Your line: %s \n" line 

let() = 
    let file_to_read = "./file_to_read.txt" in 
    let lines = In_channel.read_lines file_to_read in 
     List.iter ~f: handle_line lines 
0

Dưới đây là một giải pháp đệ quy đơn giản mà không tích lũy các dòng hoặc sử dụng thư viện bên ngoài, nhưng cho phép bạn đọc một dòng, xử lý nó bằng cách sử dụng chức năng, đọc tiếp theo đệ quy cho đến khi thực hiện, sau đó thoát sạch. Chức năng thoát sẽ đóng tập tin mở và báo hiệu thành công cho chương trình gọi.

let read_lines file process = 
    let in_ch = open_in file in 
    let rec read_line() = 
    let line = try input_line in_ch with End_of_file -> exit 0 
    in (* process line in this block, then read the next line *) 
     process line; 
     read_line(); 
in read_line();; 

read_lines some_file print_endline;; 
Các vấn đề liên quan