怎么办 正则表达式模式在字符串中的任何地方都不匹配?

Posted

技术标签:

【中文标题】怎么办 正则表达式模式在字符串中的任何地方都不匹配?【英文标题】:What to do Regular expression pattern doesn't match anywhere in string? 【发布时间】:2011-05-12 23:54:16 【问题描述】:

我正在尝试使用此模式匹配 <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="" />

但我不确定typenamevalue 属性是否总是以相同的顺序出现。如果 type 属性位于最后,则匹配将失败,因为在我的模式中它位于开头。

问题: 无论&lt;input&gt; 标记中属性的位置如何,如何更改我的模式以使其匹配?

P.S.:顺便说一句,我正在使用基于 Adobe AirRegEx Desktop Tool 来测试正则表达式。

【问题讨论】:

regex 如果您可以控制生成的 html,那么它是一个很好的解决方案,因为它是常规的而不是常规的辩论,但我的情况是,我不知道 html 将来会如何变化,所以最好的办法是使用解析器而不是正则表达式,我在我的项目中使用了正则表达式,部分我可以控制 Stack Overflow 经典是 the question for which an answer,以 “您无法使用正则表达式解析 [X]HTML。” 开头。 【参考方案1】:

与这里的所有答案相反,因为您尝试做的正则表达式是一个完全有效的解决方案。这是因为您没有尝试匹配平衡的标签——这对于正则表达式是不可能的!但你只匹配一个标签中的内容,这是完全正常的。

这就是问题所在。你不能只用一个正则表达式来做到这一点......你需要做一个匹配来捕获&lt;input&gt; 标签,然后对其进行进一步处理。请注意,这仅在所有属性值中都没有 &gt; 字符时才有效,因此它并不完美,但对于理智的输入来说应该足够了。

这里有一些 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,然后对您想要的数据进行子匹配。

编辑: 但是,我同意一般来说,使用 HTML 解析器可能更容易更好,您确实应该考虑重新设计代码或重新检查您的目标。 :-) 但是我不得不发布这个答案,以反驳无法解析 HTML 的任何子集的下意识反应:当您考虑整个规范时,HTML 和 XML 都是不规则的,但是标签的规范是相当规则的,当然在 PCRE 的能力范围内。

【讨论】:

与这里的所有答案并不矛盾。 :) @tchrist:当我发布我的答案时,你的答案不在这里。 ;-) 嗯嗯——由于某种原因,我打字的时间比你打字的时间长。我想我的键盘一定需要润滑。 :) 那是无效的 HTML - 它应该是 value="<你真的确定这个吗?>"如果他正在刮擦的地方在逃避这样的事情方面做得不好,那么他将需要一个更复杂的解决方案——但如果他们做得对(如果他能控制它,他应该确保它是正确的)那么他就没事了。 关于该主题的最佳 SO 答案的必填链接(可能是最佳 SO 答案期):***.com/questions/1732348/…【参考方案2】:

哦,是的,你可以使用正则表达式解析 HTML!

对于您正在尝试的任务,正则表达式非常好!

确实大多数人都低估了使用正则表达式解析 HTML 的难度,因此做得很差。

但这并不是与计算理论相关的基本缺陷。 That silliness is parroted a lot around here,但你不相信他们。

因此,虽然它肯定是可以做到的(这篇文章是这个无可争议的事实的存在证明),但这并不意味着它应该是。

您必须自己决定是否能够胜任使用正则表达式编写相当于专用、专用 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 的问题。

使用正则表达式解决 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 <tchrist@perl.com>
# 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> 
    $+BODYsix;
    return $_;
 

