รูปแบบนิพจน์ทั่วไปไม่ตรงกับที่ใดในสตริง


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

แต่ผมไม่แน่ใจว่าtype, nameและvalueคุณลักษณะที่มักจะปรากฏในลำดับเดียวกัน หากtypeแอตทริบิวต์มาล่าสุดการจับคู่จะล้มเหลวเพราะในรูปแบบของฉันมันเป็นจุดเริ่มต้น

คำถาม:
ฉันจะเปลี่ยนรูปแบบของฉันได้อย่างไรจึงจะจับคู่โดยไม่คำนึงถึงตำแหน่งของแอตทริบิวต์ใน<input>แท็ก

PS:โดยวิธีการที่ผมใช้Adobe AirตามRegEx เครื่องมือสก์ท็อปสำหรับการทดสอบการแสดงผลปกติ


4
regex เป็นทางออกที่ยอดเยี่ยมถ้าคุณมีการควบคุม html ที่สร้างขึ้นเพราะมันเป็น regual vs ไม่ใช่การอภิปรายปกติ แต่ฉันกรณีของฉันฉันไม่รู้ว่า html จะเปลี่ยนแปลงในอนาคตดังนั้นสิ่งที่ดีที่สุดคือการใช้ parser แทน regex ฉันได้ใช้ regex ในโครงการของฉันในส่วนที่ผมมีการควบคุมมากกว่า
ซาลมาน

คำตอบ:


108

ตรงกันข้ามกับคำตอบทั้งหมดที่นี่สำหรับสิ่งที่คุณพยายามทำ regex เป็นทางออกที่ถูกต้องสมบูรณ์ นี่เป็นเพราะคุณไม่ได้พยายามจับคู่แท็กที่มีความสมดุลซึ่งจะเป็นไปไม่ได้กับ regex! แต่คุณกำลังจับคู่สิ่งที่อยู่ในแท็กเดียวและนั่นเป็นเรื่องปกติอย่างสมบูรณ์

นี่คือปัญหาแม้ว่า คุณไม่สามารถทำได้ด้วย 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
}

หลักการพื้นฐานที่นี่คืออย่าพยายามทำมากเกินไปกับการแสดงออกปกติหนึ่ง ตามที่คุณสังเกตเห็นนิพจน์ทั่วไปบังคับให้มีคำสั่งจำนวนหนึ่ง ดังนั้นสิ่งที่คุณต้องทำคือการจับคู่กับบริบทแรกของสิ่งที่คุณพยายามที่จะแยกจากนั้นทำการส่งข้อมูลที่คุณต้องการ

แก้ไข:อย่างไรก็ตามฉันจะยอมรับว่าโดยทั่วไปการใช้ตัวแยกวิเคราะห์ HTML อาจจะง่ายและดีขึ้นและคุณควรพิจารณาออกแบบรหัสของคุณใหม่หรือตรวจสอบวัตถุประสงค์อีกครั้ง :-) แต่ฉันต้องโพสต์คำตอบนี้เป็นตัวนับปฏิกิริยาตอบโต้การกระตุกเข่าที่แยกส่วนย่อยของ HTML ใด ๆ ที่เป็นไปไม่ได้: HTML และ XML นั้นทั้งสองอย่างผิดปกติเมื่อคุณพิจารณาสเปคทั้งหมด แต่สเปคของแท็กนั้นค่อนข้างปกติ แน่นอนอยู่ในอำนาจของ PCRE


14
ไม่ขัดกับคำตอบทั้งหมดที่นี่ :)
tchrist

6
@tchrist: คำตอบของคุณไม่ได้อยู่ที่นี่เมื่อฉันโพสต์ของฉัน ;-)
Platinum Azure

7
ใช่แล้ว - ด้วยเหตุผลบางอย่างฉันใช้เวลาในการพิมพ์นานกว่าของคุณ ฉันคิดว่าแป้นพิมพ์ของฉันต้องใช้จาระบี :)
tchrist

