aprendiendo ( Erlang ).

viernes, 24 de junio de 2011

Manejo de ficheros

| 1 comentarios |

Las funciones para la manipulación de ficheros están organizadas en los siguientes módulos:
  • file: contiene las rutinas de apertura, cierre, lectura y escritura de ficheros; listado de directorios, etc...
    • change_group: cambia de grupo del fichero.
    • change_owner: cambia de dueño.
    • change_time: cambia la fecha de modificación o de último acceso.
    • close: cierra un fichero abierto.
    • consult: lee términos Erlang desde un fichero.
    • copy: copia el contenido de un fichero.
    • del_dir: borra un directorio.
    • delete: borra un fichero.
    • eval: Evalúa las expresiones Erlang de un fichero.
    • format_error: retorna una cadena descriptiva de la razón del error.
    • get_cwd: obtiene el directorio de trabajo.
    • list_dir: lista de fichero en un directorio.
    • make_dir: crea un directorio.
    • make_link: crea un enlace duro a un fichero.
    • make_symlink: crea un enlace simbólico a un fichero o directorio.
    • open: abre un fichero.
    • position: establece la posición en un fichero.
    • pread: lee desde un archivo en una posición determinada.
    • pwrite: escribe en un archivo en una posición determinada.
    • read: lee desde un archivo.
    • read_file: lee un archivo entero.
    • read_file_info: lee la información acerca del fichero.
    • read_link: obtiene a donde apunta un enlace.
    • read_link_info: obtiene información acerca de un enlace o fichero.
    • rename: renombra un fichero.
    • script: evalúa y retorna el resultado de ejecutar un script Erlang.
    • set_cwd: establece el directorio de trabajo.
    • sync: sincroniza el estado en memoria del fichero con lo que hay en disco.
    • truncate: trunca un fichero.
    • write: escribe en un fichero.
    • write_file: escribe un fichero entero.
    • write_file_info: cambia la información a fichero.
  • filename: rutinas para la manipulación de nombres de ficheros y carpetas independientemente del sistema que utilicemos.
  • filelib: librería de extensión del módulo file. Contiene rutinas para el listado de ficheros, checkeos de tipos y atributos, etc...
  • io: permite trabajar con ficheros abiertos. Escritura y/o lectura de datos en ficheros.

Trabajando con ficheros de términos

Para empezar, y si no son muchos términos, podemos obtener la lista de términos de un fichero utilizando la función consult:
1> file:consult("fichero.terms").
{ok,[{persona,verdi,{edad,99},{profesion,"Informatico"}},
     [2,3,2,5,1,2,3],
     {observaciones,"algunas observaciones"}]}
2> file:consult("fichero.noexiste").
{error,enoent}
La función consult asume que nuestro fichero contiene una secuencia de términos Erlang. Como podemos observar en el ejemplo cuando no hay problemas, la función consult, devuelve una tupla con el formato {ok, Lista_de_terminos} y en caso contrario, {error, motivo}.
A la hora de leer ficheros con muchos términos, no lo podemos hacer de una única lectura. En este caso, podemos:
leer_fichero_terminos(File) ->
    case file:open(File, read) of
        {ok, Fd} ->
            leer_termino(Fd),
            file:close(Fd),
            {ok};
        {error, Motivo} ->
            {error, Motivo}
    end.

leer_termino(Fd) ->
    case io:read(Fd, '') of
        {ok, Term} -> 
            io:format(" Termino leido: ~p~n", [Term]), 
            leer_termino(Fd);
        eof -> 
            io:format(" Fin de fichero ~n");
        Error -> 
            Error
end.
La función leer_fichero_terminos/1 recibe el nombre del fichero a leer y se encarga de abrirlo y cerrarlo. Y la función leer_terminos/1 recibe el identificador de fichero y va leyendo y procesando término a término. Veamos en detalle que realizan las funciones de manejo de ficheros en este ejemplo:
  • @spec file:open(File, [Modo]) => {ok, Fd} | {error, Motivo}: como imaginarás, abre el fichero y si tiene existo devuelve el descriptor de fichero Fd. Los modos soportados los tienes documentados aquí
  • @spec io:read(Fd, Prompt) => {ok, Term} | {error,Motivo} | eof: se encarga de leer un término desde el fichero, para ello, recibe un descriptor de fichero y un Prompt. El Prompt es ignorado en la lectura de fichero. Si se utiliza para realizar lecturas desde la entrada estandar (teclado).
  • @spec file:close(Fd) => ok | {error, Motivo}: cierra el fichero.
