Эмуляция функциональности lex like в Perl или Python


Вот в чем дело. Существует ли способ маркировать строки в строке на основе нескольких регулярных выражений?

Один пример:

Я должен получить все теги href, их соответствующий текст и некоторый другой текст, основанный на другом регулярном выражении. Итак, у меня есть 3 выражения, и я хотел бы маркировать строку и извлекать маркеры текста, соответствующие каждому выражению.

На самом деле я сделал это с помощью flex (не путать с Adobe), который является реализацией старого доброго lex. лекс обеспечивает элегантный способ сделать это, выполняя "действия", основанные на выражениях. Можно контролировать путь lex чтение файла тоже (чтение на основе блока / строки).

Проблема в том, что flex фактически производит код C/ C++, который фактически выполняет работу по маркированию. У меня есть сделайте файл, который обертывает все эти вещи. Мне было интересно, может ли perl / python каким-то образом сделать то же самое. Просто я хотел бы сделать все, что угодно. Мне нравится в одном языке программирования сам по себе.

Токенизация-это просто одна из вещей, которые я хочу сделать в рамках моего приложения.

Кроме perl или python может ли это сделать любой язык (функциональный также)?

Я действительно читал о PLY и ANTLR здесь (Разбор, где я могу узнать об этом ).

Но есть ли способ сделать это естественным образом в самом python? простите мое невежество, но используются ли эти инструменты в каких-либо популярных продуктах / услугах?

Спасибо.

8 4

8 ответов:

Если вы специально после разбора ссылок из веб-страниц, то модуль Perl WWW::Mechanize разберется с вами очень элегантно. Вот пример программы, которая захватывает первую страницу переполнения стека и анализирует все ссылки, печатая их текст и соответствующие url:

#!/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";
}

В основном цикле каждый $link является объектом WWW::Mechanize::Link, поэтому вы не просто ограничены получением текста и URL.

Все Лучший,

Павел

Посмотрите документацию для следующих модулей на CPAN

HTML:: TreeBuilder

HTML:: TableExtract

И

Parse:: RecDescent

Я использовал эти модули для обработки довольно больших и сложных веб-страниц.

Похоже, вы действительно просто хотите разобрать HTML, я рекомендую посмотреть на любой из замечательных пакетов для этого:

Или! Вы можете использовать синтаксический анализатор, например, один из следующих:

  • PyParsing
  • DParser - парсер GLR с хорошими привязками python.
  • ANTLR - рекурсивный приличный генератор синтаксического анализа, который может генерировать python код.

Этот пример взят из документации BeautifulSoup :

from BeautifulSoup import BeautifulSoup, SoupStrainer
import re

links = SoupStrainer('a')
[tag for tag in BeautifulSoup(doc, parseOnlyThese=links)]
# [<a href="http://www.bob.com/">success</a>, 
#  <a href="http://www.bob.com/plasma">experiments</a>, 
#  <a href="http://www.boogabooga.net/">BoogaBooga</a>]

linksToBob = SoupStrainer('a', href=re.compile('bob.com/'))
[tag for tag in BeautifulSoup(doc, parseOnlyThese=linksToBob)]
# [<a href="http://www.bob.com/">success</a>, 
#  <a href="http://www.bob.com/plasma">experiments</a>]

Вы смотрели на PyParsing ?

С их домашней страницы:

Вот программа для разбора "Привет, Мир!"(или любое приветствие формы ",!"):

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

Программа выдает следующее:

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

Если ваша проблема имеет какое-либо отношение к веб-скребку, я рекомендую посмотреть на Web::Scraper , который обеспечивает легкий выбор элементов через XPath соответственно CSS селекторы. У меня есть (немецкий) разговор на Web::Scraper , но если вы запустите его через babelfish или просто посмотрите примеры кода, это может помочь вам получить краткий обзор синтаксиса.

Ручной синтаксический анализ HTML обременителен и не даст вам много сверх использования одного из готовых синтаксических анализаторов HTML. Если ваш HTML является очень ограниченная вариация, вы можете обойтись с помощью умных регулярных выражений, но если вы уже выпускаете жесткие инструменты синтаксического анализа, это звучит так, как будто ваш HTML гораздо более регулярен, чем то, что разумно анализировать с регулярными выражениями.

От перлоп:

Полезная идиома для лексических сканеров есть /\G.../gc . Вы можете комбинировать несколько регулярных выражений, подобных этому, чтобы обработать a строка по частям, делая разные действия в зависимости от того, какое регулярное выражение соответствие. Каждое регулярное выражение пытается соответствовать там, где заканчивается предыдущая.

 LOOP:
    {
      print(" digits"),       redo LOOP if /\G\d+\b[,.;]?\s*/gc;
      print(" lowercase"),    redo LOOP if /\G[a-z]+\b[,.;]?\s*/gc;
      print(" UPPERCASE"),    redo LOOP if /\G[A-Z]+\b[,.;]?\s*/gc;
      print(" Capitalized"),  redo LOOP if /\G[A-Z][a-z]+\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";
    }

Также проверьте pQuery это действительно хороший перловый способ делать такие вещи....

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

Однако, если ваше требование выходит за рамки HTML / Web, то вот более раннее "Hello World!"пример в 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 !

Вероятно, слишком большой молоток, чтобы расколоть этот орех ; -)

Изменение примера Бруно для включения проверки ошибок:

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;
}

(Обратите внимание, что использование скалярного //g, к сожалению, единственное место, где вы действительно не можете избежать использования $1 и т. д. переменная.)