6
HTML นั้นไม่ถูกต้อง - ควรเป็น value = "& lt; คุณแน่ใจเกี่ยวกับสิ่งนี้จริงหรือ & gt;" หากสถานที่ที่เขาขูดอยู่นั้นเป็นงานที่น่าสงสารหลบหนีออกมาเช่นนี้เขาจะต้องมีวิธีแก้ปัญหาที่ซับซ้อนกว่า - แต่ถ้าพวกเขาทำถูกต้อง (และถ้าเขาสามารถควบคุมมันได้เขาก็ควรแน่ใจว่ามันถูก)
Ross Snyder

14
หน้าที่เชื่อมโยงไปยังคำตอบ SO ที่ดีที่สุดในหัวข้อ (อาจเป็นคำตอบ SO ที่ดีที่สุด): stackoverflow.com/questions/1732348/…
Daniel Ribeiro

682

โอ้ใช่คุณสามารถใช้ Regexes เพื่อแยกวิเคราะห์ HTML!

สำหรับงานที่คุณพยายาม regexes ก็ดีมาก!

มันเป็นความจริงที่คนส่วนใหญ่ดูถูกดูแคลนความยากลำบากในการแยกวิเคราะห์ HTML ด้วยการแสดงออกปกติและทำไม่ดี

แต่นี่ไม่ใช่ข้อบกพร่องพื้นฐานที่เกี่ยวข้องกับทฤษฎีการคำนวณ ความโง่เขลานั้นมีอยู่มากมายที่นี่แต่คุณไม่เชื่อหรอก

ดังนั้นในขณะที่สามารถทำได้อย่างแน่นอน (การโพสต์นี้ทำหน้าที่เป็นหลักฐานการมีอยู่ของความจริงที่ไม่อาจเพิกถอนได้) แต่นั่นไม่ได้หมายความว่า  ควร  จะเป็น

คุณต้องตัดสินใจด้วยตัวคุณเองไม่ว่าคุณจะทำหน้าที่เขียนจำนวนเท่าใดให้กับโปรแกรมแยกวิเคราะห์ HTML เพื่อวัตถุประสงค์พิเศษโดยเฉพาะ คนส่วนใหญ่ไม่ได้

แต่ฉันเป็น ☻


โซลูชันการแยกวิเคราะห์ HTML ตาม Regex ทั่วไป

ก่อนอื่นฉันจะแสดงให้เห็นว่าการแยกHTML โดยพลการด้วย regexes นั้นง่ายเพียงใด โปรแกรมเต็มในตอนท้ายของการโพสต์นี้ แต่หัวใจของโปรแกรมแยกวิเคราะห์คือ:

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 และบอกตำแหน่งที่พบชิ้นส่วนนั้น คุณสามารถแก้ไขมันเพื่อทำสิ่งอื่นที่คุณต้องการได้อย่างง่ายดายด้วยชิ้นส่วนที่กำหนด

ฉันไม่มีกรณีทดสอบที่ล้มเหลว (ซ้าย :): ฉันได้รันโค้ดนี้สำเร็จแล้วกับไฟล์ HTML มากกว่า 100,000 ไฟล์ - ทุกๆอันที่ฉันสามารถทำได้อย่างรวดเร็วและง่ายดาย นอกเหนือจากนั้นฉันยังรันมันบนไฟล์ที่สร้างขึ้นเป็นพิเศษเพื่อแยก parsers ไร้เดียงสา

นี่ไม่ใช่ parser ไร้เดียงสา

โอ้ฉันแน่ใจว่ามันไม่สมบูรณ์แบบ แต่ฉันยังไม่สามารถทำลายมันได้ ฉันคิดว่าถึงแม้จะมีบางสิ่งบางอย่างการแก้ไขจะง่ายต่อการติดตั้งเนื่องจากโครงสร้างที่ชัดเจนของโปรแกรม แม้แต่โปรแกรม regex-heavy ก็ควรมีโครงสร้าง

ตอนนี้ไม่เป็นไรขอให้ฉันตอบคำถามของ OP

ตัวอย่างการแก้ไขงานของ OP โดยใช้ Regexes

html_input_rxโปรแกรมเล็ก ๆ ที่ฉันรวมไว้ด้านล่างสร้างผลลัพธ์ต่อไปนี้เพื่อให้คุณเห็นว่าการแยก HTML กับ regexes ทำงานได้ดีสำหรับสิ่งที่คุณต้องการทำ:

% 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: $!";
} 

ไปแล้ว! ไม่มีอะไรให้มัน! :)

