Что делать Шаблон регулярного выражения не совпадает нигде в строке?

Я пытаюсь сопоставить типы скрытых полей <input> с помощью этого шаблона:

/<input type="hidden" name="([^"]*?)" value="([^"]*?)" />/

Это данные формы образца:

<input type="hidden" name="SaveRequired" value="False" /><input type="hidden" name="__VIEWSTATE1" value="1H4sIAAtzrkX7QfL5VEGj6nGi+nP" /><input type="hidden" name="__VIEWSTATE2" value="0351118MK" /><input type="hidden" name="__VIEWSTATE3" value="ZVVV91yjY" /><input type="hidden" name="__VIEWSTATE0" value="3" /><input type="hidden" name="__VIEWSTATE" value="" /><input type="hidden" name="__VIEWSTATE" value="" />

Но я не уверен, что атрибуты type, name и value всегда будут отображаться в том же порядке. Если атрибут type появился последним, совпадение завершится неудачно, потому что в моем шаблоне он находится в начале.

Вопрос:
Как изменить шаблон, чтобы он соответствовал независимо от позиции атрибутов в теге <input>?

PS: Кстати, я использую Adobe AIR, основанный на RegEx Desktop Tool для проверки регулярных выражений.

Ответы

Ответ 1

Вопреки всем ответам здесь, для того, что вы пытаетесь сделать, regex - это совершенно правильное решение. Это потому, что вы не пытаетесь сопоставить сбалансированные теги - это невозможно с помощью регулярного выражения! Но вы только сопоставляете то, что в одном теге, и это совершенно правильное.

Вот проблема, правда. Вы не можете сделать это только с одним регулярным выражением... вам нужно сделать одно совпадение, чтобы захватить тэг <input>, а затем продолжить обработку. Обратите внимание, что это будет работать, только если ни одно из значений атрибутов не имеет в них символа >, поэтому оно не идеально, но оно должно быть достаточным для правильных входов.

Здесь приведен код Perl (псевдо), чтобы показать, что я имею в виду:

my $html = readLargeInputFile();

my @input_tags = $html =~ m/
    (
        <input                      # Starts with "<input"
        (?=[^>]*?type="hidden")     # Use lookahead to make sure that type="hidden"
        [^>]+                       # Grab the rest of the tag...
        \/>                         # ...except for the />, which is grabbed here
    )/xgm;

# Now each member of @input_tags is something like <input type="hidden" name="SaveRequired" value="False" />

foreach my $input_tag (@input_tags)
{
  my $hash_ref = {};
  # Now extract each of the fields one at a time.

  ($hash_ref->{"name"}) = $input_tag =~ /name="([^"]*)"/;
  ($hash_ref->{"value"}) = $input_tag =~ /value="([^"]*)"/;

  # Put $hash_ref in a list or something, or otherwise process it
}

Основной принцип здесь - не пытайтесь делать слишком много с одним регулярным выражением. Как вы заметили, регулярные выражения обеспечивают соблюдение определенного порядка. Итак, что вам нужно сделать, это сначала сопоставить CONTEXT с тем, что вы пытаетесь извлечь, а затем выполнить поддевку на нужные вам данные.

EDIT: Тем не менее, я соглашусь с тем, что использование парсера HTML, вероятно, проще и лучше, и вам действительно стоит подумать о перепроектировании кода или повторной проверке ваших целей.:-) Но я должен был опубликовать этот ответ в качестве противоположности реакции коленного рефлекса, что синтаксический анализ любого подмножества HTML невозможен: HTML и XML являются нерегулярными, если вы рассматриваете всю спецификацию, но спецификация тега является прилично регулярной, безусловно, в рамках PCRE.

Ответ 2

О да, вы можете использовать регулярные выражения для разбора HTML!

Для задачи, которую вы пытаетесь, регулярные выражения прекрасно подходят!

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

Но это не какой-то фундаментальный недостаток, связанный с вычислительной теорией. Эта глупость много попугаев здесь, но вы не верите им.

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

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

Но я. ☻


