Эмуляция lex-подобной функциональности в Perl или Python

StackOverflow https://stackoverflow.com/questions/160889

  •  03-07-2019
  •  | 
  •  

Вопрос

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

Один пример:

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

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

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

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

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

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

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

Спасибо.

Это было полезно?

Решение

Если вы специально анализируете ссылки с веб-страниц, то Perl WWW::Механизировать модуль выяснит все за вас очень элегантным образом.Вот пример программы, которая захватывает первую страницу Stack Overflow и анализирует все ссылки, печатая их текст и соответствующие 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::Механизировать::Ссылка объект, поэтому вы не ограничены только получением текста и URL-адреса.

Всего наилучшего,

Павел

Другие советы

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

HTML::Деревостроитель

HTML::TableExtract

и

Разбор::RecDescent

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

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

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

  • ПиПарсинг
  • ДПарсер — Парсер GLR с хорошими привязками к Python.
  • АНТЛР — Рекурсивный приличный генератор парсера, который может генерировать код 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>]

Вы посмотрели ПиПарсинг?

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

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

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

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

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

От перлоп:

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

 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 это действительно хороший способ 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, то вот более ранний «Привет, мир!» пример в Разбор::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 и т. д.переменные.)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top