Эмуляция функциональности lex like в Perl или Python
Вот в чем дело. Существует ли способ маркировать строки в строке на основе нескольких регулярных выражений?
Один пример:
Я должен получить все теги href, их соответствующий текст и некоторый другой текст, основанный на другом регулярном выражении. Итак, у меня есть 3 выражения, и я хотел бы маркировать строку и извлекать маркеры текста, соответствующие каждому выражению.
На самом деле я сделал это с помощью flex (не путать с Adobe), который является реализацией старого доброго lex. лекс обеспечивает элегантный способ сделать это, выполняя "действия", основанные на выражениях. Можно контролировать путь lex чтение файла тоже (чтение на основе блока / строки).
Проблема в том, что flex фактически производит код C/ C++, который фактически выполняет работу по маркированию. У меня есть сделайте файл, который обертывает все эти вещи. Мне было интересно, может ли perl / python каким-то образом сделать то же самое. Просто я хотел бы сделать все, что угодно. Мне нравится в одном языке программирования сам по себе.
Токенизация-это просто одна из вещей, которые я хочу сделать в рамках моего приложения.
Кроме perl или python может ли это сделать любой язык (функциональный также)?
Я действительно читал о PLY и ANTLR здесь (Разбор, где я могу узнать об этом ).
Но есть ли способ сделать это естественным образом в самом python? простите мое невежество, но используются ли эти инструменты в каких-либо популярных продуктах / услугах?
Спасибо.
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, я рекомендую посмотреть на любой из замечательных пакетов для этого:
Или! Вы можете использовать синтаксический анализатор, например, один из следующих:
- 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 и т. д. переменная.)