Общие решения для парсинга HTML на основе регулярных выражений

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

for (;;) {
  given ($html) {
    last                    when (pos || 0) >= length;
    printf "\@%d=",              (pos || 0);
    print  "doctype "   when / \G (?&doctype)  $RX_SUBS  /xgc;
    print  "cdata "     when / \G (?&cdata)    $RX_SUBS  /xgc;
    print  "xml "       when / \G (?&xml)      $RX_SUBS  /xgc;
    print  "xhook "     when / \G (?&xhook)    $RX_SUBS  /xgc;
    print  "script "    when / \G (?&script)   $RX_SUBS  /xgc;
    print  "style "     when / \G (?&style)    $RX_SUBS  /xgc;
    print  "comment "   when / \G (?&comment)  $RX_SUBS  /xgc;
    print  "tag "       when / \G (?&tag)      $RX_SUBS  /xgc;
    print  "untag "     when / \G (?&untag)    $RX_SUBS  /xgc;
    print  "nasty "     when / \G (?&nasty)    $RX_SUBS  /xgc;
    print  "text "      when / \G (?&nontag)   $RX_SUBS  /xgc;
    default {
      die "UNCLASSIFIED: " .
        substr($_, pos || 0, (length > 65) ? 65 : length);
    }
  }
}

Видишь, как легко это читать?

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

У меня нет неудачных тестов (слева :): я успешно запустил этот код на более чем 100 000 HTML файлов - каждый из которых я мог быстро и легко достать. Помимо этого, я также запускаю его на файлах, специально созданных для того, чтобы сломать наивные парсеры.

Это не наивный парсер.

О, я уверен, что это не идеально, но мне еще не удалось сломать это. Я полагаю, что даже если бы что-то и было, исправить это было бы легко из-за четкой структуры программ. Даже регулярные программы должны иметь структуру.

Теперь это не так, позвольте мне ответить на вопрос ОП.

Демонстрация решения задачи OP с помощью регулярных выражений

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

% html_input_rx Amazon.com-_Online_Shopping_for_Electronics,_Apparel,_Computers,_Books,_DVDs_\&_more.htm 
input tag #1 at character 9955:
       class => "searchSelect"
          id => "twotabsearchtextbox"
        name => "field-keywords"
        size => "50"
       style => "width:100%; background-color: #FFF;"
       title => "Search for"
        type => "text"
       value => ""

input tag #2 at character 10335:
         alt => "Go"
         src => "http://g-ecx.images-amazon.com/images/G/01/x-locale/common/transparent-pixel._V192234675_.gif"
        type => "image"

Разбор тегов ввода, не вижу злого ввода

Вот источник для программы, которая произвела вывод выше.

#!/usr/bin/env perl
#
# html_input_rx - pull out all <input> tags from (X)HTML src
#                  via simple regex processing
#
# Tom Christiansen <[email protected]>
# Sat Nov 20 10:17:31 MST 2010
#
################################################################

use 5.012;

use strict;
use autodie;
use warnings FATAL => "all";    
use subs qw{
    see_no_evil
    parse_input_tags
    input descape dequote
    load_patterns
};    
use open        ":std",
          IN => ":bytes",
         OUT => ":utf8";    
use Encode qw< encode decode >;

    ###########################################################

                        parse_input_tags 
                           see_no_evil 
                              input  

    ###########################################################

until eof(); sub parse_input_tags {
    my $_ = shift();
    our($Input_Tag_Rx, $Pull_Attr_Rx);
    my $count = 0;
    while (/$Input_Tag_Rx/pig) {
        my $input_tag = $+{TAG};
        my $place     = pos() - length ${^MATCH};
        printf "input tag #%d at character %d:\n", ++$count, $place;
        my %attr = ();
        while ($input_tag =~ /$Pull_Attr_Rx/g) {
            my ($name, $value) = @+{ qw< NAME VALUE > };
            $value = dequote($value);
            if (exists $attr{$name}) {
                printf "Discarding dup attr value '%s' on %s attr\n",
                    $attr{$name} // "<undef>", $name;
            } 
            $attr{$name} = $value;
        } 
        for my $name (sort keys %attr) {
            printf "  %10s => ", $name;
            my $value = descape $attr{$name};
            my  @Q; given ($value) {
                @Q = qw[  " "  ]  when !/'/ && !/"/;
                @Q = qw[  " "  ]  when  /'/ && !/"/;
                @Q = qw[  ' '  ]  when !/'/ &&  /"/;
                @Q = qw[ q( )  ]  when  /'/ &&  /"/;
                default { die "NOTREACHED" }
            } 
            say $Q[0], $value, $Q[1];
        } 
        print "\n";
    } 

}

