Эмуляция функций типа lex в Perl или Python

Здесь сделка. Есть ли способ, чтобы строки, обозначенные в строке на основе нескольких регулярных выражений?

Один пример:

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

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

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

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

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

Здесь я читал о PLY и ANTLR (Parsing, где я могу узнать об этом).

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

Спасибо.

Ответы

Ответ 1

Если вы специально проанализировали ссылки на веб-страницах, то модуль 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.

Все самое лучшее,

Пол

Ответ 2

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

HTML:: TreeBuilder

HTML:: TableExtract

и

Parse:: RecDescent

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

Ответ 3

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

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

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

Этот пример из BeautifulSoup Documentation:

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>]

Ответ 4

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

На главной странице:

Вот программа для синтаксического анализа "Hello, World!" (или любое приветствие формы ",!" ):

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', '!']

Ответ 5

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

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

Ответ 6

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

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 !

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

Ответ 7

От perlop:

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

 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 all!\n";
    }

Ответ 8

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

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 и т.д.)