正規表現パターンが文字列のどこにも一致しませんか?


181

次の<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属性が最後に来る場合、私のパターンではそれが最初にあるので、一致は失敗します。

質問:タグ
内の属性の位置に関係なく一致するようにパターンを変更するにはどうすればよい<input>ですか?

PS:ちなみに私はAdobe AirベースのRegExデスクトップツールを使用して正規表現をテストしています。


4
正規表現は、生成されたhtmlを制御できる場合は優れたソリューションです。これは、通常の議論ではなく、通常の議論です。私が制御している部分での私のプロジェクトの正規表現
Salman

回答:


108

ここでのすべての答えに反して、正規表現を実行しようとしていることは完全に有効なソリューションです。これは、バランスの取れたタグを一致させようとしていないためです。これは、正規表現では不可能です。ただし、一致するのは1つのタグ内のものだけであり、それは完全に規則的です。

しかし、ここに問題があります。1つの正規表現だけでそれを行うことはできません... <input>タグをキャプチャーするために1つの一致を実行してから、さらに処理を行う必要があります。これは、属性値に>文字が含まれていない場合にのみ機能するため、完全ではありませんが、正気な入力には十分です。

これが、私が何を意味するかを示すための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
}

ここでの基本的な原則は、1つの正規表現であまりやりすぎないことです。お気づきのとおり、正規表現はある程度の順序を強制します。したがって、代わりに行う必要があるのは、最初に抽出しようとしているもののCONTEXTを照合し、次に必要なデータでサブマッチングを行うことです。

編集:ただし、一般的には、HTMLパーサーを使用する方がおそらく簡単で優れているので、コードの再設計または目的の再検討を検討する必要があることに同意します。:-)しかし、私はこの回答を、HTMLのサブセットを解析することは不可能であるというつまらない反応へのカウンターとして投稿する必要がありました。仕様全体を考えると、HTMLとXMLはどちらも不規則ですが、タグの仕様はまあまあです、確かにPCREの力の範囲内です。


14
ここでのすべての答えに反するわけではありません。:)
tchrist 2010年

6
@tchrist:私が投稿したとき、あなたの答えはここにありませんでした。;-)
Platinum Azure

7
ええ、そうです。何らかの理由で、あなたのタイプよりもタイプするのに時間がかかりました。私のキーボードにはグリースが必要だと思います。:)
tchrist '20

6
これは無効なHTMLです-value = "&lt;本当にこれでよろしいですか?&gt;" 彼がかき集めている場所がこのようなことを回避する貧しい仕事をしているなら、彼はより洗練された解決策を必要とするでしょう-しかし、彼らがそれを正しく行うなら(そして彼がそれを制御できるなら、彼はそれが正しいことを確認するべきです)、彼は大丈夫です。
ロススナイダー、

14
主題で最高のSOの答えは(おそらく最高のSOの回答期間)に必須のリンク:stackoverflow.com/questions/1732348/...
ダニエル・リベイロ

682

そうそう、あなたは正規表現を使って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の質問について説明しましょう。

