Bienvenidos sean a este post, hoy continuaremos con lo visto en el post anterior como es el modelo completo, sin mas preambulos comencemos con el primer tema.

Anuncios

Un truquito para mejorarlo

Habitualmente en Lua es mas rapido leer el archivo como un todo y no linea por linea, sin embargo en algunas consecuencias podes tener un GRAN archivo (digamos varios MB) por lo cual no es razonable leer todo de una, ahora si quieres manejar este tipo de archivos con la mejor performance es preferible leerlo en chunks razonablemente grandes, por ejemplo 8 KB, pero para evitar el problema de las lineas rotas o interrumpidas debemos simplemente leer el chunk con un line:

local lineas, resto = f:read(BUFTAM, "*line")
Anuncios

La variable resto conseguira el resto de cualquier linea rota por el chunk, luego concatenamos el chunk y el resto de su linea, de esta manera el chunk resultante siempre se interrumpe en el limite de la linea, veamos el siguiente codigo:

io.lua

local BUFTAM = 2^13
local f = io.input(arg[1])
local cc, lc, pc = 0, 0, 0
while true do
	local lineas, resto = f:read(BUFTAM, "*line")
	if not lineas then break end
	if resto then lineas = lineas .. resto .. "\n" end
	cc = cc + #lineas
	local _, t = string.gsub(lineas, "%S+", "")
	pc = pc + t
	_, t = string.gsub(lineas, "\n", "\n")
	lc = lc + t
end
print(lc, pc, cc) 
Anuncios

La primera linea se encarga de crear el buffer de un tamaño de 8 KB, despues abriremos el archivo informado como argumento, despues iniciaremos a las variables cc (cuenta los caracteres), lc (cuenta las lineas) y pc (cuenta las palabras), nuestro siguiente paso sera un bucle infinito, en este primero leeremos la linea y la guardaremos en lineas, y como dijimos antes usaremos a “*line” para añadir lo que falte de la linea en resto, el siguiente es un condicional que en caso de no haber mas lineas sale del bucle, si resto tiene algun contenido el siguiente condicional se encarga de concatenerlo, nuestra siguiente linea se encargara de incrementar a cc, nuestra siguiente linea se encargara de contar las palabras en el chunk y una vez obtenido lo agregaremos a pc, por ultimo tendremos esta linea que se encargara de contar las nuevas lineas lo que equivale a las lineas, una vez obtenido se agregara a lc, con todo esto explicamos el bucle y por ultimo devolvemos todos los valores obtenidos del bucle, si lo probamos obtendremos este resultado:

tinchicus@dbn001dsk:~/lenguaje/lua$ lua5.3 io.lua metamtd01.lua
61      183     1151
tinchicus@dbn001dsk:~/lenguaje/lua$

Podemos ver como nos cuenta todos los datos solicitados, continuemos con el siguiente tema.

Anuncios

Archivos binarios

Las funciones del modelo simple io.input e io.output abren siempre el archivo en modo texto, predeterminado, y en S.O como Unix no tendremos grandes inconvenientes porque no hay muchas diferencias entre archivos binarios y archivos de texto pero en otros sistemas como Windows los archivos binarios deben ser abiertos con un marcador especial, por eso para manejar tales archivos se debe usar io.open con la letra ‘b’ en la cadena de modo, la data binaria en Lua es manejada de forma similar al texto porque una cadena en Lua puede contener cualquier bytes y casi todas las funciones en las librerias pueden manejar bytes arbitrarios e inclusive se puede hacer una coincidencia de patrones con datos binarios, siempre y cuando el patron no posea un byte cero, aunque si quieres coincidir este byte en el tema puedes usar en su lugar la clase %z.

Anuncios

Habitualmente se lee datos binarios con el patron *all, el cual se encarga de leer todo el archivo, o con el patron n, que se encarga de leer n bytes, el siguiente ejemplo convierte un archivo de texto de formato DOS a formato Unix (se encargara de convertir secuencias de retorno-nueva linea a nueva linea), para este caso no usamos los archivos estandar para I/O (stdin, stdout) porque estos archivos los abren en modo texto, tambien vamos a hacerle asumir que los nombres de los archivos de entrada y salida son informados como argumentos del programa:

prog.lua

local ent = assert(io.open(arg[1], "rb"))
local sal = assert(io.open(arg[2], "wb"))

local datos = ent:read("*all")
datos = string.gsub(datos, "\r\n", "\n")
sal:write(datos)

assert(sal:close())
Anuncios

Las primeras dos lineas se encargan de asignar los archivos de entrada y salida en base a lo que informemos al programa, ya lo veremos luego, en el caso de ent le asignamos de lectura (r) y binario (b), para el caso de sal le asignamos escritura (w) y binario (b), luego tendremos la variable local datos que recibira todos los datos del archivos, gracias a *all, nuestro siguiente paso sera sustituir todos retornos-nueva linea por nueva linea, una vez terminado escribiremos en el archivo de salida la modificacion realizada, por ultimo cerramos el archivo de salida, veamos como se llama al programa:

lua5.3 prog.lua archivo.dos archivo.unix
Anuncios

El siguiente programa imprime todas las cadenas encontradas en un archivo binario:

local f = assert(io.open(arg[1], "rb"))
local datos = f:read("*all")
local caracvalidos = "[%w%p%s]"
local patron = string.rep(caracvalidos, 6) .. "+%z"
for p in string.gmatch(datos, patron) do
	print(p)
