You're at Mabishu, a website about discovering quality on code, searching emerging technologies, and leading a simpler, more mindful coder life. This site was established in 2005 by Fran Diéguez, a blogger, software developer, and open source geek. Subscribe to the RSS feed for updates.

Procesado de documentos XML con Ruby (I)

Written on June 26th, 2009
.

Uno de los problemas al lidiar con documentos XML es el análisis de los mismos y la representación del resultado en nuestros programas. Actualmente hai dos formas de análisis de esquemas: el análisis en árbol (tree parsing) y el análisis de flujos (stream parsing). En este artículo tratare el primer caso utilizando una de las librerías estándar de Ruby llamada REXML.
Un analizador en árbol lee el documento XML completo y represente su árbol en memoria.

La forma más sencilla de convertir un documento XML en un árbol no prodría ser más fácil.

require "rexml/document"
include REXML

doc = Document.new("<verdad-verdadera>Ya llega el veranito</verdad-verdadera>")
print doc.root.name, ": ", doc.root.text, "\n"

Esto produce:

verdad-verdadera: Ya llega el veranito

Al llamar Document.new() con este ejemplo estamos convirtiendo una cadena de texto en una instancia de la clase Document, pero el metodo new() acepta parámetros de distintos tipos:

  • Las instancias de la clase REXML::Document simplemente se copiarán
  • Las cadenas que contienen documentos XML se analizarán y serán convertidas en instancias de la clase REXML::Document.
  • Las instancias de la clase IO serán leídas y analizadas. Por ejemplo para analizar un fichero llamado ejemplo.xml, deberías llamar a:
Document.new(File.new("ejemplo.xml"))

Para acceder a los elementos o atributos de un nodo existen un par de funciones que nos va a ser de mucha ayuda:

  • .elements es una función que nos devuelve un array con los elementos hijo que contiene un nodo. por lo que podemos iterar sobre ellos.
document.root.elements.each do |hijo|
	puts hijo.text
end
  • .attributes es una función que nos devuelve un hash con los atributos y sus valores correspondientes al elemento desde el que se llama.
  • .text si el elemento a analizar es un nodo con texto o con texto y más elementos hijos podemos extraer sólo la parte plana con esta función.

Hay que tener en cuenta que los índices de los elementos REXML comienzan por 1, y no por 0, por lo que para recoger el primer hijo del elemento raíz deberías llamar a document.root.elements[1] o tambien documento.root[0] (nótese que en este caso si que es 0).

Teneis mucha más documentación en http://www.ruby-doc.org/core/classes/REXML.html

Let's relax us

Written on February 7th, 2009
.

Macroworld of Bali from Alex.Be. on Vimeo.

Real Wall-E

Written on December 20th, 2008
.

Búsqueda avanzada de archivos con find, locate y grep

Written on December 8th, 2008
.

Todos los que trabajamos asiduamente con un terminal no podemos evitar en algún momento lidiar con la tarea de búsqueda de archivos por nombre o incluso por contenido, incluso podemos querer ejecutar comandos con los resultados obtenidos.

Utilizaremos estos dos comandos: find, locate, grep.

Find

Busca en un directorio seleccionado segun unos modificadores proporcionados entre los que podemos destacar

  • -iregex para buscar con una expresión regular sin tener en cuenta las mayusculas/minúsculas
  • -regex para buscar con unha expresión regular teniendo en cuenta las mayusculas/minúsculas
  • -name para buscar segun un nombre dado se puede proporcionar una expresión regular
  • -empty para buscar ficheros vacíos
  • -user para buscar arquivos que pertenecen a un usuario determinado
  • -group para buscar arquivos que pertenecen a un grupo de sistema determinado
  • -atime -mtime etc. utiliza el tiempo de modificación, acceso, creación… de los archivos
  • -executable para buscar archivos con el flag de ejecutable
  • -type [letra] para buscar archivos [letra]=f , directorio [letra]=d, enlaces simbólicos [letra]=l, ficheros socket [letra]=s

además también proporciona modificadores de acción, esto es, modificadores que le dicen al comando que hacer con los resultados

  • -print imprime el nombre de archivo completo en la salida estándar
  • -exec {} ejecuta el comando seleccionado en {}
  • -delete borra el archivo

Ejemplos prácticos:
find . -exec grep “www.athabasca” ‘{}’ \; -print
#busca la cadena “www.athabasca” dentro del contenido de los archivos del directorio actual y los imprime

find . -name "rc.conf" -exec chmod o+r '{}' \;
#le cambia el modo a o+r a los archivos con nombre rc.conf en el directorio actual
find . -perm -g+w,u+w ! -perm -o+w
#busca los archivos que son escribibles por el propietario o su grupo, pero no el resto

Locate

Locate supone un salto en el rendimiento respecto al comando find siempre y cuando busquemos los archivos por su nombre. La razón del rendimiento de locate es que utiliza un índice de nombres de archivos normalmente ubicado en /var/lib/mlocate/mlocate.db. Normalmente tódolos los sistemas UNIX tienen una tarea de cron que actualiza dicha base de datos, aún así si queremos actualizarla manualmente podemos ejecutar el comando updatedb.

Podemos destacar varios modificadores de dicho comando:

  • –limit limita el número de resultados
  • –count imprime el número de resultados en vez de los resultados mismos.
  • –regex busca en la base de datos según una expresión regular

En la siguiente imagen podemos ver la diferencia de rendimiento entre find y locate, encontrado aquí:
Find locate comparison performance

Grep

Muy utilizado comando sobre todo utilizando tuberías ( | ), se utilza principalmente para buscar ficheros según el contenido de los mismos. La mejor forma de observar su comportamiento es mediante ejemplos:

grep 'string' *.txt
#busca la cadena "string" en todos los archivos .txt
grep 'main(' *.c
#busca la cadena "main(" enn todos los archivos .c
grep -i 'ultra' *.conf
#busca la cadena ultra (sin tener en cuenta mayusculas/minúsculas) en los ficheros .conf
grep -iR 'ultra' *.conf
#busca la cadena ultra (sin tener en cuenta mayusculas/minúsculas) en los
  ficheros .conf en el directorio actual y subdirectorios del mismo

Y ahora unos truquillos:

$ grep --color=auto -iR 'getChar();' *.c
#resalta los resultados para ver mejor el texto
$ grep --color=auto -iRnH 'getChar();' *.c
#muestra el nombre de archivo y número de línea donde se encontró la cadena

Y ahora combinando find y grep:

$ find . -name "*.c" -print | xargs grep "main("
#busca en tódos los ficheros .c la cadena "main)"

Como acabamos de ver tenemos diversas posibilidadades, que luego de estudiarnos un poco la sintaxis de los comandos nos harán abandonar las lentas y a veces podo intuitivas GUIs.