正規表現を使用して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> 
    }{$+{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'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プログラムはいくつもの有効な方法で見ることができます。その1つは、正規表現を使用してHTMLを実際に解析できることです。しかし、もう1つは、ほとんどの人が思っているよりもずっと難しいということです。これは非常に難しいので、私のプログラムはあなたがしてはいけないことの証拠であることを簡単に結論付けることができます。

私はそれに同意しません。確かに、私が私のプログラムで行うすべてのことは、いくつかの研究の後であなたにとって意味がない場合は、この種のタスクに正規表現を使用しようとするべきではありません。特定のHTMLの場合、正規表現は優れていますが、一般的なHTMLの場合、狂気と同じです。私はいつもパースクラスを使用しています。特にそれがHTMLの場合は、自分で生成していません。

小さな HTML解析問題に最適な正規表現、大きな問題には悲観的

私のプログラムが、一般的なHTMLの解析に正規表現を使用してはいけない理由の例として取り上げられたとしても、それは大丈夫です。私はそれをmeantにするつもりだったためです。読みにくく、構造化されておらず、維持できないパターンを書くという厄介で厄介な癖。

パターンは醜い必要はなく、難しいものである必要もありません。醜いパターンを作成する場合、それはそれらではなくあなたへの反映です。

驚異的に絶妙な正規表現言語

私の問題に対する私の解決策はPerlで書かれていることを指摘するように言われました。驚きましたか?気づかなかったの?この啓示は爆弾ですか?

他のすべてのツールやプログラミング言語が、正規表現に関しては、Perlと同じくらい便利で表現力があり、強力であるとは限りません。そこには大きなスペクトルがあり、他のものよりも適切なものもあります。一般に、正規表現をライブラリとしてではなくコア言語の一部として表現した言語の方が、操作が簡単です。Cを使用している場合はプログラムの構成が異なりますが、たとえばPCREで実行できない正規表現では何もしていません。

最終的に他の言語は、Perlが正規表現に関して今どこにあるかに追いつくでしょう。私がこれを言ったのは、Perlが始まった頃は、Perlの正規表現のようなものは誰もいなかったからです。あなたが好きなことを言ってください、しかしこれはPerlが明らかに勝った場所です:開発のさまざまな段階ではありますが、誰もがPerlの正規表現をコピーしました。Perlは、使用するツールや言語に関係なく、現代のパターンで今日依存するようになったほとんど(すべてではありませんが)のすべてを開拓しました。したがって、最終的には他の人追いつくでしょう

しかし、彼らは今と同じように、Perlが過去のある時点に追いつくだけです。すべてが進みます。正規表現では、Perlがリードする他に何もない場合、他の人が従います。Perlがいったいどこにいるのか、ついにPerlが今どこにいるのに追いつくのか?わかりませんが、私たちも引っ越してきます。おそらく、Perlのクラフトパターンのスタイルに近づくでしょう。

そのようなものが好きで、Perl₅で使用したい場合は、Damian Conwayの素晴らしい Regexp :: Grammarsモジュールに興味があるかもしれません。それは完全に素晴らしく、私のプログラムでここで行ったことは、空白やアルファベットの識別子なしで人々が詰め込むパターンを作るのと同じくらい原始的に見えます。見てみな!


シンプルなHTMLチャンカー

これが、この投稿の最初に目玉を示したパーサーの完全なソースです。

私はないあなたが厳密にテスト解析クラスの上にこれを使用する必要があることを示唆しています。しかし、私は正規表現だけのためにその誰もができ、解析HTMLを装っていない人々の疲れている、彼らはできません。あなたは明らかにそうすることができ、このプログラムはその主張の証拠です。

確かに、それは簡単ではありませんが、それ可能です!

そして、そうすることは時間のひどい無駄です。なぜなら、このタスクに使用する必要がある優れた解析クラスが存在するからです。任意の HTML を解析しようとする人々に対する正しい答え、それが不可能であるということではありません。それは簡単で不誠実な答えです。正直な正解は、ゼロから理解するのは面倒すぎるので、彼らはそれを試みるべきではないということです。彼らは、完璧に機能するホイールを作り直すために努力している背中を壊すべきではありません。

一方、予測可能なサブセットに含まれる HTML は、正規表現で解析するのが非常に簡単です。人々がそれらを使おうとするのも不思議ではありません。なぜなら、小さな問題、おもちゃの問題では、おそらくもっと簡単なことはないでしょう。そのため、特定のアプローチと一般的なタスクの2つのタスクを区別することが非常に重要です。これらは必ずしも同じアプローチを必要としないからです。

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 }

23
コメントからの2つのハイライト "特に、自分で生成していないHTMLの場合は、解析クラスを常に使用しています。" そして、「パターンは醜くなくても、難しい必要もありません。醜いパターンを作成する場合、それはあなたではなく、あなたに反映されます。」私はあなたが言ったことに完全に同意するので、問題を再評価しています。そのような詳細な回答に感謝します
Salman

168
知らない人のために、トムは "Programming Perl"(別名Camelブック)の共著者であり、Perlのトップの権威の1人であると私は言及したいと思いました。これが本当のトム・クリスチャンセンだと疑うなら、戻って投稿を読んでください。
Bill Ruppert、2011

20
要約すると、RegExの名前は間違っています。残念だと思いますが、変わらないでしょう。互換性のある「RegEx」エンジンは、非正規言語を拒否することはできません。したがって、Finte State Machineだけでは正しく実装できません。計算クラスに関する強力な概念は適用されません。RegExを使用しても、O(n)の実行時間は保証されません。RegExの利点は、簡潔な構文と文字認識の暗黙のドメインです。私にとって、これはゆっくり動く列車の難破船であり、目をそらすことは不可能ですが、恐ろしい結果をもたらします。
Steve Steiner、

27
@tchristさん、これはOPの元の質問には決して答えません。そして、ここで適切な用語を解析していますか?Afaics正規表現はトークン化/字句解析を行っていますが、最終的な解析は正規表現自体ではなく、Perlコードで行われます。
Qtax 2011

65
@tchristy非常に印象的です。あなたは明らかに非常に熟練した才能のあるPerlプログラマーであり、最新の正規表現について非常に精通しています。しかし、あなたが書いたものは実際には正規表現(モダン、正規、またはその他)ではなく、正規表現を多用するPerlプログラムであることを指摘しておきます。あなたの投稿は、正規表現がHTMLを正しく解析できるという主張を本当にサポートしていますか?それとも、PerlがHTMLを正しく解析できるという証拠に近いのでしょうか。いずれにせよ、素晴らしい仕事です!
Mike Clark

126
  1. あなたはtchristがしたような小説を書くことができます
  2. 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>

72
実際、それはちょっと私のポイントでした。それがどれほど難しいかを見せたかった。
tchrist '20 / 11/10

19
とても良いものがあります。解析クラスを使用する方がはるかに簡単であることを人々に示すことを本当に望んでいたので、ありがとう!私は、正規表現を使用して最初からやり直す必要がある極端な問題の実例を望んでいました。ほとんどの人が、独自のHTMLをロールするのではなく、汎用のHTMLでプレハブパーサーを使用すると結論づけることを期待しています。ただし、正規表現は、複雑さの99.98%を取り除くため、独自に作成した単純なHTMLには依然として優れています。
tchrist

5
これらの2つの非常に興味深いアプローチを読んだ後の良い点は、あるアプローチの速度/メモリ使用量/ CPUを別のアプローチと比較することです(つまり、正規表現ベースのVS解析クラス)。
the_yellow_logo 2014

1
@ Avt'Wええ、Regexesがたまたま高速になった場合は「小説」を書くべきではありませんが、実際に知っておくのは本当に興味深いでしょう。:)しかし、私の推測では、すでにあまりにも..パーサは少ないリソースを取ること、である
Dennis98

これが、XPathが最初に発明された理由です。
するThorbjörnRavnアンデルセン

21

トム・クリスチャンセンのレクサー・ソリューションの精神に則って、ロバート・キャメロンの一見忘れられていた1998年の記事、REX:正規表現によるXML浅い構文解析へのリンクがあります。

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

概要

XMLの構文は非常に単純なので、単一の正規表現を使用して、XMLドキュメントをそのマークアップとテキストアイテムのリストに解析できます。XMLドキュメントのこのような浅い解析は、さまざまな軽量XML処理ツールの構築に非常に役立ちます。ただし、複雑な正規表現は、作成するのが難しく、さらに読みにくい場合があります。このペーパーでは、正規表現に読み書き可能なプログラミングの形式を使用して、シンプルで正確、効率的で、堅牢で、言語に依存しないXML浅い解析の基礎として使用できる一連のXML浅い解析式について説明します。Perl、JavaScript、Lex / Flexでそれぞれ50行未満の完全な浅いパーサー実装も提供されます。

正規表現について読むのが好きなら、キャメロンの論文は魅力的です。彼の執筆は簡潔で、徹底的で、非常に詳細です。彼は単にREX正規表現を作成する方法を示すだけでなく、小さな部分から複雑な正規表現を構築する方法も示しています。

最初の投稿者が尋ねた種類の問題を解決するために、REX正規表現を10年間オンとオフで使用してきました(この特定のタグに一致するが、他の非常に類似したタグには一致しない方法は?)。彼が開発した正規表現は完全に信頼できるものであることがわかりました。

REXは、ドキュメントの語彙の詳細に焦点を当てている場合に特に役立ちます。たとえば、ある種類のテキストドキュメント(プレーンテキスト、XML、SGML、HTMLなど)を別のドキュメントに変換し、ドキュメントが有効でない場合があります。整形式、またはほとんどの変換で解析可能です。これにより、ドキュメントの他の部分に影響を与えることなく、ドキュメント内の任意の場所にあるマークアップのアイランドをターゲットにできます。


7

これらの残りの回答の内容は気に入っていますが、質問に直接または正しく回答していませんでした。プラチナの答えでさえ、非常に複雑で、効率も劣っていました。だから私はこれを置くことを余儀なくされました。

正しく使用すると、私はRegexの大きな支持者です。しかし、不名誉(およびパフォーマンス)のため、整形式のXMLまたはHTMLではXMLパーサーを使用する必要があると常に述べています。また、文字列解析の方がパフォーマンスが向上しますが、手に負えない場合は読みやすさの間に線があります。しかし、それは問題ではありません。問題は、隠しタイプの入力タグをどのように照合するかです。答えは:

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

フレーバーによっては、含める必要がある正規表現オプションはignorecaseオプションのみです。


5
<input type='hidden' name='Oh, <really>?' value='Try a real HTML parser instead.'>
Ilmari Karonen 2014年

4
あなたの例は自己完結型です。/>で終わる必要があります。また、>名前フィールドにaが含まれる可能性はほとんどありません>が、アクションハンドルにaがある可能性は確かにあります。EG:OnClickプロパティのインラインJavaScript呼び出し。そうは言っても、私はそれらのためのXMLパーサーを持っていますが、与えられたドキュメントがXMLパーサーで処理するには乱雑になっているもののためにRegexも持っていますが、Regexはできます。さらに、これは問題ではありませんでした。非表示の入力でこれらの状況に遭遇することは決してないでしょう。私の答えが最善です。 Ya, <really>!
Suamere 2014年

3
/>XML-ismです。XHTMLを除いて、HTMLのどのバージョンでも必要ありません(これは実際にはあまり牽引されておらず、HTML5に取って代わられました)。そして、実際には有効ではないHTMLが散らかっていることはたくさんありますが、(XML ではなく)優れたHTML パーサーはそのほとんどに対応できるはずです。そうでない場合は、おそらくブラウザもそうではありません。
Ilmari Karonen 14年

1
必要な解析または検索が、非表示の入力フィールドのコレクションを返す単一のヒットだけである場合、この正規表現は完璧です。.NET XMLドキュメントクラスを使用するか、サードパーティのXML / HTMLパーサーを参照して1つのメソッドを呼び出すだけでは、Regexが組み込まれているとやり過ぎになります。パーサーはそれを処理できませんでした。おそらく開発者が見ているものでさえありません。しかし、私の会社は月に何百万ものページを渡しており、多くの方法で連結およびジャッキングされているため、(常にではないが)Regexが最良のオプションです。
Suamere 2014年

1
ただ、この開発者がこの答えを求めている理由は会社全体にはわからないことです。しかし、それは彼が求めたものです。
Suamere 2014年

3

あなたはこれを試すことができます:

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

そして、のランダムな順序のためのtypenameおよびvalueuは、これを使用することができます。

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


1

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


0

あなたのhtmlコンテンツが文字列htmlに格納されていると仮定すると、非表示のタイプを含むすべての入力を取得するには、正規表現を使用できます

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

上記の正規表現は<input、それが取得されるまで任意の数の文字が続くか、type="hidden"またはtype = 'hidden'が取得されるまで任意の数の文字が続く>

/ gは、指定されたパターンに一致するすべての部分文字列を検索するように正規表現に指示します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.