end
Anuncios

El programa asume que una cadena es cualquier secuencia terminada en cero de seis o mas caracteres validos, donde un caracter valido es cualquier caracter aceptado por el patron caracvalidos, en el codigo el patron comprende a los caracteres alfanumericos, las puntuaciones y los espacios, luego usamos a string.rep y concatenacion para crear un patron que captura todas las secuencias de seis o mas caracvalidos, el %z al final del patron coincide con el cero al final de la cadena, como ultimo ejemplo el siguiente codigo hace un volcado de un archivo binario:

prog.lua

local f = assert(io.open(arg[1], "rb"))
local bloque = 16

while true do
	local bytes = f:read(bloque)
	if not bytes then break end
	for _, b in pairs{string.byte(bytes, 1, -1)} do
		io.write(string.format("%02X ", b))
	end
	io.write(string.rep("   ", bloque - string.len(bytes)))
	io.write(" ", string.gsub(bytes, "%c", "."), "\n")
end 
Anuncios

En este caso el primer argumento es el nombre del archivo de entrada, la salida va a ser la salida estandar, el programa lee el archivo en un chunk de 16 bytes, por cada chunk escribe la representacion hexadecimal de cada byte y luego escribe el chunk como texto, cambiando los caracteres de control por puntos, si lo probamos obtendremos una salida semejante a esta:

tinchicus@dbn001dsk:~/lenguaje/lua$ lua5.3 prog.lua a.lua
6C 6F 63 61 6C 20 6E 6F 6D 62 72 65 64 65 63 6C  local nombredecl
61 72 61 64 6F 73 20 3D 20 7B 7D 0A 0A 73 65 74  arados = {}..set
6D 65 74 61 74 61 62 6C 65 28 5F 47 2C 20 7B 0A  metatable(_G, {.
09 5F 5F 6E 65 77 69 6E 64 65 78 20 3D 20 66 75  .__newindex = fu
6E 63 74 69 6F 6E 28 74 2C 20 6E 2C 20 76 29 0A  nction(t, n, v).
09 09 69 66 20 6E 6F 74 20 6E 6F 6D 62 72 65 64  ..if not nombred
65 63 6C 61 72 61 64 6F 73 5B 6E 5D 20 74 68 65  eclarados[n] the
6E 0A 09 09 09 6C 6F 63 61 6C 20 70 20 3D 20 64  n....local p = d
65 62 75 67 2E 67 65 74 69 6E 66 6F 28 32 2C 20  ebug.getinfo(2,
22 53 22 29 2E 77 68 61 74 0A 09 09 09 69 66 20  "S").what....if
70 20 7E 3D 20 22 6D 61 69 6E 22 20 61 6E 64 20  p ~= "main" and
70 20 7E 3D 20 22 43 22 20 74 68 65 6E 0A 09 09  p ~= "C" then...
09 09 65 72 72 6F 72 28 22 49 6E 74 65 6E 74 61  ..error("Intenta
73 20 65 73 63 72 69 62 69 72 20 75 6E 61 20 76  s escribir una v
61 72 69 61 62 6C 65 20 22 0A 09 09 09 09 2B 20  ariable ".....+
22 6E 6F 20 64 65 63 6C 61 72 61 64 61 20 22 20  "no declarada "
2E 2E 20 6E 2C 20 32 29 0A 09 09 09 65 6E 64 0A  .. n, 2)....end.
09 09 09 6E 6F 6D 62 72 65 64 65 63 6C 61 72 61  ...nombredeclara
64 6F 73 5B 6E 5D 20 3D 20 74 72 75 65 0A 09 09  dos[n] = true...
65 6E 64 0A 09 09 72 61 77 73 65 74 28 74 2C 20  end...rawset(t,
6E 2C 20 76 29 0A 09 65 6E 64 2C 0A 09 5F 5F 69  n, v)..end,..__i
6E 64 65 78 20 3D 20 66 75 6E 63 74 69 6F 6E 28  ndex = function(
5F 2C 20 6E 29 0A 09 09 69 66 20 6E 6F 74 20 6E  _, n)...if not n
6F 6D 62 72 65 64 65 63 6C 61 72 61 64 6F 73 5B  ombredeclarados[
6E 5D 20 74 68 65 6E 0A 09 09 09 65 72 72 6F 72  n] then....error
28 22 49 6E 74 65 6E 74 61 73 20 6C 65 65 72 20  ("Intentas leer
75 6E 61 20 76 61 72 69 61 62 6C 65 20 6E 6F 20  una variable no
64 65 63 6C 61 72 61 64 61 20 22 20 2E 2E 20 6E  declarada " .. n
2C 20 32 29 0A 09 09 65 6C 73 65 0A 09 09 09 72  , 2)...else....r
65 74 75 72 6E 20 6E 69 6C 0A 09 09 65 6E 64 0A  eturn nil...end.
09 65 6E 64 2C 0A 7D 29 0A                       .end,.}).
tinchicus@dbn001dsk:~/lenguaje/lua$
Nota: La tabla con todos los bytes es creada gracias a la linea string.byte(bytes, 1, -1).
Anuncios

En resumen, hoy hemos visto como optimizar nuestra busqueda con el modelo completo, un ejemplo practico de como utilizarlo, tambien hemos visto como trabajar con archivos binarios, para que se usa y un ejemplo practico donde vimos como es su salida, 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