มีเพียงคุณเท่านั้นที่ จะตัดสินว่าทักษะของคุณกับ regexes นั้นขึ้นอยู่กับภารกิจในการวิเคราะห์ ระดับความสามารถของทุกคนแตกต่างกันและงานใหม่ทุกอย่างจะแตกต่างกัน สำหรับงานที่คุณมีชุดอินพุตที่กำหนดชัดเจน regexes เป็นตัวเลือกที่ถูกต้องเนื่องจากเป็นเรื่องไม่สำคัญที่จะนำมารวมกันเมื่อคุณมีชุดย่อยของ HTML ที่ จำกัด ในการจัดการ แม้แต่ผู้เริ่มต้น regex ควรจัดการงานเหล่านั้นด้วย regexes สิ่งอื่นใดที่เกินความจริง

อย่างไรก็ตามเมื่อ HTML เริ่มจับได้น้อยลงเมื่อเริ่มแตกในแบบที่คุณไม่สามารถคาดเดาได้ แต่มันถูกกฎหมายอย่างสมบูรณ์แบบเมื่อคุณต้องจับคู่สิ่งต่าง ๆ ที่แตกต่างกันหรือมีการพึ่งพาที่ซับซ้อนมากขึ้นในที่สุดคุณจะไปถึงจุดที่ คุณต้องทำงานให้หนักขึ้นเพื่อแก้ไขปัญหาที่ใช้ regexes มากกว่าที่คุณจะต้องใช้คลาสการแยกวิเคราะห์ ตำแหน่งที่จุดคุ้มทุนตกลงมาอีกครั้งในระดับความสะดวกสบายของคุณเองด้วย regexes

แล้วฉันควรทำอย่างไรดี?

ฉันไม่ได้จะบอกสิ่งที่คุณต้องทำหรือสิ่งที่คุณไม่สามารถทำ ฉันคิดว่ามันผิด ฉันแค่ต้องการนำเสนอคุณด้วยความเป็นไปได้เปิดตาของคุณเล็กน้อย คุณได้รับเลือกสิ่งที่คุณต้องการทำและวิธีที่คุณต้องการ ไม่มีข้อ จำกัด - และไม่มีใครรู้สถานการณ์ของตัวเองเช่นเดียวกับตัวคุณเอง หากสิ่งที่ดูเหมือนว่ามันทำงานมากเกินไปก็อาจจะเป็น การเขียนโปรแกรมควรสนุกคุณรู้ไหม หากไม่ใช่คุณอาจทำผิด

สามารถดูhtml_input_rxโปรแกรมของฉันได้หลายวิธี หนึ่งในนั้นคือคุณสามารถแยก HTML ด้วยนิพจน์ปกติได้ แต่อีกอย่างคือมันหนักกว่าและยากกว่าที่ใคร ๆ คิดว่ามาก สิ่งนี้สามารถนำไปสู่ข้อสรุปได้อย่างง่ายดายว่าโปรแกรมของฉันเป็นเครื่องพิสูจน์ถึงสิ่งที่คุณไม่ควรทำเพราะมันยากเกินไป

ฉันจะไม่เห็นด้วยกับสิ่งนั้น แน่นอนถ้าทุกอย่างที่ฉันทำในโปรแกรมของฉันไม่สมเหตุสมผลกับคุณหลังจากการศึกษาบางอย่างแล้วคุณไม่ควรพยายามใช้ regexes สำหรับงานประเภทนี้ สำหรับ HTML ที่เฉพาะเจาะจง regexes นั้นยอดเยี่ยม แต่สำหรับ HTML ทั่วไปพวกเขาจะเท่ากับความบ้าคลั่ง ฉันใช้การแยกวิเคราะห์คลาสตลอดเวลาโดยเฉพาะอย่างยิ่งถ้าเป็น HTML ฉันไม่ได้สร้างตัวเองขึ้นมา

Regexes ที่ดีที่สุดสำหรับปัญหาการแยกวิเคราะห์ HTML ขนาดเล็กซึ่งเป็นส่วนที่เล็กที่สุด