sub descape 
    my $string = $_[0];
    for my $_ ($string) 
        s
            (?<! % )
            % ( \pHex_Digit 2 )
        
            chr hex $1;
        gsex;
        s
            & \043 
            ( [0-9]+ )
            (?: ; 
              | (?= [^0-9] )
            )
        
            chr     $1;
        gsex;
        s
            & \043 x
            ( \pASCII_HexDigit + )
            (?: ; 
              | (?= \PASCII_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's 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。

正则表达式最适合 small HTML 解析问题,对大问题最不利

即使我的程序被用来说明为什么你应该使用正则表达式来解析一般的 HTML——这没关系,因为我的意思是这样 ☺——它仍然应该是一个让更多人大开眼界,让更多的人改掉编写不可读、非结构化和不可维护的模式的非常普遍、令人讨厌、令人讨厌的习惯。

模式不必难看,也不必难。如果你创造了丑陋的图案,那是你的反映,而不是它们。

非凡精致的正则表达式语言

有人要求我指出,我为您的问题提供的解决方案是用 Perl 编写的。你惊喜吗?你没注意到吗?这个启示是重磅炸弹吗?

确实,在正则表达式方面,并非所有其他工具和编程语言都像 Perl 一样方便、富有表现力和强大。那里有很大的范围,有些比其他更合适。一般来说,将正则表达式表达为核心语言的一部分而不是作为库的语言更容易使用。我没有对你在 PCRE 等中无法使用的正则表达式做任何事情,尽管如果你使用 C,你会以不同的方式构建程序。

最终,其他语言将在正则表达式方面赶上 Perl 现在的水平。我这样说是因为在 Perl 刚开始的时候,没有其他人有像 Perl 的正则表达式这样的东西。随便说什么,但这就是 Perl 明显胜出的地方:每个人都在复制 Perl 的正则表达式,尽管它们处于不同的开发阶段。 Perl 开创了当今现代模式中几乎(不是全部,但几乎)您所依赖的一切,无论您使用什么工具或语言。所以最终其他人赶上来。

但它们只会赶上 Perl 过去某个时候的水平,就像现在一样。一切都在进步。在正则表达式中,如果没有别的,Perl 领先,其他人紧随其后。一旦其他人最终赶上 Perl 现在的位置,Perl 会在哪里?我不知道,但我知道我们也会搬家。可能我们会更接近Perl₆’s style of crafting patterns。

如果你喜欢这种东西但又想在 Perl₅ 中使用它,你可能会对 Damian Conway’s wonderful Regexp::Grammars 模块感兴趣。这太棒了,让我在我的程序中所做的看起来和我的一样原始,让人们在没有空格或字母标识符的情况下挤在一起的模式。看看吧!


简单的 HTML 分块器

这是我在本文开头展示的核心部分的解析器的完整源代码。

建议您应该在经过严格测试的解析类上使用它。但是我厌倦了人们假装没有人可以用正则表达式解析 HTML,因为 他们 不能。你显然可以,这个程序就是这个断言的证明。

当然,这并不容易,但可能的!

尝试这样做是非常浪费时间的,因为存在您应该用于此任务的良好解析类。对于试图解析任意 HTML 的人的正确答案是不是,这是不可能的。这是一个轻率而虚伪的答案。正确而诚实的答案是他们不应该尝试,因为从头开始计算太麻烦了;他们不应该为了重新发明一个运转良好的***而拼命挣扎。

另一方面,在可预测子集内的 HTML 使用正则表达式非常容易解析。难怪人们会尝试使用它们,因为对于小问题,也许是玩具问题,没有什么比这更容易了。这就是区分这两个任务(具体任务还是通用任务)如此重要的原因,因为它们不一定需要相同的方法。

我希望将来在这里看到对 HTML 和正则表达式问题的更公平和诚实的处理。

这是我的 HTML 词法分析器。它不会尝试进行验证解析;它只是识别词汇元素。您可能会将其视为 HTML 分块器,而不是 HTML 解析器。它对损坏的 HTML 不是很宽容,尽管它在这个方向上做了一些很小的允许。

即使您自己从不解析完整的 HTML(为什么要解析?这是一个已解决的问题!),这个程序有很多很酷的正则表达式位,我相信很多人可以从中学到很多东西。享受吧!

#!/usr/bin/env perl
#
# chunk_HTML - a regex-based HTML chunker
#
# Tom Christiansen <tchrist@perl.com
#   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 

【讨论】:

您评论中的两个亮点“我一直在使用解析类,尤其是如果它是我自己没有生成的 HTML。” “图案不一定要丑,也不一定要硬。如果你创造出丑陋的图案,那是对你的反映,而不是对它们的反映。”我完全同意你所说的,所以我正在重新评估这个问题。非常感谢这么详细的回答 对于那些不知道的人,我想我会提到 Tom 是“Programming Perl”(又名 Camel 书)的合著者,也是 Perl 的***权威之一。如果您怀疑这是否是真正的汤姆·克里斯蒂安森,请返回阅读帖子。 总结一下:RegEx 的命名错误。我认为这是一种耻辱,但它不会改变。兼容的“RegEx”引擎不允许拒绝非常规语言。因此,仅使用有限状态机无法正确实现它们。围绕计算类的强大概念并不适用。使用 RegEx 并不能确保 O(n) 的执行时间。 RegEx 的优点是语法简洁和字符识别的隐含域。对我来说,这是一个缓慢移动的火车残骸,无法将视线移开,但可怕的后果正在展开。 @tchrist,这永远不会回答 OP 的原始问题。 parsing 是这里的正确术语吗? Afaics 正则表达式正在做标记/词法分析,但最终解析是用 Perl 代码完成的,而不是正则表达式本身。 @tchrist 非常令人印象深刻。您显然是一位技艺高超、才华横溢的 Perl 程序员,并且对现代正则表达式非常了解。不过,我要指出的是,您编写的并不是真正的正则表达式(现代、正则或其他),而是一个大量使用正则表达式的 Perl 程序。你的帖子真的支持正则表达式可以正确解析 HTML 的说法吗?或者它更像是 Perl 可以正确解析 HTML 的证据?不管怎样,干得好!【参考方案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>

【讨论】:

实际上,这有点像我的观点。我想展示它有多难。 那里的东西非常好。我真的希望人们能证明使用解析类是多么容易,所以谢谢!我只是想要一个工作示例,说明使用正则表达式从头开始必须经历的极端麻烦。我当然希望大多数人得出结论,在通用 HTML 上使用预制解析器,而不是自己滚动。不过,正则表达式仍然适用于他们自己制作的简单 HTML,因为这消除了 99.98% 的复杂性。 在阅读了这两种非常有趣的方法后,最好将一种方法的速度/内存使用率/CPU 与另一种方法(即基于正则表达式的 VS 解析类)进行比较。 @Avt'W 是的,不是说如果正则表达式碰巧更快,你应该去写一部“小说”,但事实上它真的很有趣。 :) 但我的猜测已经是,解析器占用的资源也更少.. 这就是 XPath 最初被发明的原因!【参考方案4】:

本着 Tom Christiansen 的词法分析器解决方案的精神,这里是 Robert Cameron 看似被遗忘的 1998 年文章的链接,REX: XML Shallow Parsing with Regular Expressions。

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

摘要

XML 的语法非常简单,可以使用单个正则表达式将 XML 文档解析为其标记和文本项的列表。 XML 文档的这种浅层解析对于构建各种轻量级 XML 处理工具非常有用。然而,复杂的正则表达式可能难以构建,甚至更难以阅读。使用正则表达式的文学编程形式,本文记录了一组 XML 浅层解析表达式,可用作简单、正确、高效、健壮和与语言无关的 XML 浅层解析的基础。还给出了用 Perl、javascript 和 Lex/Flex 编写的完整的浅解析器实现,每个不到 50 行。

如果您喜欢阅读有关正则表达式的文章,Cameron 的论文很吸引人。他的作品简洁、透彻、非常详细。他不仅向您展示了如何构建 REX 正则表达式,而且还向您展示了一种从较小的部分构建任何复杂正则表达式的方法。

10 年来我一直在使用 REX 正则表达式来解决最初发帖人提出的问题(我如何匹配这个特定的标签,而不是其他一些非常相似的标签?)。我发现他开发的正则表达式完全可靠。

当您专注于文档的词汇细节时,REX 特别有用——例如,当将一种文本文档(例如,纯文本、XML、SGML、HTML)转换为另一种文档时,该文档可能不会对于大多数转换来说是有效的、格式良好的,甚至是可解析的。它使您可以在文档中的任何位置定位标记岛,而不会干扰文档的其余部分。

【讨论】:

【参考方案5】:

你可以试试这个:

<[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" />

对于typenamevalue的随机顺序,你可以使用这个:

<[ ]*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/

【讨论】:

【参考方案6】:

虽然我喜欢这些答案的其余内容,但他们并没有真正直接或正确地回答问题。即使是白金的答案也过于复杂,而且效率也较低。所以我只好放这个了。

如果使用得当,我是 Regex 的忠实拥护者。但是由于污名(和性能),我总是声明格式良好的 XML 或 HTML 应该使用 XML Parser。甚至更好的性能将是字符串解析,尽管如果它变得过于失控,则可读性之间存在界限。然而,这不是问题。问题是如何匹配隐藏类型的输入标签。答案是:

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

根据您的喜好,您需要包含的唯一正则表达式选项是 ignorecase 选项。

【讨论】:

&lt;input type='hidden' name='Oh, &lt;really&gt;?' value='Try a real HTML parser instead.'&gt; 你的例子是自动关闭的。应该以 /> 结尾。此外,虽然在名称字段中出现&gt; 的可能性几乎为零,但在操作句柄中确实有可能出现&gt;。例如:对 OnClick 属性的内联 javascript 调用。话虽这么说,我有一个 XML 解析器,但也有一个正则表达式,用于那些我给出的文档太乱而 XML 解析器无法处理的地方,但正则表达式可以。此外,这不是问题所在。你永远不会遇到隐藏输入的这些情况,我的回答是最好的。 Ya, &lt;really&gt;!. /&gt; 是一种 XML 主义;它在任何版本的 HTML 中都不是必需的,除了 XHTML(它从未真正获得过太大的吸引力,几乎已被 HTML5 取代)。你是对的,那里有很多乱七八糟的非真正有效的 HTML,但是一个好的 HTML(not XML)解析器应该能够处理其中的大部分;如果他们不这样做,很可能浏览器也不会。 如果您需要的唯一解析或搜索是单击以返回隐藏输入字段的集合,那么此正则表达式将是完美的。当 Regex 内置时,使用 .NET XML 文档类或引用第三方 XML/HTML 解析器只是为了调用一种方法将是矫枉过正的。你是对的,一个网站如此混乱以至于一个好的 HTML解析器无法处理它可能甚至不是开发人员会看的东西。但是我的公司每个月要交付数百万页,这些页面以多种方式连接和提取,因此有时(并非总是)Regex 是最佳选择。 唯一的一点是,我们不确定这个开发人员想要这个答案的整个公司原因。但这是他要求的。【参考方案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。它实时显示结果。 关于正则表达式的一些规则: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);

上述正则表达式查找 &lt;input 后跟任意数量的字符,直到它得到 type="hidden" 或 type='hidden' 后跟任意数量的字符,直到它得到 &gt;

/g 告诉正则表达式查找与给定模式匹配的每个子字符串。

【讨论】:

以上是关于怎么办 正则表达式模式在字符串中的任何地方都不匹配?的主要内容,如果未能解决你的问题,请参考以下文章

正则表达式匹配特定长度的字符串,中间有空格(任何地方)

正则表达式,匹配给定字符集的powerset中的任何内容

正则表达式

正则表达式字符匹配

js正则匹配总结

《剑指offer》 面试题53 :正则表达式匹配 Java