sub dequote {
    my $_ = $_[0];
    s{
        (?<quote>   ["']      )
        (?<BODY>    
          (?s: (?! \k<quote> ) . ) * 
        )
        \k<quote> 
    }{$+{BODY}}six;
    return $_;
} 

sub descape {
    my $string = $_[0];
    for my $_ ($string) {
        s{
            (?<! % )
            % ( \p{Hex_Digit} {2} )
        }{
            chr hex $1;
        }gsex;
        s{
            & \043 
            ( [0-9]+ )
            (?: ; 
              | (?= [^0-9] )
            )
        }{
            chr     $1;
        }gsex;
        s{
            & \043 x
            ( \p{ASCII_HexDigit} + )
            (?: ; 
              | (?= \P{ASCII_HexDigit} )
            )
        }{
            chr hex $1;
        }gsex;

    }
    return $string;
} 

sub input { 
    our ($RX_SUBS, $Meta_Tag_Rx);
    my $_ = do { local $/; <> };  
    my $encoding = "iso-8859-1";  # web default; wish we had the HTTP headers :(
    while (/$Meta_Tag_Rx/gi) {
        my $meta = $+{META};
        next unless $meta =~ m{             $RX_SUBS
            (?= http-equiv ) 
            (?&name) 
            (?&equals) 
            (?= (?&quote)? content-type )
            (?&value)    
        }six;
        next unless $meta =~ m{             $RX_SUBS
            (?= content ) (?&name) 
                          (?&equals) 
            (?<CONTENT>   (?&value)    )
        }six;
        next unless $+{CONTENT} =~ m{       $RX_SUBS
            (?= charset ) (?&name) 
                          (?&equals) 
            (?<CHARSET>   (?&value)    )
        }six;
        if (lc $encoding ne lc $+{CHARSET}) {
            say "[RESETTING ENCODING $encoding => $+{CHARSET}]";
            $encoding = $+{CHARSET};
        }
    } 
    return decode($encoding, $_);
}

sub see_no_evil {
    my $_ = shift();

    s{ <!    DOCTYPE  .*?         > }{}sx; 
    s{ <! \[ CDATA \[ .*?    \]\] > }{}gsx; 

    s{ <script> .*?  </script> }{}gsix; 
    s{ <!--     .*?        --> }{}gsx;

    return $_;
}

sub load_patterns { 

    our $RX_SUBS = qr{ (?(DEFINE)
        (?<nv_pair>         (?&name) (?&equals) (?&value)         ) 
        (?<name>            \b (?=  \pL ) [\w\-] + (?<= \pL ) \b  )
        (?<equals>          (?&might_white)  = (?&might_white)    )
        (?<value>           (?&quoted_value) | (?&unquoted_value) )
        (?<unwhite_chunk>   (?: (?! > ) \S ) +                    )
        (?<unquoted_value>  [\w\-] *                              )
        (?<might_white>     \s *                                  )
        (?<quoted_value>
            (?<quote>   ["']      )
            (?: (?! \k<quote> ) . ) *
            \k<quote> 
        )
        (?<start_tag>  < (?&might_white) )
        (?<end_tag>          
            (?&might_white)
            (?: (?&html_end_tag) 
              | (?&xhtml_end_tag) 
             )
        )
        (?<html_end_tag>       >  )
        (?<xhtml_end_tag>    / >  )
    ) }six; 

    our $Meta_Tag_Rx = qr{                          $RX_SUBS 
        (?<META> 
            (?&start_tag) meta \b
            (?:
                (?&might_white) (?&nv_pair) 
            ) +
            (?&end_tag)
        )
    }six;

    our $Pull_Attr_Rx = qr{                         $RX_SUBS
        (?<NAME>  (?&name)      )
                  (?&equals) 
        (?<VALUE> (?&value)     )
    }six;

    our $Input_Tag_Rx = qr{                         $RX_SUBS 

        (?<TAG> (?&input_tag) )

        (?(DEFINE)

            (?<input_tag>
                (?&start_tag)
                input
                (?&might_white) 
                (?&attributes) 
                (?&might_white) 
                (?&end_tag)
            )

            (?<attributes>
                (?: 
                    (?&might_white) 
                    (?&one_attribute) 
                ) *
            )

            (?<one_attribute>
                \b
                (?&legal_attribute)
                (?&might_white) = (?&might_white) 
                (?:
                    (?&quoted_value)
                  | (?&unquoted_value)
                )
            )

            (?<legal_attribute> 
                (?: (?&optional_attribute)
                  | (?&standard_attribute)
                  | (?&event_attribute)
            # for LEGAL parse only, comment out next line 
                  | (?&illegal_attribute)
                )
            )

            (?<illegal_attribute>  (?&name) )

            (?<required_attribute> (?#no required attributes) )

            (?<optional_attribute>
                (?&permitted_attribute)
              | (?&deprecated_attribute)
            )

            # NB: The white space in string literals 
            #     below DOES NOT COUNT!   It just 
            #     there for legibility.

            (?<permitted_attribute>
                  accept
                | alt
                | bottom
                | check box
                | checked
                | disabled
                | file
                | hidden
                | image
                | max length
                | middle
                | name
                | password
                | radio
                | read only
                | reset
                | right
                | size
                | src
                | submit
                | text
                | top
                | type
                | value
            )

            (?<deprecated_attribute>
                  align
            )

            (?<standard_attribute>
                  access key
                | class
                | dir
                | ltr
                | id
                | lang
                | style
                | tab index
                | title
                | xml:lang
            )

            (?<event_attribute>
                  on blur
                | on change
                | on click
                | on dbl   click
                | on focus
                | on mouse down
                | on mouse move
                | on mouse out
                | on mouse over
                | on mouse up
                | on key   down
                | on key   press
                | on key   up
                | on select
            )
        )
    }six;

}

UNITCHECK {
    load_patterns();
} 

END {
    close(STDOUT) 
        || die "can't close stdout: $!";
} 

Вот, пожалуйста! Ничего подобного! :)

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

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

Так что мне делать?

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

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

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

Оптимальные регулярные выражения для небольших задач разбора HTML, а для больших - пессимальные

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

Шаблоны не должны быть безобразными, и они не должны быть жесткими. Если вы создаете уродливые узоры, это отражение вас, а не их.

Феноменально изысканный язык регулярных выражений

Меня попросили указать, что мое решение вашей проблемы было написано на Perl. Вы удивлены? Вы не заметили? Является ли это откровение бомбой?

Это правда, что не все другие инструменты и языки программирования столь же удобны, выразительны и мощны, когда речь идет о регулярных выражениях, как Perl. Существует большой спектр, причем некоторые из них являются более подходящими, чем другие. В целом, с языками, в которых регулярные выражения выражены как часть основного языка, а не как библиотека, легче работать. Я ничего не сделал с регулярными выражениями, которые вы не могли бы сделать, скажем, в PCRE, хотя вы бы по-другому структурировали программу, если бы использовали C.

В конце концов, другие языки будут догонять то, где сейчас находится Perl с точки зрения регулярных выражений. Я говорю это потому, что когда Perl начинал, никто не имел ничего подобного регулярным выражениям Perls. Скажите что угодно, но именно здесь Perl явно выиграл: все копировали Perls регулярные выражения, хотя и на разных этапах своего развития. Perl впервые применил почти (не совсем все, но почти) все, на что вы сегодня полагаетесь в современных моделях, независимо от того, какой инструмент или язык вы используете. Так что в конце концов остальные догонят.

Но они только догонят, где Perl был когда-то в прошлом, так же, как сейчас. Все продвигается. В регулярных выражениях, если ничего другого, куда ведет Perl, следуют другие. Где будет Perl, когда все наконец поймут, где сейчас находится Perl? Понятия не имею, но знаю, что мы тоже переедем. Вероятно, лучше быть ближе к стилю создания Perl.

Если вам нравятся такие вещи, но вы хотели бы использовать их в Perl₅, вас может заинтересовать Damian Conways замечательный Regexp :: Grammars. Это совершенно потрясающе и делает то, что я сделал здесь в моей программе, таким же примитивным, как и мое, делает шаблоны, которые люди собирают вместе, без пробелов и буквенных идентификаторов. Проверьте это!


Простой HTML Chunker

Вот полный исходный код парсера, с которого я показал центральную часть в начале этой публикации.

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

Конечно, это нелегко, но это возможно!

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

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

Я надеюсь, что в будущем мы увидим более справедливое и честное рассмотрение вопросов о HTML и регулярных выражениях.

Вот мой HTML-лексер. Он не пытается выполнить проверочный анализ; он просто определяет лексические элементы. Вы можете думать об этом больше как о блоке HTML, чем о парсере HTML. Разбитый HTML не очень простителен, хотя в этом направлении он делает некоторые небольшие поправки.

Даже если вы никогда не анализируете полный HTML самостоятельно (и почему вы должны решить эту проблему!), В этой программе есть много интересных элементов регулярных выражений, из которых, я полагаю, многие люди могут многому научиться. Наслаждайтесь!

#!/usr/bin/env perl
#
# chunk_HTML - a regex-based HTML chunker
#
# Tom Christiansen <[email protected]
#   Sun Nov 21 19:16:02 MST 2010
########################################

use 5.012;

use strict;
use autodie;
use warnings qw< FATAL all >;
use open     qw< IN :bytes OUT :utf8 :std >;

MAIN: {
  $| = 1;
  lex_html(my $page = slurpy());
  exit();
}

########################################################################
sub lex_html {
    our $RX_SUBS;                                        ###############
    my  $html = shift();                                 # Am I...     #
    for (;;) {                                           # forgiven? :)#
        given ($html) {                                  ###############
            last                when (pos || 0) >= length;
            printf "\@%d=",          (pos || 0);
            print  "doctype "   when / \G (?&doctype)  $RX_SUBS  /xgc;
            print  "cdata "     when / \G (?&cdata)    $RX_SUBS  /xgc;
            print  "xml "       when / \G (?&xml)      $RX_SUBS  /xgc;
            print  "xhook "     when / \G (?&xhook)    $RX_SUBS  /xgc;
            print  "script "    when / \G (?&script)   $RX_SUBS  /xgc;
            print  "style "     when / \G (?&style)    $RX_SUBS  /xgc;
            print  "comment "   when / \G (?&comment)  $RX_SUBS  /xgc;
            print  "tag "       when / \G (?&tag)      $RX_SUBS  /xgc;
            print  "untag "     when / \G (?&untag)    $RX_SUBS  /xgc;
            print  "nasty "     when / \G (?&nasty)    $RX_SUBS  /xgc;
            print  "text "      when / \G (?&nontag)   $RX_SUBS  /xgc;
            default {
                die "UNCLASSIFIED: " .
                  substr($_, pos || 0, (length > 65) ? 65 : length);
            }
        }
    }
    say ".";
}
#####################
# Return correctly decoded contents of next complete
# file slurped in from the <ARGV> stream.
#
sub slurpy {
    our ($RX_SUBS, $Meta_Tag_Rx);
    my $_ = do { local $/; <ARGV> };   # read all input

    return unless length;

    use Encode   qw< decode >;

    my $bom = "";
    given ($_) {
        $bom = "UTF-32LE" when / ^ \xFf \xFe \0   \0   /x;  # LE
        $bom = "UTF-32BE" when / ^ \0   \0   \xFe \xFf /x;  #   BE
        $bom = "UTF-16LE" when / ^ \xFf \xFe           /x;  # le
        $bom = "UTF-16BE" when / ^ \xFe \xFf           /x;  #   be
        $bom = "UTF-8"    when / ^ \xEF \xBB \xBF      /x;  # st00pid
    }
    if ($bom) {
        say "[BOM $bom]";
        s/^...// if $bom eq "UTF-8";                        # st00pid

        # Must use UTF-(16|32) w/o -[BL]E to strip BOM.
        $bom =~ s/-[LB]E//;

        return decode($bom, $_);

        # if BOM found, don't fall through to look
        #  for embedded encoding spec
    }

    # Latin1 is web default if not otherwise specified.
    # No way to do this correctly if it was overridden
    # in the HTTP header, since we assume stream contains
    # HTML only, not also the HTTP header.
    my $encoding = "iso-8859-1";
    while (/ (?&xml) $RX_SUBS /pgx) {
        my $xml = ${^MATCH};
        next unless $xml =~ m{              $RX_SUBS
            (?= encoding )  (?&name)
                            (?&equals)
                            (?&quote) ?
            (?<ENCODING>    (?&value)       )
        }sx;
        if (lc $encoding ne lc $+{ENCODING}) {
            say "[XML ENCODING $encoding => $+{ENCODING}]";
            $encoding = $+{ENCODING};
        }
    }

    while (/$Meta_Tag_Rx/gi) {
        my $meta = $+{META};

        next unless $meta =~ m{             $RX_SUBS
            (?= http-equiv )    (?&name)
                                (?&equals)
            (?= (?&quote)? content-type )
                                (?&value)
        }six;

        next unless $meta =~ m{             $RX_SUBS
            (?= content )       (?&name)
                                (?&equals)
            (?<CONTENT>         (?&value)    )
        }six;

        next unless $+{CONTENT} =~ m{       $RX_SUBS
            (?= charset )       (?&name)
                                (?&equals)
            (?<CHARSET>         (?&value)    )
        }six;

        if (lc $encoding ne lc $+{CHARSET}) {
            say "[HTTP-EQUIV ENCODING $encoding => $+{CHARSET}]";
            $encoding = $+{CHARSET};
        }
    }

    return decode($encoding, $_);
}
########################################################################
# Make sure to this function is called
# as soon as source unit has been compiled.
UNITCHECK { load_rxsubs() }

# useful regex subroutines for HTML parsing
sub load_rxsubs {

    our $RX_SUBS = qr{
      (?(DEFINE)

        (?<WS> \s *  )

        (?<any_nv_pair>     (?&name) (?&equals) (?&value)         )
        (?<name>            \b (?=  \pL ) [\w:\-] +  \b           )
        (?<equals>          (?&WS)  = (?&WS)    )
        (?<value>           (?&quoted_value) | (?&unquoted_value) )
        (?<unwhite_chunk>   (?: (?! > ) \S ) +                    )

        (?<unquoted_value>  [\w:\-] *                             )

        (?<any_quote>  ["']      )

        (?<quoted_value>
            (?<quote>   (?&any_quote)  )
            (?: (?! \k<quote> ) . ) *
            \k<quote>
        )

        (?<start_tag>       < (?&WS)      )
        (?<html_end_tag>      >           )
        (?<xhtml_end_tag>   / >           )
        (?<end_tag>
            (?&WS)
            (?: (?&html_end_tag)
              | (?&xhtml_end_tag) )
         )

        (?<tag>
            (?&start_tag)
            (?&name)
            (?:
                (?&WS)
                (?&any_nv_pair)
            ) *
            (?&end_tag)
        )

        (?<untag> </ (?&name) > )

        # starts like a tag, but has screwed up quotes inside it
        (?<nasty>
            (?&start_tag)
            (?&name)
            .*?
            (?&end_tag)
        )

        (?<nontag>    [^<] +            )

        (?<string> (?&quoted_value)     )
        (?<word>   (?&name)             )

        (?<doctype>
            <!DOCTYPE
                # please don't feed me nonHTML
                ### (?&WS) HTML
            [^>]* >
        )

        (?<cdata>   <!\[CDATA\[     .*?     \]\]    > )
        (?<script>  (?= <script ) (?&tag)   .*?     </script> )
        (?<style>   (?= <style  ) (?&tag)   .*?     </style> )
        (?<comment> <!--            .*?           --> )

        (?<xml>
            < \? xml
            (?:
                (?&WS)
                (?&any_nv_pair)
            ) *
            (?&WS)
            \? >
        )

        (?<xhook> < \? .*? \? > )

      )

    }six;

    our $Meta_Tag_Rx = qr{                          $RX_SUBS
        (?<META>
            (?&start_tag) meta \b
            (?:
                (?&WS) (?&any_nv_pair)
            ) +
            (?&end_tag)
        )
    }six;

}

# nobody *ever* remembers to do this!
END { close STDOUT }

Ответ 3

  • Вы можете написать роман, похожий на tchrist.
  • Вы можете использовать библиотеку DOM, загрузить HTML и использовать xpath и просто использовать //input[@type="hidden"]. Или, если вы не хотите использовать xpath, просто получите все входы и отфильтруйте, какие из них скрыты с помощью getAttribute.

Я предпочитаю # 2.

<?php

$d = new DOMDocument();
$d->loadHTML(
    '
    <p>fsdjl</p>
    <form><div>fdsjl</div></form>
    <input type="hidden" name="blah" value="hide yo kids">
    <input type="text" name="blah" value="hide yo kids">
    <input type="hidden" name="blah" value="hide yo wife">
');
$x = new DOMXpath($d);
$inputs = $x->evaluate('//input[@type="hidden"]');

foreach ( $inputs as $input ) {
    echo $input->getAttribute('value'), '<br>';
}

Результат:

hide yo kids<br>hide yo wife<br>

Ответ 4

В духе решения Лексера Тома Кристиансена, здесь ссылка на Роберта Камерона, казалось бы, забытую статью 1998 года, REX: XML Shallow Parsing с регулярными выражениями.

http://www.cs.sfu.ca/~cameron/REX.html

Абстрактный

Синтаксис XML достаточно прост, что можно разобрать XML-документ в список его разметки и текстовых элементов, используя одно регулярное выражение. Такой неглубокий анализ XML-документа может быть очень полезен для построения множества легких инструментов обработки XML. Однако сложные регулярные выражения могут быть сложными для создания и еще труднее читать. Используя форму грамотного программирования для регулярных выражений, в этой статье представлен набор XML-выражений сильного разбора, которые могут быть использованы в качестве основы для простого, правильного, эффективного, надежного и независимого от языка XML мелкого анализа. Также даются полные представления парсера менее 50 строк в Perl, JavaScript и Lex/Flex.

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

Я использую регулярное выражение REX для включения и выключения в течение 10 лет, чтобы решить проблему, о которой спрашивал первоначальный плакат (как мне сопоставить этот конкретный тег, но не какой-то другой очень похожий тег?). Я нашел регулярное выражение, которое он разработал, чтобы быть полностью надежным.

REX особенно полезен, когда вы фокусируетесь на лексических деталях документа - например, при преобразовании одного вида текстового документа (например, обычного текста, XML, SGML, HTML) в другой, где документ может не быть быть правильной, хорошо сформированной или даже анализируемой для большей части трансформации. Он позволяет вам нацеливать острова разметки в любом месте документа, не нарушая остальную часть документа.

Ответ 5

Пока я люблю содержание остальных ответов, они не отвечали на вопрос напрямую или правильно. Даже платиновый ответ был слишком сложным, а также менее эффективным. Поэтому я был вынужден сделать это.

Я использую правильную команду Regex. Но из-за стигмы (и производительности) я всегда утверждаю, что хорошо сформированный XML или HTML должен использовать XML Parser. И даже более высокая производительность - это синтаксический анализ строк, хотя между читабельностью есть строка, которая становится слишком из-за руки. Однако это не вопрос. Вопрос в том, как сопоставить тег ввода скрытого типа. Ответ:

<input[^>]*type="hidden"[^>]*>

В зависимости от вашего вкуса единственным параметром регулярного выражения, который вам нужно будет включить, является опция ignorecase.

Ответ 6

вы можете попробовать следующее:

<[A-Za-z ="/_0-9+]*>

и для более близкого результата вы можете попробовать следующее:

<[ ]*input[ ]+type="hidden"[ ]*name=[A-Za-z ="_0-9+]*[ ]*[/]*>

вы можете проверить свой шаблон регулярного выражения здесь http://regexpal.com/

эти паттенсы хороши для этого:

<input type="hidden" name="SaveRequired" value="False" /><input type="hidden" name="__VIEWSTATE1" value="1H4sIAAtzrkX7QfL5VEGj6nGi+nP" /><input type="hidden" name="__VIEWSTATE2" value="0351118MK" /><input type="hidden" name="__VIEWSTATE3" value="ZVVV91yjY" />

и для случайного порядка type, name и value u можете использовать это:

<[ ]*input[ ]*[A-Za-z ="_0-9+/]*>

или

<[ ]*input[ ]*[A-Za-z ="_0-9+/]*[ ]*[/]>

:

<input  name="SaveRequired" type="hidden" value="False" /><input type="hidden" name="__VIEWSTATE1" value="1H4sIAAtzrkX7QfL5VEGj6nGi+nP" /><input type="hidden" name="__VIEWSTATE2" value="0351118MK" /><input  name="__VIEWSTATE3" type="hidden" value="ZVVV91yjY" />

`

кстати, я думаю, что вы хотите что-то вроде этого:

<[ ]*input(([ ]*type="hidden"[ ]*name=[A-Za-z0-9_+"]*[ ]*value=[A-Za-z0-9_+"]*[ ]*)+)[ ]*/>|<[ ]*input(([ ]*type="hidden"[ ]*value=[A-Za-z0-9_+"]*[ ]*name=[A-Za-z0-9_+"]*[ ]*)+)[ ]*/>|<[ ]*input(([ ]*name=[A-Za-z0-9_+"]*[ ]*type="hidden"[ ]*value=[A-Za-z0-9_+"]*[ ]*)+)[ ]*/>|<[ ]*input(([ ]*value=[A-Za-z0-9_+"]*[ ]*type="hidden"[ ]*name=[A-Za-z0-9_+"]*[ ]*)+)[ ]*/>|<[ ]*input(([ ]*name=[A-Za-z0-9_+"]*[ ]*value=[A-Za-z0-9_+"]*[ ]*type="hidden"[ ]*)+)[ ]*/>|<[ ]*input(([ ]*value=[A-Za-z0-9_+"]*[ ]*name=[A-Za-z0-9_+"]*[ ]*type="hidden"[ ]*)+)[ ]*/>

его не очень хорошо, но он работает в любом случае.

проверить его в: http://regexpal.com/

Ответ 7

Я хотел бы использовать **DOMDocument** для извлечения html-кода.

$dom = new DOMDocument();
$dom ->loadHTML($input);
$x = new DOMXpath($dom );
$results = $x->evaluate('//input[@type="hidden"]');

foreach ( $results as $item) {
    print_r( $item->getAttribute('value') );
}

Кстати, вы можете протестировать его здесь - regex101.com. Он показывает результат в реальном времени. Некоторые правила о Regexp: http://www.eclipse.org/tptp/home/downloads/installguide/gla_42/ref/rregexp.html Reader.

Ответ 8

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

var regex = /(<input.*?type\s?=\s?["']hidden["'].*?>)/g;
html.match(regex);

указанное выше регулярное выражение находит <input, за которым следует любое количество символов, пока оно не получит type="hidden" или type = 'hidden', за которым следует любое количество символов, пока оно не получит >

/g скажите регулярное выражение, чтобы найти каждую подстроку, которая соответствует заданному шаблону.