Skip to content

XML con tinyxml y regex con boost

2 marzo 2010

¡Buenas! Ya queda poco para la fase local del concurso y llevo el proyecto fatal! No he avanzado nada, pero bueno, lo importante es participar. La cosa es que he estado liado con las dos últimas asignaturas que me quedaban pa terminar la carrera y tal. Asignaturas que, por cierto, ¡he aprobado, y con buena nota además! Una de ellas tal vez hasta me pongan la matrícula de honor.

Pero bueno, ahora que no tengo que ir a clase ni tengo asignaturas ni ná de ná, no me queda otra que ponerme con el proyecto.

Dejándolo todo por escrito

Una de las clases principales del juego será la clase Canción, que se encargará de unas cuantas cosas. Esta clase tendrá que cargar la información de la canción: título, bpm y notas que la componen. La información de las canciones se guardará en un xml, uno por canción, que parsearemos usando tinyxml y boost::regex, más abajo explicamos cómo. Tal vez en un futuro se añadan más campos al xml.

El almacenamiento de las notas lo he ideado de la siguiente manera: La clase canción tendrá, por un lado, una pila en la que estarán metidas todas las notas con los datos básicos: altura, figura y posición en el tiempo. Estas notas dentro de la pila no tendrán aún una imagen asociada ni se estarán pintando en pantalla. Por otro lado, dispondremos de un vector de notas instanciadas, en el que estarán las notas que ya deban aparecer en pantalla porque deberán tocarse y sonar en breve.

En cada iteración del Game Loop, la clase canción mirará las notas en el tope de la pila, calculará cuánto tiempo queda para que suenen y, si este tiempo es poco, pasará las notas que sean de la pila al vector, quedando éstas instanciadas y pintándose en pantalla en la posición que les corresponda.

Parseando el XML

Los XML que voy a usar tendrán la siguiente estructura (aún no definitiva):

?xml version="1.0" ?>
<Song>
  <Title>Aquí va el título</Title>
  <BPM>90</BPM>
  <Notes>do5r re5b mi5n fa5c</Notes>
</Song>

Como véis, hay un elemento principal Song, dentro del cual encontramos Title, BPM y Notes. La sintaxis por la que se rigen las notas es la siguiente: primero va la nota que sea (do, re, mí…) luego la octava, que en la flauta sólo puede ser la quinta o la sexta para el Do y el Re, y luego la figura (r = redonda, b = blanca, n = negra, c = corchea).

Para parsear el XML en sí, como dije antes, he utilizado TinyXML, una pequeña pero potente librería libre escrita en C++ con una sintaxis muy sencilla. Son cuatro o cinco archivos fuente que se compilan en tres segundos, con un makefile hecho a mano, muy elegante por cierto.

#define TIXML_USE_STL
#include "tinyxml.h"
#include "tinystr.h"

int main(int argc, char *argv[])
{
    // Creamos un nuevo objeto para leer el xml
    TiXmlDocument doc; 
    doc.LoadFile( "song1.xml");  // Cargamos el XML

    // Los handles son una manera fácil de acceder a los elementos sin tener que lidiar con punteros nulos 
    TiXmlHandle manejador( &doc );

    // Sacamos el elemento que nos interesa navegando por la jerarquía
    TiXmlElement * elemento = manejador
	.FirstChild("Song")
	.FirstChild("Notes")
	.ToElement();

    if(!elemento) cerr << "ERROR" << endl;

    // Sacamos el texto del elemento
    string notas = elemento -> GetText();

En este caso la variable notas contendría la cadena “do5r re5b mi5n fa5c”. Para ahora parsear las notas contenidas en ella, utilizaremos la sección de expresiones regulares de boost, boost::regex. Dentro de boost, regex es una de las librerías más grandes. De hecho, suele distribuirse de manera independiente y hay que compilarla y enlazarla aparte. Las librerías que había utilizado hasta ahora no habían necesitado enlazado porque toda la funcionalidad se encontraba en ficheros de cabecera, pero en este caso he tenido que añadir un -lboost_regex al enlazar el proyecto.

He estado trasteando poco con la librería porque he encontrado rápidamente lo que necesitaba. En mi caso, me ha bastado con definir la expresión regular (utilizando la sintaxis típica) y luego utilizar un iterador para que encuentre todas las concordancias en la cadena de notas. He utilizado paréntesis en la expresión regular para poder definir grupos a los que luego boost nos permite acceder utilizando el operador [ ].

    map<string, string> nombresFiguras;
    nombresFiguras["r"] = "redonda"; 
    nombresFiguras["b"] = "blanca"; 
    nombresFiguras["n"] = "negra"; 
    nombresFiguras["c"] = "corchea"; 

    boost::regex myRegExp("(do|re|mi|fa)(5|6)(r|b|n|c)");

    boost::sregex_iterator myIt(notas.begin(), notas.end(), myRegExp), itEnd;

    for(;myIt != itEnd; myIt++){
	cout << "Nota: " << (*myIt)[1]
	     << ", octava: " << (*myIt)[2] 
	     << ", figura: " << nombresFiguras[(*myIt)[3]] << endl;
    } //*/

Esto imprimiría la siguiente salida:

Nota: do, octava: 5, figura: redonda
Nota: re, octava: 5, figura: blanca
Nota: mi, octava: 5, figura: negra
Nota: fa, octava: 5, figura: corchea

I love it!

2 comentarios leave one →
  1. Olman permalink
    11 junio 2010 0:17

    Mi amigo me da error en la linea 18.ToElement();

  2. Jose permalink
    5 diciembre 2011 23:32

    A mí no, funciona perfectamente. ¡Gracias!

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: