Anuncios

Bienvenidos sean a este post, hoy comenzaremos a hablar sobre la libreria que nos permite manipular los archivos de nuestro sistema, en si para esta sistema se dispone de dos modelos de acceso a los archivos, uno simple y otro completo, en este post nos centraremos en el simple para el completo hablaremos en el proximo y tambien sobre algunas otras operaciones pero sera mas adelante, comencemos con el tema de hoy.

Anuncios

Este modelo hace todas sus operaciones en dos archivos, la libreria inicializa el archivo de entrada como el proceso estandard de entrada (stdin) y el archivo de salida como el proceso estandard de salida (stdout), por lo tanto cuando ejecutamos algo como io.read leemos una linea de la entrada estandard (stdin), podemos cambiar los archivos actuales con las funciones io.input e io.output, una llamada como la siguiente:

io.input(archivo) 
Anuncios

Abre al archivo en modo lectura y lo asigna como el archivo de entrada actual, a partir de este punto todas las entradas provienen de este archivo hasta otra llamada a io.input, por el otro lado io.output hace una tarea similar para la salida, ambas funciones devuelven un error en caso de que se produzca alguna excepcion, si quieres manejar los errores debemos utilizar a io.open del modelo completo lo cual veremos en el proximo post.

Anuncios

Como write es mas simple que read hablaremos de el primero, la funcion io.write simplemente toma un numero arbitrario de argumentos de tipo cadena y los escribe al archivo de salida actual, los numeros son convertidos a cadenas siguiendo las reglas de conversion usual, para un mayor control de la conversion puedes usar la funcion string.format:

> io.write("sin (3) = " .. math.sin(3) .. "\n")
sin (3) = 0.14112000805987
file (0xb7629d60)
Anuncios

Esta es la forma mas basica, veamos con string.format:

> io.write(string.format("sin (3) = %.4f\n", math.sin(3)))
sin (3) = 0.1411
file (0xb7629d60)
Anuncios

A diferencia de print, write no agrega caracteres extra a la salida tales como tabs o nueva lineas (\n) mas aun write esa el archivo de salida actual mientras que print siempre usa la salida estandar, para finalizar print aplica automaticametne tostring a sus argumentos asi que esto le permite mostrar tablas, funciones y nil.

Anuncios

La funcion io.read lee las cadenas desde el archivo de entrada y sus argumentos controlan que es lo que lee:

ArgumentoDescripcion
“*all”Lee el archivo entero
“*line”Lee la proxima linea
“*number”Lee un numero
numLee una cadena hasta el num de caracteres
Anuncios

Cuando nosotros ejecutamos a la llamada:

io.read("*all") 

Esta lee todo el archivo de entrada actual comenzando desde la posicion actual, si estamos al final del archivo o el archivo esta vacio devuelve una cadena vacia esto permite a Lua manejar largas cadenas de manera eficiente, una tecnica simple para escribir filtros en Lua es leer todo el archivo y almacenarlo en una cadena, hacer el procesamiento a la cadena (usualmente con gsub) y luego escribir la cadena al archivo de salida, como se ve a continuacion:

t = io.read("*all")
t = string.gsub(...)           -- Procesamos a la cadena leida anteriormente
io.write(t)
Anuncios

Para nuestro siguiente ejemplo el codigo es un programa para codificar el contenido de archivos usando la codificación imprimible entre comillas MIME, en esta codificacion caracteres no ASCII son codificados como =xx donde xx es el codigo numerico del caracter en hexadecimal, para mantener la consistencia el caracter ‘=’ debe ser codificado tambien:

t = io.read("*all")
t = string.gsub(t, "([\128-\255=])", function(c)
	return string.format("=%02%", string.byte(c))
end)
io.write(t) 
Anuncios

El patron usado en string.gsub captura todos los caracteres del 128 al 255 ademas del signo igual, despues trabaja de la misma forma que vimos hasta ahora.

Anuncios

Para este caso vamos a usar la llamada a:

io.read("*line") 

Esta va a devolver la proxima linea del archivo de entrada actual sin el caracter de nueva linea (\n) cuando alcanzamos el final del archivo la llamada devuelve nil indicando que no hay mas lineas que devolver, este patron es el predeterminado para read, este patron es recomendable usarlo cuando el algoritmo maneja de forma natural al archivo linea por linea, de lo contrario es mas recomendable todo el archivo de una vez, o en bloques como veremos mas adelante, a continuacion veremos un simple ejemplo de este patron en el cual el programa copia la entrada actual a la salida actual numerando cada linea:

for contar = 1, math.huge do
	local linea = io.read()
	if linea == nil then break end
	io.write(string.format("%6d  ", contar), linea, "\n")
end
Anuncios

Sin embargo para iterar sobre todo un archivo linea por linea es mejor hacerlo por medio del iterador io.lines, por ejemplo podemos escribir un programa completo para ordenar las lineas de un archivo:

local lineas = {}
for linea in io.lines() do lineas[#ineas + 1] = linea end
table.sort(lineas)
for _, l in ipairs(lineas) do io.write(l, "\n") end
Anuncios

En este caso primero crearemos una tabla llamada lineas, luego por medio de un bucle for leeremos las lineas y las agregaremos a la tabla lineas, para luego ordenarlas por medio de sort y finalmente usar el iterador ipairs para obtener todo el contenido de la tabla lineas y escribir en nuestro archivo de salida actual por medio de io.write.

Anuncios

La siguiente va a ser la llamada a:

io.read("*number") 

Esto lee un numero del archivo de entrada actual, este es el unico caso donde read devuelve un numero en lugar de una cadena, cuando un programa necesita leer muchos numeros desde un archivo, la ausencia de las cadenas intermedias mejora mucho la performance, la opcion *number ignora cualquier espacio antes del numero y acepta formatos de numeros como -3, +5.2, 1000 y -3.4e-23, si no puede encontrar un numero en la posicion actual de archivo (ya sea por un mal formato o sea el fin del archivo) devueve un nil, se puede llamar a read con multiples opciones donde por cada argumento la funcion devolvera el resultado respectivo, vamos a suponer que tenemos un archivo con tres numeros por pares:

6.0	-3.23	15e12
4.3	234	10000001
....
Anuncios

Con nuestro ejemplo anterior vamos a suponer que queremos imprimir el numero maximo de cada linea, para ello puedes leer los tres numeros con una simple llamada a read, como se ve en el siguiente codigo:

while true do
	local n1, n2, n3 = io.read("*number", "*number", "*number")
	if not n1 then break end
	print(math.max(n1,n2,n3))
end
Anuncios

Igualmente siempre es mas recomendable usar la opcion “*all” para leer todo el archivo y luego desmenuzarlo por medio de gmatch:

local pat = "(%S+)%s+(%S+)%s+(%S+)%s+"
for n1, n2, n3 in string.gmatch(io.read("*all", pat) do
	print(math.max(tonumber(n1), tonumber(n2), tonumber(n3)))
end
Anuncios

Mas alla de los patrones de lecturas basicas, se puede llamar a read con un numero n como argumento, para este caso read intenta leer los caracteres de n del archivo de entrada, si no puede leer ningun caracter (llega al final del archivo) read devuelve nil, de lo contrario devuelve una cadena con la mayoria de los caracteres de n, un ejemplo de este tipo de patron de lectura es el siguiente codigo el cual es una manera eficiente de copiar un archivo de stdin a stdout:

while true do
	local bloque = io.read(2^13)
	if not bloque then break end
	io.write(bloque)
end
Anuncios

En este caso lo mas llamativo es la linea donde creamos la variable local llamada bloque en donde el io.read crea un buffer de un tamaño de 8k, el resto como vimos hasta ahora, pero existe un caso especial el cual es:

io.read(0) 

Esto hace que trabaje como un test para el final del archivo, en este caso devuelve una cadena vacia si hay mas para leer de lo contrario devuelve nil.

Anuncios

En resumen, hoy hemos visto una introduccion de la libreria de I/O, en nuestro primer post vimos como usar el metodo simple para I/O, algunos de sus comodines, para que sirven, una breve explicacion de como usarlos, tambien se comento el metodo practico y simple, y el verdadero metodo que mas debemos usar (“*all”), espero les haya sido util sigueme en Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Tengo un Patreon donde podes acceder de manera exclusiva a material para este blog antes de ser publicado, sigue los pasos del link para saber como.

Tambien podes donar

Es para mantenimiento del sitio, gracias!

$1.00