Hola Gente,
Asi como Daniel me habia incentivado a ver un poco de regex y spirit en C++ lo hice. Lamentablemente lo hice hace tiempo, y siempre postergaba el post para mas adelante. Como veo que Daniel esta muy ocupado y hace mucho que no se le da por hacer sus preguntas locas :) (pero sirven mucho), aca les mando algo de lo que vi de regex y spirit.
Empezamos con regex (
http://www.boost.org/doc/libs/1_35_0/libs/regex/doc/html/index.html). La verdad que la implementacion de esta libreria es muy buena, me hace acordar mucho a como se usan las regex en php u otros lenguajes que la implementan. Eso esta bueno, porque una vez que aprendes mas o menos a reliazar una expresion regular, luego es sencillo ver como el lenguaje o las librerias la soportan. Como ejemplo les presento el parseo de una url que es lo que yo necesitaba. La verda que fue muy sencillo de realizar y en unos minutos uno tiene algo andando
const string REURL = "http://([\\w\\.\\-_\\d]+)(/?.*)";
de acuerdo a esa expresion regular uno puede sacar un codigo como
const boost::regex e( REURL );
boost::smatch what;
if ( boost::regex_match( url, what, e ) )
{
hostName = what[1];
if ( what.size() > 2 && what[2].length() > 0 )
{
page = what[2];
}
else
{
page = "/";
}
}
donde es muy facil acceder a los elementos que se han hecho match a traves del operador []. Solo construi la expresion regular a partir de la constante, luego cree un objeto que va a contener el resultado de la expresion y por ultimo solo llamo a ver si la expresion coincide.
Dani, ahora te queda a vos ver que es lo que tiene la exp regular.
La unica contrapartida que le vi es que al linkear, le tenemos que especificar la libreria boost_regex. Acostumbrado a no especificar librerias en C++ por usar mucho templates, me parecio raro, pero depende si el destino es para un sistema embebido o uno no embebido.
Para spirit la cosa fue bastante mas complicada. Me llevo tiempo entender pero vi que es muy potente. Spirit en si es un analizador lexico que permite parsear una gramatica. Es decir, a partir de una gramatica, podemos llegar a un parser bastante interesante, como aquellos que alguna vez usamos lex e yacc.
El ejemplo que yo implemente es un mini parser de un form en html. La verdad que estuve lidiando un poco con la gramatica y los casos especiales, asi que en un momento corte de casos especiales porque no queria un browser, solo un ejemplo de spirit :)
El parser que arme fue el siguiente
struct form_parser : public grammar<form_parser>
{
/** Form attributes **/
FormParser::InputContainer &fields;
FormParserResult result;
form_parser( FormParser::InputContainer &fields, FormParser::ActionType &action, FormParser::SubmitName &subname ) : fields( fields ), result( fields, action, subname ) { }
template <typename ScannerT>
struct definition
{
definition( form_parser const &const_self )
{
form_parser &self = const_cast<form_parser&>( const_self );
attributes = lexeme_d[
as_lower_d[
+alnum_p >>
!( '=' >>
!ch_p('"') >>
+(alnum_p | '.' | '/' | '_' ) >>
!ch_p('"') ) ]];
input = ch_p('<') >> as_lower_d[ "input" ] >> *attributes[ boost::bind( &attribute, boost::ref(self.result), _1, _2 ) ] >> str_p("/>");
br = as_lower_d["<br/>"];
text = +(~ch_p('<') & anychar_p);
form_open = ch_p('<') >> as_lower_d["form"] >> *attributes[boost::bind( &attribute, boost::ref(self.result), _1, _2 )] >> ch_p('>');
form_close = as_lower_d[str_p("</form>")];
html_open = ch_p('<') >> as_lower_d["html"] >> ch_p('>');
html_close = as_lower_d[str_p("</html>")];
body_open = ch_p('<') >> as_lower_d["body"] >> ch_p('>');
body_close = as_lower_d[str_p("</body>")];
form = !html_open >>
!body_open >>
form_open[boost::bind( &form_creator, boost::ref(self.result), _1, _2 )] >>
*( input[boost::bind( &input_creator, boost::ref(self.result), _1, _2 )] |
text[&print] |
br[&newline] ) >>
form_close >>
!body_close >>
!html_close >> (*anychar_p)[&print];
}
rule<ScannerT> const &start() { return form; }
rule<ScannerT> attributes,
input,
text,
br,
html_open, html_close,
body_open, body_close,
form_open, form_close,
form;
};
};
Como veran es un codigo medio complejo, pero les puedo comentar lo que si vale la pena y luego si tienen ganas lo pueden probar (no creo que tengan taaantas ganas :-P )
Primero que nada, el parser es una clase que debe implementar la clase de spirit "grammar" quien debe conocer la clase que hereda como parametro del template. Este es un concepto que no recuerdo el nombre, asi que Daniel o Fernando podrian ayudarme en este sentido.
Como parte de la clase se definen como atributos los elementos que van a ser parseados, en este caso form, form_open, etc. y en el metodo "definition" uno completa lo que cada elemento va a parser. Para esto, spirit contiene un monton de elementos implementados que nos permiten ir componiendo la gamatica de forma facil. Por ejemplo br se define como el tag as_lower_d["<br/>"] donde dice que la entrada la tomamos en lower case y la comparamos con "<br/>".
Para mas informacion de spirit y sus componentes puede ir a
http://www.boost.org/doc/libs/1_35_0/libs/spirit/index.htmlLo mas interesante de todo esto, es que por cada componente que se reconoce en la gramatica se le puede asociar una funcion o functor a ser llamado cuando la ocurrencia de ese componente aparece en el texto parseado. Por ejemplo, siguiendo el ejemplo de br, cuando se crea el form una de las partes contiene
br[&newline]
esto esta diciendo que cada vez que se encuentre el componente br (que fue el que creamos anteriormente) llame a la funcion newline.
Esto realmente es muuy potente, y si lo juntamos con funtores y demas elementos de la stl o boost podemos lograr cosas muuuy interesantes, como las que puse en el caso del componente input cuando reconoce attributes.
Mi unica preocupacion despues de haber usado spirit fue que luego de compilar me genero un binario de 3 MB solo para esa gramatica!!!!! Imaginense algo mas grande. Lo bueno es que no tuve que linkear con nada. Ademas, stripeado quedo en 300 k. Anyway, para un sistema embebido es demasiado.
Como conclusion, les puedo decir que tanto spirit como regex son excelentes, pero son para cosas totalmente diferentes. Las regex son solo para cosas chicas donde necesitamos realizar una busqueda en un texto o necesitamos que el texto se de tal forma, pero no para hacer algo con gramaticas, para eso esta spirit. Cuando se metan con spirit, realmente ponganle pilas a la gramatica porque es fundamental, luego el codigo sale solo.
Espero que esta mini introduccion les sirva de algo y si necesitan alguna explicacion mas en detalle avisenme.
Saludos a todos.
David
PD: Si llegaron hasta aca leyendo se los agradezco mucho!!! Me llevo su tiempo escribir esto :)
PD2: Dani, FELIZ CUMPLE!!! espero que la pases de 10 y lo disfrutes Abrazo (se que vos vas a leer hasta aca :) )
2008/5/30 Daniel Gutson
<danielgutson-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Ya que clamás que esto sirve en C++, y que está on-topic, y que boost y todo eso, te propongo, para que a todos los que nos interesa el C++ nos sirva, que de hecho postees el programita que lo implementa, usando a) Boost.Regex, y b) Boost.Spirit.
De esa manera sí compro y me interesa mucho ver como queda. A mí por lo menos. Vos sabé que me sube la presión y me broto cuando pasa mucho tiempo y no veo código en C++. Con este thread ya tengo una eruptiva.
Daniel.
:)
On 5/30/08, David Elfi <david.elfi-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
Mas alla de que Daniel piense que esto esta off the topic, yo lo creo muy interesante sobre todo porque uno nunca sabe como trabajar bien con texto y esto ayuda a que empecemos a ver como podemos usar Regex de Boost
http://www.boost.org/doc/libs/1_35_0/libs/regex/doc/html/index.html
Yo lo estoy usando para un pequeno practico (el mismo de los streams que antes les comente) y es muy potente. Pero para usarlo necesitas conocer expresiones regulares!! Y aprender boost.RegEx y expresiones regulares al mismo tiempo creo que puede ser muuuy complicado.
Es solo mi opinion ... aguafiestas ... jaja
David
On Fri, May 30, 2008 at 12:36 PM, Daniel Gutson <
danielgutson <at> gmail.com> wrote:
On 5/30/08, David Elfi <david.elfi-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
Perdon me olvide de eso.
Expliquemos paso a paso:
> cat test | perl -n -e '/^ABC_.*(ABC-.*)$/ && print "var=\"$1\"\n";'
1. cat test hace un cat del archivo que contiene la info
2. perl -n realiza un bucle que lee toda la info que viene por la
entrada estandar y que no imprime lo que va leyendo. En caso de que se
quiera imprimir se tiene que utilizar la opcion -p
3. la opcion de -e de perl significa que se tiene que evaluar los
comandos a continuacion por cada una de las lineas leidas de la
entrada estandar. Segun la explicacion del man de perl en linux
tenemos
LINE:
while (<>) {
... # your program goes here
}
-n significa ese while y el -e es lo que dice "# your program goes here"
4. lo que esta entre // es la expresion que se va a evaluar para cada linea.
5. La expresion esta compuesta de "^alguna expresion$". El simbolo ^
significa comienzo de linea y el $ significa fin de linea.
6. Luego tenemos ABC_.* en esta parte buscamos ABC_ como patron fijo
y .* donde . es cualquie caracter y el * dice que se tiene que
reconocer 0 o mas veces. Podemos reconocer 1 o mas veces utilizando el
signo +, donde la expresion quedaria ABC_.+
7. Luego aparecen los ( ) y una expresion dentro muy parecida a la
anterior. Esto es, todo lo que reconozca dentro del patron que esta en
los parentesis va a ponerlo en una variable que es $1 (con el $ se
indican las variables en perl).
8. Por ultimo tenemos el print que lo unico que hace es imprimir
"var='contenido-de-la-expresion'"
Para ser mas grafico veamos el ejemplo que enviaste
ABC_ DEF=123 and ABC- I=hello world
^^^^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^ ^^^^^^^^^^^^^^^^
^ ABC_ .* ABC- .* $
Espero que quede claro. Si quieren ver mas de esto pongan "man
perlrequick", "man perlretut" o "man perlre".
Espero les sirva.
Saludos
David
On Fri, May 30, 2008 at 9:36 AM, Fernando Cacciola
<fernando.cacciola-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>
> Hola David,
>
> > Ok, yo siempre use perl para este tipo de cosas, no se si vale la
> > respuesta,
> > pero como estas preguntando solo de expresiones regulares podemos
> > adaptarla
> > a la tool que quieras.
>
> Claro.. de hecho me olvidé nada menos que del mismísimo perl cuando mencioné
> las herramientas a usar para esto!
>
> > Mi forma de resolverlo seria algo como
> >
> > cat test | perl -n -e '/^ABC_.*(ABC-.*)$/ && print "var=\"$1\"\n";'
> >
> > suponiendo que el archivo test tiene las lineas que mencionas.
> > Las variables $1,$2, $n en perl corresponden a las expresiones que estan
> > entre corchetes cuando se hace un matching.
> >
> Eso esta perfecto (jaja ahora que se como hacerlo me hago el canchero!)
>
> Pero te falta explicarlo, si no no vale ;)
>
> (y no estás asignando el match a una variable)
>
> Salu2
>
> Fernando
>
>
>
> >