Emulación de la funcionalidad lex en Perl o Python.

Aquí está el trato. ¿Hay una manera de tener cadenas tokenizadas en una línea basada en múltiples expresiones regulares?

Un ejemplo:

Tengo que obtener todas las tags href, su texto correspondiente y algún otro texto basado en una expresión regular diferente. Así que tengo 3 expresiones y me gustaría tokenizar la línea y extraer tokens de texto que coincidan con cada expresión.

De hecho, he hecho esto usando flex (no confundirlo con Adobe), que es una implementación del viejo y viejo lex. lex proporciona una forma elegante de hacerlo ejecutando “acciones” basadas en expresiones. Uno puede controlar la forma en que lex lee un archivo también (lectura basada en bloque / línea).

El problema es que flex realmente produce código C / C ++ que hace el trabajo de tokenización. Tengo un archivo de marca que envuelve todas estas cosas. Me preguntaba si Perl / python puede de alguna manera hacer lo mismo. Es solo que me gustaría hacer todo lo que me gusta en un solo lenguaje de progtwigción.

La tokenización es solo una de las cosas que quiero hacer como parte de mi aplicación.

Aparte de perl o python, ¿puede cualquier lenguaje (funcional también) hacer esto?

Leí acerca de PLY y ANTLR aquí ( Parsing, donde puedo aprender sobre esto ).

Pero, ¿hay una manera de hacerlo naturalmente en Python? perdone mi ignorancia, pero ¿se usan estas herramientas en algún producto / servicio popular?

Gracias.

Si está específicamente después de analizar los enlaces de páginas web, entonces el módulo WWW :: Mechanize de Perl resolverá las cosas de manera muy elegante. Aquí hay un progtwig de ejemplo que toma la primera página de Stack Overflow y analiza todos los enlaces, imprimiendo su texto y las URL correspondientes:

#!/usr/bin/perl use strict; use warnings; use WWW::Mechanize; my $mech = WWW::Mechanize->new; $mech->get("http://stackoverflow.com/"); $mech->success or die "Oh no! Couldn't fetch stackoverflow.com"; foreach my $link ($mech->links) { print "* [",$link->text, "] points to ", $link->url, "\n"; } 

En el bucle principal, cada $link es un objeto WWW :: Mechanize :: Link , por lo que no está limitado a obtener el texto y la URL.

Todo lo mejor,

Pablo

Mire la documentación para los siguientes módulos en CPAN

HTML :: TreeBuilder

HTML :: TableExtract

y

Parse :: RecDescent

He usado estos módulos para procesar páginas web bastante grandes y complejas.

Parece que realmente quieres analizar HTML, te recomiendo que mires cualquiera de los paquetes maravillosos para hacerlo:

  • BeautifulSoup
  • lxml.html
  • html5lib

¡O! Puede usar un analizador como uno de los siguientes:

  • PyParsing
  • DParser – Un analizador GLR con buenos enlaces de python.
  • ANTLR : un generador de analizador decente recursivo que puede generar código de Python.

Este ejemplo es de la documentación de BeautifulSoup:

 from BeautifulSoup import BeautifulSoup, SoupStrainer import re links = SoupStrainer('a') [tag for tag in BeautifulSoup(doc, parseOnlyThese=links)] # [success, # experiments, # BoogaBooga] linksToBob = SoupStrainer('a', href=re.compile('bob.com/')) [tag for tag in BeautifulSoup(doc, parseOnlyThese=linksToBob)] # [success, # experiments] 

¿Has mirado en PyParsing ?

Desde su página de inicio:

Aquí hay un progtwig para analizar “¡Hola, mundo!” (o cualquier saludo del formulario “,!”):

 from pyparsing import Word, alphas greet = Word( alphas ) + "," + Word( alphas ) + "!" # <-- grammar defined here hello = "Hello, World!" print hello, "->", greet.parseString( hello ) 

El progtwig produce lo siguiente:

 Hello, World! -> ['Hello', ',', 'World', '!'] 

Si su problema tiene algo que ver con el raspado web, recomiendo mirar Web :: Scraper , que proporciona una selección fácil de elementos a través de los selectores de CSS y XPath respectivamente. Tengo una charla (en alemán) en Web :: Scraper , pero si la ejecuta a través de babelfish o simplemente mira las muestras de código, eso puede ayudarlo a obtener una visión general rápida de la syntax.

El análisis manual a mano es oneroso y no le dará mucha información sobre el uso de uno de los analizadores HTML preparados previamente. Si su HTML es de una variación muy limitada, puede hacerlo mediante el uso de expresiones regulares inteligentes, pero si ya está utilizando herramientas de análisis de núcleo duras, suena como si su HTML fuera mucho más regular de lo que es sensato analizar. expresiones regulares.

Desde perlop :

Un modismo útil para los escáneres de tipo lex es /\G.../gc . Puede combinar varias expresiones regulares como esta para procesar una cadena parte por parte, haciendo diferentes acciones dependiendo de qué expresión regular coincida. Cada expresión regular intenta coincidir con la anterior.

  LOOP: { print(" digits"), redo LOOP if /\G\d+\b[,.;]?\s*/gc; print(" lowercase"), redo LOOP if /\G[az]+\b[,.;]?\s*/gc; print(" UPPERCASE"), redo LOOP if /\G[AZ]+\b[,.;]?\s*/gc; print(" Capitalized"), redo LOOP if /\G[AZ][az]+\b[,.;]?\s*/gc; print(" MiXeD"), redo LOOP if /\G[A-Za-z]+\b[,.;]?\s*/gc; print(" alphanumeric"), redo LOOP if /\G[A-Za-z0-9]+\b[,.;]?\s*/gc; print(" line-noise"), redo LOOP if /\G[^A-Za-z0-9]+/gc; print ". That's all!\n"; } 

También echa un vistazo a pQuery it como una forma perlish muy agradable de hacer este tipo de cosas …

 use pQuery; pQuery( 'http://www.perl.com' )->find( 'a' )->each( sub { my $pQ = pQuery( $_ ); say $pQ->text, ' -> ', $pQ->toHtml; } ); # prints all HTML anchors on www.perl.com # => link text -> anchor HTML 

Sin embargo, si su requisito está más allá de HTML / Web, aquí está el anterior “¡Hola mundo!” ejemplo en Parse :: RecDescent …

 use strict; use warnings; use Parse::RecDescent; my $grammar = q{ alpha : /\w+/ sep : /,|\s/ end : '!' greet : alpha sep alpha end { shift @item; return \@item } }; my $parse = Parse::RecDescent->new( $grammar ); my $hello = "Hello, World!"; print "$hello -> @{ $parse->greet( $hello ) }"; # => Hello, World! -> Hello , World ! 

Probablemente demasiado de un martillo grande para romper esta tuerca 😉

Modificando el ejemplo de Bruno para incluir la comprobación de errores:

 my $input = "..."; while (1) { if ($input =~ /\G(\w+)/gc) { print "word: '$1'\n"; next } if ($input =~ /\G(\s+)/gc) { print "whitespace: '$1'\n"; next } if ($input !~ /\G\z/gc) { print "tokenizing error at character " . pos($input) . "\n" } print "done!\n"; last; } 

(Tenga en cuenta que usar scalar // g es desafortunadamente el único lugar donde realmente no puede evitar usar las variables $ 1, etc.)