Veamos como se comportan nuestras funciones:
3> c(ficheros.erl).
{ok,ficheros}
4> ficheros:leer_fichero_terminos("fichero.terms").
 Termino leido: {persona,verdi,{edad,99},{profesion,"Informatico"}}
 Termino leido: [2,3,2,5,1,2,3]
 Termino leido: {observaciones,"algunas observaciones"}
 Fin de fichero 
{ok}
5> ficheros:leer_fichero_terminos("fichero.noexiste").
{error,enoent}
Una vez que sabemos leer continuemos con la escritura de ficheros de términos. Para ello, tenemos que abrir el fichero en modo de escritura y utilizar la función format/3. Esta función es idéntica a la que conocemos y se comporta exactamente igual, con la salvedad de que escribe en el fichero que le indiquemos. Veamos el código:
escribir_fichero_terminos (File, Terminos) ->
    case file:open(File, write) of
        {ok, Fd} ->
            lists:foreach(fun(Termino) -> io:format(Fd, "~p.~n" ,[Termino]) end, Terminos),
            file:close(Fd),
            {ok};
        {error, Motivo} ->
            {error, Motivo}
    end.
Comprobemos su funcionamiento:
6> c(ficheros).
{ok,ficheros}
7> ficheros:escribir_fichero_terminos("fichero.terms", [{persona,verdi,{edad,99},{profesion,"Informatico"}},
7>      [2,3,2,5,1,2,3],
7>      {observaciones,"algunas observaciones"}]).
{ok}
8> file:consult("fichero.terms").
{ok,[{persona,verdi,{edad,99},{profesion,"Informatico"}},
     [2,3,2,5,1,2,3],
     {observaciones,"algunas observaciones"}]}

Trabajando con ficheros de texto

A la hora de trabajar con ficheros de textos lo más interesante es poder leer líneas completas. El código queda prácticamente igual, con la salvedad de utilizar la función io:get_line/2.
leer_fichero_texto_linea_a_linea(File) ->
    case file:open(File, read) of
        {ok, Fd} ->
            leer_texto_linea_a_linea(Fd),
            file:close(Fd),
            {ok};
        {error, Motivo} ->
            {error, Motivo}
    end.

leer_texto_linea_a_linea(Fd) ->
    case io:get_line(Fd, '') of
        eof -> 
            io:format(" Fin de fichero ~n");
        {error, Motivo} -> 
            {error,Motivo};
        Texto -> 
            io:format(" Texto leido: ~p~n", [Texto]), 
            leer_texto_linea_a_linea(Fd)
    end.
Veamos como se comporta:
9> c(ficheros.erl).
{ok,ficheros}
10> ficheros:leer_fichero_texto_linea_a_linea("fichero.texto").
 Texto leido: "linea1 linea1 \n"
 Texto leido: "linea2 linea2 \n"
 Texto leido: "linea3 linea3 \n"
 Texto leido: "linea4 linea4 \n"
 Fin de fichero 
{ok}
Para leer un conjunto de caracteres o un carácter suelto podemos utilizar la función io:get_chars/3, que además, de recibir el descriptor de fichero y prompt, recibe el número de caracteres a leer.
A la hora de escribir seguimos utilizando la función io:format/3 que tan versátil nos resulta, o io:put_chars/2.

Trabajando con ficheros binarios

Si sabemos de antemano que el fichero no es muy grande, siempre podemos utilizar la funciones file:read_file/1 y file:write_file/2/3, para leer y escribir los ficheros de una atacada. Pero como este comportamiento no es lo normal, para trabajar con ficheros binarios largos tenemos que pasar de utilizar las funciones de lectura y escritura del módulo io a utilizar la funciones de la librería file. Veamos como leer un fichero binario:
leer_fichero_binario(File) ->
    case file:open(File, [read,binary]) of
        {ok, Fd} ->
            leer_binario(Fd),
            file:close(Fd),
            {ok};
        {error, Motivo} ->
            {error, Motivo}
    end.

leer_binario (Fd) ->
    case file:read(Fd, 512) of
        eof -> 
            io:format(" Fin de fichero ~n");
        {error, Motivo} -> 
            io:format(" Error de lectura: ~p~n", [Motivo]),
            {error,Motivo};
        {ok,Binario} -> 
            io:format(" binario leido: ~p~n", [Binario]), 
            leer_binario(Fd)
    end.
Como puedes el ejemplo es muy parecido a los anterior, tener en cuenta que hemos indicado en la apertura el modo de trabajo binario. Veamos el resultado:
11> c(ficheros).                                 
{ok,ficheros}
12> ficheros:leer_fichero_binario("fichero.png").
 binario leido: <<137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,140,0,0,
                  0,174,8,2,0,0,0,107,198,114,84,0,0,0,9,112,72,89,115,0,0,11,
                  18,0,0,11,18,1,210,221,126,252,0,0,0,8,116,69,88,116,67,111,
    ... >>
 binario leido: <<72,34,137,36,146,72,34,137,36,146,72,34,137,36,146,72,34,137,
                  36,151,126,14,201,184,51,238,140,59,146,72,34,137,36,146,72,
                  34,137,36,146,72,34,137,36,146,72,34,137,36,146,72,34,137,36,
    ... >>
 Fin de fichero 
{ok}
Para ilustrar la escritura he implementado una función copiar. Dicha función realiza una copia de un fichero origen a un fichero destino. Y me ha queda tal y como queda a continuación:
copiar(Origen, Destino) ->
    case file:open(Origen, [read,binary]) of
        {ok, Fd_origen} ->
            case file:open(Destino, [write,binary]) of
                {ok, Fd_destino} ->
                    copiar_datos(Fd_origen, Fd_destino),
                    file:close(Fd_destino),
                    file:close(Fd_origen),
                    {ok};
                {error, Motivo} ->
                    {error, Motivo}
            end;
        {error, Motivo} ->
            {error, Motivo}
    end.

copiar_datos (Fd_origen, Fd_destino) ->
    case file:read(Fd_origen, 512) of
        eof -> 
            io:format(" Terminado ~n");
        {error, Motivo} -> 
            io:format(" Error de lectura: ~p~n", [Motivo]),
            {error,Motivo};
        {ok,Binario} -> 
            file:write(Fd_destino, Binario),
            copiar_datos(Fd_origen, Fd_destino)
    end.
Sencillo ¿verdad?
13> c(ficheros).                                 
{ok,ficheros}
14> ficheros:copiar("fichero.png", "copia.png"). 
 Terminado

Acceso aleatorio

En este último apartado vamos a utilizar la técnica de acceso aleatorio. Es decir, vamos a indicar, a la hora de leer y escribir, la posición.
leer_fichero_acceso_aleatorio(File) ->
    case file:open(File, [read,binary]) of
        {ok, Fd} ->
            leer_acceso_aleatorio(Fd),
            file:close(Fd),
            {ok};
        {error, Motivo} ->
            {error, Motivo}
    end.

leer_acceso_aleatorio (Fd) ->
    case file:pread(Fd, 1, 7) of
        eof -> 
            io:format(" Fin de fichero ~n");
        {error, Motivo} -> 
            io:format(" Error de lectura: ~p~n", [Motivo]),
            {error,Motivo};
        {ok,Datos} -> 
            io:format(" datos leidos: ~p~n", [Datos]) 
    end.

escribir_fichero_acceso_aleatorio(File) ->
    case file:open(File, [write,binary]) of
        {ok, Fd} ->
            escribir_acceso_aleatorio(Fd),
            file:close(Fd),
            {ok};
        {error, Motivo} ->
            {error, Motivo}
    end.

escribir_acceso_aleatorio (Fd) ->
    Dato = << "cosa" >>,
    case file:pwrite(Fd, 1, Dato) of
        {error, Motivo} -> 
            io:format(" Error de escritura: ~p~n", [Motivo]),
            {error,Motivo};
        ok ->
            io:format(" escrito en fichero: ~p~n", [Dato]) 
    end.
Como podemos observar en el código para leer le indicamos la posición y el número de bytes a leer; y para escribir le indicamos la posición en la que deseamos empezar a escribir el dato. Hay que tener en cuenta que la función de escritura sobreescribe los datos a partir de la posición indicada.
14> c(ficheros).     
{ok,ficheros}
15> ficheros:leer_fichero_acceso_aleatorio("fichero.terms").
 datos leidos: <<"persona">>
{ok}
16> ficheros:escribir_fichero_acceso_aleatorio("fichero.terms").
{ok}
15> ficheros:escribir_fichero_acceso_aleatorio("fichero.terms").
 escrito en fichero: <<"cosa">>
{ok}
17> ficheros:leer_fichero_acceso_aleatorio("fichero.terms").    
 datos leidos: <<"cosa">>

Publicar un comentario en la entrada

1 comentarios:

  1. Anónimo dijo...

    hola soy nuevo en este leguaje de programacion en estos momentos estoy haciendo un sitio web y necesito obtener el contenido de archivos PDF (solo el texto no imagenes).
    si saben de algun codigo haganmelo saber a este mail mrao080482@upemor.edu.mx

    atte:Armando

 
Licencia Creative Commons
Aprendiendo Erlang por Verdi se encuentra bajo una Licencia Creative Commons Atribución-NoComercial-CompartirIgual 3.0 Unported.