แม้ว่าโปรแกรมของฉันจะได้รับการอธิบายว่าทำไมคุณไม่ควรใช้ regexes สำหรับการแยกวิเคราะห์ HTML ทั่วไป - ซึ่งก็โอเคเพราะฉันคิดว่ามันควรจะเป็นแบบนั้น☺ - มันควรจะเป็นที่เปิดหูเปิดตา และน่ารังเกียจนิสัยที่น่ารังเกียจในการเขียนรูปแบบที่อ่านไม่ได้ไม่มีโครงสร้างและไม่สามารถทำลายได้

รูปแบบไม่จำเป็นต้องน่าเกลียดและพวกเขาไม่จำเป็นต้องยาก หากคุณสร้างรูปแบบที่น่าเกลียดมันเป็นภาพสะท้อนของคุณไม่ใช่พวกมัน

ภาษา Regex ที่งดงามอย่างมหัศจรรย์

ฉันได้รับการขอให้ชี้ให้เห็นว่าการแก้ปัญหาที่เป็นมืออาชีพของฉันได้ถูกเขียนเป็นภาษา Perl คุณประหลาดใจไหม? คุณไม่สังเกตเห็นไหม? การเปิดเผยนี้เป็นกระสุนหรือไม่?

มันเป็นความจริงที่ไม่ใช่เครื่องมือและภาษาการเขียนโปรแกรมอื่น ๆ ทั้งหมดนั้นค่อนข้างสะดวกสบายมีความหมายและมีประสิทธิภาพเมื่อพูดถึง regexes อย่างที่ Perl เป็น มีคลื่นความถี่ขนาดใหญ่ออกมีบางคนมีความเหมาะสมกว่าคนอื่น ๆ โดยทั่วไปภาษาที่แสดง regexes เป็นส่วนหนึ่งของภาษาหลักแทนที่จะเป็นห้องสมุดจะทำงานได้ง่ายขึ้น ฉันไม่ได้ทำอะไรกับ regexes ที่คุณทำไม่ได้พูด PCRE แม้ว่าคุณจะจัดโครงสร้างของโปรแกรมแตกต่างกันถ้าคุณใช้ C

ในที่สุดภาษาอื่น ๆ ก็จะติดต่อกับ Perl ตอนนี้ในแง่ของ regexes ฉันพูดแบบนี้เพราะเมื่อก่อน Perl เริ่มไม่มีใครมีอะไรเหมือน regexes ของ Perl พูดอะไรก็ได้ที่คุณชอบ แต่นี่คือที่ Perl ชัดเจนชนะ: ทุกคนคัดลอก regexes ของ Perl แม้ว่าจะอยู่ในขั้นตอนต่าง ๆ ของการพัฒนา Perl เป็นผู้บุกเบิกเกือบทุกอย่างที่คุณต้องพึ่งพาในรูปแบบที่ทันสมัยทุกวันนี้ไม่ว่าคุณจะใช้เครื่องมือหรือภาษาใดก็ตาม ดังนั้นในที่สุดคนอื่น ๆจะตามมาทัน

แต่พวกเขาจะตามไปยังจุดที่ Perl เคยเป็นอดีตในอดีตเหมือนตอนนี้ ทุกอย่างก้าวหน้า ใน regexes ถ้าไม่มีอะไรอื่นที่ Perl นำไปสู่คนอื่น ๆ ทำตาม Perl จะที่ไหนเมื่อคนอื่น ๆ จับกันได้ว่า Perl อยู่ที่ไหนในที่สุด? ฉันไม่มีความคิด แต่ฉันรู้ว่าเราก็ต้องประทับใจเช่นกัน เราน่าจะใกล้ชิดกับรูปแบบการประดิษฐ์ของPerl₆มากกว่า

หากคุณชอบสิ่งนั้น แต่ต้องการใช้ในPerl₅คุณอาจสนใจโมดูลRegexp :: Grammars ที่ยอดเยี่ยมของ Damian Conway มันยอดเยี่ยมมากและทำให้สิ่งที่ฉันทำที่นี่ในโปรแกรมของฉันดูเป็นเรื่องดั้งเดิมเหมือนกับที่ฉันสร้างรูปแบบที่ผู้คนอัดแน่นด้วยกันโดยไม่ต้องเว้นวรรคหรือตัวอักษร ลองดูสิ!


Simple HTML Chunker

นี่คือแหล่งที่มาที่สมบูรณ์ของตัวแยกวิเคราะห์ที่ฉันแสดงจุดศูนย์กลางจากจุดเริ่มต้นของการโพสต์นี้

ฉันไม่แนะนำให้คุณใช้สิ่งนี้กับคลาสการแยกวิเคราะห์ที่เข้มงวด แต่ฉันรู้สึกเบื่อหน่ายกับคนที่แกล้งทำเป็นว่าไม่มีใครสามารถแยก HTML ด้วย regexes เพียงเพราะพวกเขาทำไม่ได้ คุณสามารถทำได้อย่างชัดเจนและโปรแกรมนี้เป็นหลักฐานยืนยันว่า

แน่นอนว่ามันไม่ได้เป็นเรื่องง่าย แต่ก็เป็นไปได้!

และการพยายามทำเช่นนั้นเป็นการเสียเวลาอย่างมากเนื่องจากมีคลาสการแยกวิเคราะห์ที่ดีซึ่งคุณควรใช้สำหรับงานนี้ คำตอบที่ถูกต้องสำหรับผู้ที่พยายามแยกHTML โดยพลการนั้นไม่ได้เป็นไปไม่ได้ นั่นคือคำตอบที่ง่ายและไม่ตรงไปตรงมา คำตอบที่ถูกต้องและตรงไปตรงมาก็คือพวกเขาไม่ควรลองเพราะมันเป็นเรื่องที่น่ากังวลเกินกว่าที่จะคิดออกมาจากศูนย์ พวกเขาไม่ควรทำลายหลังที่พยายามดิ้นรนล้อที่ทำงานได้อย่างสมบูรณ์แบบ

ในทางกลับกัน HTML ที่อยู่ในชุดย่อยที่คาดเดาได้นั้นง่ายต่อการแยกวิเคราะห์ด้วย regexes ไม่น่าแปลกใจที่ผู้คนพยายามใช้มันเพราะสำหรับปัญหาเล็ก ๆ ปัญหาของเล่นอาจจะไม่มีอะไรง่ายไปกว่านี้อีกแล้ว นั่นเป็นสาเหตุที่สำคัญมากที่ต้องแยกความแตกต่างของสองงาน - เฉพาะเจาะจง vs ทั่วไป - เพราะสิ่งเหล่านี้ไม่จำเป็นต้องมีแนวทางเดียวกัน

ฉันหวังว่าในอนาคตที่นี่เพื่อดูคำถามเกี่ยวกับ HTML และ regexes ที่ยุติธรรมและซื่อสัตย์ยิ่งขึ้น

นี่คือ HTML lexer ของฉัน มันไม่ได้พยายามทำการแยกวิเคราะห์ที่ถูกต้อง มันแค่ระบุองค์ประกอบศัพท์ คุณอาจคิดว่ามันเป็นHTML chunkerมากกว่า parser HTML มันไม่ได้มีการให้อภัย HTML ที่เสียหายมากนักแม้ว่าจะมีค่าใช้จ่ายเล็กน้อยในทิศทางนั้น

แม้ว่าคุณจะไม่แยกวิเคราะห์ HTML แบบเต็ม ๆ ด้วยตัวเอง (และทำไมต้องเป็นคุณเป็นปัญหาที่แก้ไขได้!) โปรแกรมนี้มีบิต regex ที่น่าสนใจมากมายที่ฉันเชื่อว่าผู้คนจำนวนมากสามารถเรียนรู้ได้มากมาย สนุก!

#!/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
สองไฮไลท์จากความคิดเห็นของคุณ "ฉันใช้การแยกวิเคราะห์คลาสตลอดเวลาโดยเฉพาะอย่างยิ่งถ้าเป็น HTML ที่ฉันไม่ได้สร้างขึ้นเอง" และ "รูปแบบไม่จำเป็นต้องน่าเกลียดและพวกเขาไม่จำเป็นต้องยากถ้าคุณสร้างรูปแบบที่น่าเกลียดมันเป็นภาพสะท้อนที่คุณไม่ใช่พวกเขา" ฉันเห็นด้วยอย่างยิ่งกับสิ่งที่คุณพูดดังนั้นฉันจึงประเมินปัญหา ขอบคุณมากสำหรับคำตอบอย่างละเอียด
Salman

168
สำหรับผู้ที่ไม่ทราบฉันคิดว่าฉันจะพูดถึงว่าทอมเป็นผู้เขียนร่วมของ "Programming Perl" (หรือที่รู้จักในหนังสืออูฐ) และเป็นหนึ่งในเจ้าหน้าที่ Perl ชั้นนำ หากคุณสงสัยว่านี่คือ Tom Christiansen ตัวจริงให้กลับไปอ่านโพสต์
Bill Ruppert

20
เพื่อสรุป: RegEx ของผิด ฉันคิดว่ามันเป็นความอัปยศ แต่ก็ไม่เปลี่ยนแปลง เอ็นจิน 'RegEx' ที่เข้ากันได้ไม่ได้รับอนุญาตให้ปฏิเสธภาษาที่ไม่ใช่ภาษาปกติ ดังนั้นจึงไม่สามารถใช้งานได้อย่างถูกต้องด้วย Finte State Machines เท่านั้น แนวคิดที่ทรงพลังรอบ ๆ คลาสการคำนวณไม่สามารถใช้ได้ การใช้ RegEx นั้นไม่รับรองเวลาดำเนินการ O (n) ข้อดีของ RegEx คือไวยากรณ์สั้นและโดเมนโดยนัยของการรู้จำอักขระ สำหรับฉันนี่เป็นซากรถไฟที่เคลื่อนไหวช้าไม่สามารถมองออกไปได้
Steve Steiner

27
@tchrist นี้ไม่เคยตอบคำถามเดิมของ OP และแยกคำที่เหมาะสมที่นี่หรือไม่ Afaics regex กำลังทำการวิเคราะห์ tokenizing / lexical แต่การแยกวิเคราะห์ขั้นสุดท้ายทำด้วยโค้ด Perl ไม่ใช่ regex
Qtax

65
@tchrist น่าประทับใจมาก เห็นได้ชัดว่าคุณเป็นโปรแกรมเมอร์ 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

19
สิ่งที่ดีมากที่นั่น ฉันหวังว่าผู้คนจะแสดงให้เห็นว่าการใช้คลาสการแยกวิเคราะห์ง่ายขึ้นเท่าใดขอบคุณมาก! ฉันแค่ต้องการตัวอย่างการทำงานของปัญหาร้ายแรงที่คุณต้องทำเพื่อเริ่มต้นจากการใช้ regexes ฉันหวังว่าคนส่วนใหญ่จะสรุปว่าใช้ตัวแยกวิเคราะห์สำเร็จรูปบน HTML ทั่วไปแทนที่จะใช้ตัวแยกวิเคราะห์ Regexes ยังคงยอดเยี่ยมสำหรับ HTML แบบง่ายที่พวกเขาสร้างขึ้นเองเพราะมันกำจัด 99.98% ของความซับซ้อน
tchrist

5
สิ่งที่จะดีหลังจากอ่าน 2 วิธีที่น่าสนใจมากนั้นคือการเปรียบเทียบการใช้ความเร็ว / หน่วยความจำ / CPU ของวิธีหนึ่งกับอีกวิธีหนึ่ง (เช่นคลาสการแยกวิเคราะห์ตาม VS ของ regex)
the_yellow_logo

1
@ Avt'W ใช่ไม่ใช่ว่าคุณควรจะเขียนนวนิยาย 'ถ้า Regexes เกิดขึ้นเร็วขึ้น แต่ในความเป็นจริงมันน่าสนใจจริงๆที่จะรู้ :) แต่ฉันเดาอยู่แล้วว่าตัวแยกวิเคราะห์ใช้ทรัพยากรน้อยลงมากเกินไป ..
Dennis98

นี่คือเหตุผลที่ XPath ถูกคิดค้นขึ้นตั้งแต่แรก!
Thorbjørn Ravn Andersen

21

ด้วยจิตวิญญาณของวิธีแก้ปัญหาเล็ก ๆ น้อย ๆ ของ Tom Christiansen นี่คือลิงค์ของบทความREX: REX: XML Shallow Parsing ที่มีการแสดงออกปกติ

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

บทคัดย่อ

ไวยากรณ์ของ XML นั้นง่ายพอที่จะแยกวิเคราะห์เอกสาร XML ลงในรายการมาร์กอัปและรายการข้อความโดยใช้นิพจน์ทั่วไปเดียว การแยกวิเคราะห์เอกสาร XML แบบนี้จะมีประโยชน์มากสำหรับการสร้างเครื่องมือการประมวลผล XML ที่มีน้ำหนักเบาที่หลากหลาย อย่างไรก็ตามนิพจน์ทั่วไปที่ซับซ้อนอาจสร้างยากและยากต่อการอ่าน การใช้รูปแบบของการเขียนโปรแกรมเชิงความรู้สำหรับนิพจน์ทั่วไปกระดาษนี้จะจัดทำชุดของนิพจน์ XML แบบแยกวิเคราะห์ตื้นที่สามารถใช้เป็นพื้นฐานสำหรับการแยกวิเคราะห์ XML แบบตื้นที่เรียบง่ายถูกต้องมีประสิทธิภาพและมีประสิทธิภาพและไม่ขึ้นกับภาษา การใช้งานตัวแยกวิเคราะห์แบบตื้นสมบูรณ์น้อยกว่า 50 บรรทัดใน Perl, JavaScript และ Lex / Flex

หากคุณสนุกกับการอ่านเกี่ยวกับการแสดงออกปกติกระดาษคาเมรอนเป็นที่น่าสนใจ งานเขียนของเขาสั้นกระชับละเอียดและละเอียดมาก เขาไม่เพียงแสดงให้คุณเห็นวิธีการสร้างการแสดงออกปกติ REX แต่ยังเป็นวิธีการสร้าง regex ที่ซับซ้อนจากส่วนเล็ก ๆ

ฉันได้ใช้การเปิดและปิดการแสดงผลปกติของ REX เป็นเวลา 10 ปีเพื่อแก้ไขปัญหาที่โปสเตอร์เริ่มต้นถาม (ฉันจะจับคู่แท็กนี้โดยเฉพาะได้อย่างไร ฉันพบว่า regex ที่เขาพัฒนาขึ้นมีความน่าเชื่อถืออย่างสมบูรณ์

REX มีประโยชน์อย่างยิ่งเมื่อคุณมุ่งเน้นไปที่รายละเอียดคำศัพท์ของเอกสาร - ตัวอย่างเช่นเมื่อแปลงเอกสารข้อความชนิดหนึ่ง (เช่นข้อความธรรมดา XML, SGML, HTML) ไปเป็นอีกเอกสารหนึ่งซึ่งเอกสารอาจไม่ถูกต้อง ก่อตัวได้ดีหรือแยกวิเคราะห์ได้สำหรับการเปลี่ยนแปลงส่วนใหญ่ มันช่วยให้คุณกำหนดเป้าหมายเกาะมาร์กอัปได้ทุกที่ภายในเอกสารโดยไม่รบกวนส่วนที่เหลือของเอกสาร


7

ในขณะที่ฉันรักเนื้อหาของคำตอบที่เหลือพวกเขาไม่ได้ตอบคำถามโดยตรงหรืออย่างถูกต้อง แม้แต่คำตอบของ Platinum ก็ซับซ้อนเกินไปและมีประสิทธิภาพน้อยกว่า ดังนั้นฉันจึงถูกบังคับให้ใส่สิ่งนี้

ฉันเป็นผู้สนับสนุนอย่างมากของ Regex เมื่อใช้อย่างถูกต้อง แต่เนื่องจากความอัปยศ (และประสิทธิภาพ) ฉันมักจะระบุว่า XML หรือ HTML ที่มีรูปแบบที่ดีควรใช้ตัวแยกวิเคราะห์ XML และประสิทธิภาพที่ดียิ่งขึ้นก็คือการแยกวิเคราะห์สตริงแม้ว่าจะมีเส้นแบ่งระหว่างความสามารถในการอ่านได้ถ้ามันออกนอกมือไปแล้ว อย่างไรก็ตามนั่นไม่ใช่คำถาม คำถามคือวิธีจับคู่แท็กอินพุตที่ซ่อนอยู่ คำตอบคือ:

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

ตัวเลือก regex เพียงอย่างเดียวที่คุณต้องมีคือตัวเลือกการเพิกเฉย


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

4
ตัวอย่างของคุณคือการปิดตัวเอง ควรลงท้ายด้วย /> นอกจากนี้แม้ว่าโอกาสที่จะมี>ชื่อในฟิลด์นั้นแทบจะไม่มีเลยก็ตาม แต่เป็นไปได้ที่จะมี>ตัวจัดการแอ็คชัน EG: การเรียกจาวาสคริปต์แบบอินไลน์ในคุณสมบัติ OnClick ที่ถูกกล่าวว่าฉันมี XML parser สำหรับผู้ที่ยังมี Regex สำหรับผู้ที่เอกสารที่ฉันได้รับจะสับสนเกินไปสำหรับ XML parsers เพื่อจัดการ แต่ Regex สามารถ นอกจากนี้นี่ไม่ใช่สิ่งที่เป็นคำถาม คุณจะไม่พบกับสถานการณ์เหล่านี้ด้วยการป้อนข้อมูลที่ซ่อนอยู่และคำตอบของฉันดีที่สุด Ya, <really>!.
Suamere

3
/>เป็น XML-ism; ไม่จำเป็นต้องใช้ใน HTML เวอร์ชันใด ๆ ยกเว้น XHTML (ซึ่งไม่เคยได้รับแรงฉุดมากนักและได้รับทั้งหมดยกเว้น HTML5) และคุณพูดถูกว่ามี HTML ที่ยุ่งเหยิงไม่ถูกต้อง แต่ HTML parser ที่ดี ( ไม่ใช่ XML) น่าจะจัดการกับมันได้ หากไม่เป็นไปได้ว่าเบราว์เซอร์ส่วนใหญ่จะไม่
Ilmari Karonen

1
หากการแยกวิเคราะห์หรือการค้นหาเพียงอย่างเดียวที่คุณต้องการคือการเข้าชมครั้งเดียวเพื่อส่งคืนคอลเล็กชันของฟิลด์อินพุตที่ซ่อนอยู่ regex นี้จะสมบูรณ์แบบ การใช้. NET XML Document class (es) หรือการอ้างอิงตัวแยกวิเคราะห์ XML / HTML ของบุคคลที่สามเพื่อเรียกวิธีการหนึ่งจะ overkill เมื่อ Regex ถูกสร้างขึ้นและคุณพูดถูกว่าเว็บไซต์ยุ่งกับ HTML ที่ดี ตัวแยกวิเคราะห์ไม่สามารถจัดการได้อาจไม่ใช่แม้กระทั่งบางสิ่งที่นักพัฒนาจะมอง แต่ บริษัท ของฉันถูกส่งออกเป็นล้าน ๆ หน้าต่อเดือนที่ต่อกันและต่อเข้ากับหลาย ๆ ทางซึ่งบางครั้ง (ไม่เสมอไป) Regex เป็นตัวเลือกที่ดีที่สุด
Suamere

1
มีเพียงจุดเดียวที่เราไม่แน่ใจว่าเหตุผลทั้งหมดที่ บริษัท ต้องการให้คำตอบนี้ แต่มันเป็นสิ่งที่เขาขอ
Suamere

3

คุณสามารถลองสิ่งนี้:

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

และเพื่อผลลัพธ์ที่ใกล้กว่าคุณสามารถลอง:

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

คุณสามารถทดสอบรูปแบบ regex ของคุณได้ที่นี่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และ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') );
}

BTW คุณสามารถทดสอบได้ที่นี่ - regex101.com มันแสดงให้เห็นผลในเวลาจริง กฎระเบียบบางอย่างเกี่ยวกับ Regexp: http://www.eclipse.org/tptp/home/downloads/installguide/gla_42/ref/rregexp.html อ่าน


0

สมมติว่าเนื้อหา html ของคุณถูกเก็บไว้ในสตริง html จากนั้นเพื่อรับอินพุตทุกประเภทที่มีประเภทซ่อนอยู่คุณสามารถใช้นิพจน์ทั่วไปได้

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

regex ข้างต้นค้นหา<inputตามด้วยจำนวนอักขระใด ๆ จนกว่าจะได้รับtype="hidden"หรือพิมพ์ = 'ซ่อน' ตามด้วยจำนวนอักขระใด ๆ จนกว่าจะได้รับ>

/ g บอกนิพจน์ทั่วไปเพื่อค้นหาทุกสตริงย่อยที่ตรงกับรูปแบบที่กำหนด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.