2011-04-25 31 views

Respuesta

17

Si usted no tiene instalado Extlib (y al parecer no se basan en el mensaje de error anterior), entonces por lo general se hace algo como esto:

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 ;; 

Si tiene extlib:

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

... que es bastante más de lo que tiene.

Si usted tiene la Batteries-included biblioteca se podía leer un archivo en un Enum.t e iterar sobre ella de la siguiente manera:

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

¡Muchas gracias! – Travis

+2

fd fuga detectada en la variante extlib: el canal de entrada no está cerrado – ygrek

+0

¿Puede explicar por qué está encadenando el ciclo while con una lista vacía? Creo que nunca se llega. – Rizo

0

Std.input_list aparentemente requiere Extlib, que debe instalar en su sistema (libextlib-ocaml y libextlib-ocaml-dev en los sistemas Debian).

1

he aquí una solución recursiva utilizando 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);; 

Uso:

let all_lines = read_all_lines "input.txt";; 

Sin embargo, preferiría para transmitir línea por línea:

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;; 

Uso:

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

Y puede ser un poco de glaseado:

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};; 

Uso:

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

Espero que esto ayude!

+0

[http://caml.inria.fr/pub/docs/manual-ocaml/libref/Scanf.html#space](http://caml.inria.fr/pub/docs/manual-ocaml/libref/Scanf .html # espacio) _ ** ... Del mismo modo, un carácter de avance de línea en la cadena de formato coincide con una alimentación de línea única o un retorno de carro seguido de un avance de línea ** _ Entonces, esta solución "debería" trabajo para terminaciones de línea de estilo "\ r \ n" y "\ n" (solo probado en un sistema Linux). –

+0

todos los fragmentos de código no cierran los canales de entrada, de ahí los descriptores de archivos de fugas – ygrek

+0

@ygrek: ¡Gracias! Fijo. Puede ser que sea mejor dejar que la parte llamante realice el negocio de apertura/cierre y hacer que estas funciones tomen canales de entrada en su lugar. –

0

Otro estilo para leer líneas de un archivo usando Scanf "string indition" y carácter de ancho cero. Es como el estilo imperativo tradicional.

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 

NOTA:

  1. read_line ignorar uno de arrastre \ n, por lo que si el último carácter de su archivo es \ n, que puede parece como si han perdido la última línea vacía.
  2. Al usar Scanf, debido a la amortiguación, no mezcle otras lecturas de bajo nivel en el mismo canal, de lo contrario, provocará un comportamiento extraño.
15

Si tiene instalada la biblioteca OCaml Core, entonces es tan simple como:

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

Si tiene corebuild instalado, a continuación, puedes compilar el código con él:

corebuild filename.byte 

si su código reside en un archivo llamado filename.ml.

Si no tiene OCaml Core, o no desea instalarlo, o alguna otra implementación de biblioteca estándar, entonces, por supuesto, puede implementarlo utilizando una biblioteca estándar de OCaml. Hay una función input_line, definida en el módulo Pervasives, que se abre automáticamente en todos los programas OCaml (es decir, todas sus definiciones son accesibles sin más aclaraciones con un nombre de módulo). Esta función acepta un valor de tipo in_channel y devuelve una línea, que se leyó desde el canal. Con esta función se puede implementar la función requerida:

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 [] 

Esta aplicación utiliza la recursividad, y es mucho más natural para la programación OCaml.

+0

¡Gracias por la implementación recursiva! Parece que entiendo la mayor parte de tu código, excepto el 'List.rev acc in loop []'. ¿Podrías por favor explicar exactamente qué está pasando? – nambvarun

+1

se anteponen los datos a la lista, por lo que en realidad podemos pensar en 'acc' como una pila. Cuando terminamos el bucle (es decir, pulsamos la condición de parada) invertimos nuestra pila, de modo que nuestra función devuelve datos en un orden normal – ivg

1

Esto lee las líneas del archivo y copias de cada una de ellas:

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

Aquí es una solución recursiva simple que no se acumulan las líneas o usar bibliotecas externas, pero le permite leer una línea, procesarla utilizando una función, lea el próximo de forma recursiva hasta que finalice, luego salga limpiamente. La función de salida cierra el identificador de archivo abierto y señala el éxito al programa de llamada.

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;; 
Cuestiones relacionadas