RegEx จับคู่แท็กเปิดยกเว้นแท็ก XHTML ที่มีในตัว


1473

ฉันต้องการจับคู่แท็กเปิดทั้งหมดเหล่านี้:

<p>
<a href="foo">

แต่ไม่ใช่สิ่งเหล่านี้:

<br />
<hr class="foo" />

ฉันมากับสิ่งนี้และต้องการให้แน่ใจว่าฉันทำถูกแล้ว a-zฉันเป็นเพียงการจับ

<([a-z]+) *[^/]*?>

ฉันเชื่อว่ามันพูดว่า:

  • ค้นหาน้อยกว่าแล้ว
  • ค้นหา (และจับ) az อย่างน้อยหนึ่งครั้ง
  • ค้นหาช่องว่างเป็นศูนย์หรือมากกว่านั้น
  • ค้นหาตัวละครใด ๆ ที่เป็นศูนย์หรือมากกว่าครั้งโลภยกเว้น/จากนั้น
  • ค้นหามากกว่า

ฉันมีสิทธินั้นไหม และที่สำคัญคุณคิดว่าอย่างไร

คำตอบ:


4417

คุณไม่สามารถแยกวิเคราะห์ [X] HTML ด้วย regex เนื่องจาก HTML ไม่สามารถแยกวิเคราะห์โดย regex Regex ไม่ใช่เครื่องมือที่สามารถใช้แยกวิเคราะห์ HTML ได้อย่างถูกต้อง ตามที่ฉันได้ตอบคำถาม HTML และ regex ที่นี่หลายครั้งก่อนการใช้ regex จะไม่อนุญาตให้คุณใช้ HTML นิพจน์ทั่วไปเป็นเครื่องมือที่ไม่ซับซ้อนพอที่จะเข้าใจโครงสร้างที่ใช้โดย HTML HTML ไม่ใช่ภาษาปกติและด้วยเหตุนี้จึงไม่สามารถแยกวิเคราะห์โดยการแสดงออกปกติ การสืบค้นของ Regex ไม่พร้อมที่จะแยก HTML ออกเป็นส่วน ๆ ที่มีความหมาย หลายต่อหลายครั้ง แต่ไม่ได้มาหาฉัน แม้แต่นิพจน์ปกติที่ได้รับการปรับปรุงให้ดีขึ้นซึ่ง Perl ใช้ไม่ได้ขึ้นอยู่กับภารกิจการแยกวิเคราะห์ HTML คุณจะไม่ทำให้ฉันแตก HTML เป็นภาษาที่มีความซับซ้อนเพียงพอที่ไม่สามารถแยกวิเคราะห์โดยนิพจน์ทั่วไป แม้แต่ Jon Skeet ก็ไม่สามารถแยก HTML ได้โดยใช้นิพจน์ทั่วไป ทุกครั้งที่คุณพยายามแยก HTML ด้วยนิพจน์ทั่วไปเด็กที่ไม่บริสุทธิ์จะร้องไห้ด้วยเลือดของหญิงพรหมจารีและแฮกเกอร์ชาวรัสเซียใช้เว็บแอพของคุณ การแยก HTML กับ regex เรียกวิญญาณที่ไม่บริสุทธิ์เข้ามาในอาณาจักรของสิ่งมีชีวิต HTML และ regex ทำงานร่วมกันเช่นความรักการแต่งงานและพิธีกรรมทางศาสนา <center> ไม่สามารถเก็บไว้สายเกินไป พลังของ regex และ HTML เข้าด้วยกันในพื้นที่ความคิดเดียวกันจะทำลายความคิดของคุณเช่นผงสำหรับอุดรูที่เป็นน้ำ หากคุณแยกวิเคราะห์ HTML ด้วย regex คุณได้ให้กับพวกเขาและวิธีการดูหมิ่นของพวกเขาซึ่งทำให้เราทุกคนต้องทำงานหนักอย่างไร้มนุษยธรรมสำหรับผู้ที่ไม่มีชื่อแสดงในเครื่องบินหลายภาษาพื้นฐานเขามา HTML-plus-regexp จะทำให้เหลวไหลของประโยคในขณะที่คุณสังเกตจิตใจของคุณเหี่ยวเฉาในการโจมตีของสยองขวัญมันสายเกินไปที่จะสายเราไม่สามารถบันทึกเกินไป trangession ของเด็กเพื่อให้แน่ใจ regex จะใช้เนื้อเยื่อที่อยู่อาศัยทั้งหมด (ยกเว้น HTML ซึ่งมันไม่สามารถเป็นพยากรณ์ก่อนหน้านี้) เจ้านายของความช่วยเหลือที่รักให้เรารู้ว่าทุกคนสามารถอยู่รอดระบาดนี้ใช้ regex ที่จะแยก HTML ได้อีกต่อมนุษยชาตินิรันดร์ของความกลัวการทรมานและการรักษาความปลอดภัยหลุมโดยใช้ Rege x เป็นเครื่องมือในการประมวลผลแบบ HTML กำหนด Brea CH ระหว่างโลกนี้และดินแดนของความหวาดกลัวของหน่วยงานเสียหาย (เช่นหน่วยงาน SGML แต่เสียหายเพิ่มเติม) เพียง glimp SE ของ โลกของ reg parsers อดีตเพื่อใช้ HTML จะอิน tantly ขนส่ง AP สติ rogrammer ของฉัน n ใน AW Orl d จากหยุดหย่อนกรีดร้องเขามาที่ภัย SL ithy regex ติดเชื้อ Wil ลิตรกิน HT ของคุณ parser ML แอพลิเคชันและการดำรงอยู่ตลอดเวลาเช่น Visual Basic เท่านั้นที่เลวร้ายยิ่งเขามาเขาคอม ES ไม่ Fi GHT เอชอีมา HI s Unholy Radiance เดstro҉ying ตรัสรู้ทุกแท็ก HTML รั่วfr̶ǫm Yo ur ตาเหมือน liq uid พีเอไอเอ็นเพลงปกติประสบการณ์เรื่องการแยก ssionจะ exti nguish เสียงของหมอคนตาลจากเอสพีที่นี่ฉันสามารถดูได้ที่คุณสามารถดู มันเป็นเสื้อที่สวยงามเขาฉinal snuffing o ฉโกหกของผู้ชายทั้งหมดจะหายไป LL ฉัน SLOST TH E ม้าเขามา s เขา com ES เขาร่วมฉัน s T เขา ichหรือ permeat ES อัลลิตร MY FAC E MY FACE ᵒhพระเจ้า n o NO Noo O บน Θหยุดทีเขานี้ * ̶͑̾̾ GL ES ͎a̧͈͖r̽̾̈́͒͑en OT จริงZA̡͊͠͝LGΌISͮ҉̯͈͕̹̘ T O͇̹̺Ɲ̴ȳ̳ TH e PO NY H̸̡̪̯ͨ͊̽̅̾Ȩ̶̧̨̬̩̹̭̯̾͛ͪ̈ͧ̾ͬ͘C̷̙̝͖ͭ̏ͥͮ͟Oͮ͏̮̪̝͍M̖͊̒ͪͩͬ̚̚͜Ȇ̴̟̟͙̞ͩ͌͝ S


คุณลองใช้ตัวแยกวิเคราะห์ XML แทนหรือไม่


หมายเหตุของผู้ดำเนินรายการ

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


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

27
เป็นไปได้ไหมที่จะใช้ RegEx ในการแยกคำตอบนี้?
Chris Porter

2
หากคุณไม่เห็นโพสต์นี้นี่เป็นภาพหน้าจอที่น่ายินดีในทุกด้าน: imgur.com/gOPS2.png
Andrew Keeton

3248

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

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

Regexes ทำงานได้ดีสำหรับฉันและรวดเร็วมากในการตั้งค่า


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

255
@MichaelJohnston "หักง่าย" แทบจะไม่แน่นอน Regexes ใส่ใจกับรายละเอียดการจัดรูปแบบข้อความมากกว่าตัวแยกวิเคราะห์ XML สามารถละเว้นได้อย่างเงียบ ๆ สลับไปมาระหว่างการ&foo;เข้ารหัสและCDATAส่วน? ใช้ตัวขยาย HTML เพื่อลบช่องว่างทั้งหมดในเอกสารของคุณที่เบราว์เซอร์ไม่แสดงผลหรือไม่ ตัวแยกวิเคราะห์ XML จะไม่สนใจและจะไม่มีคำสั่ง XPath ที่เป็นลายลักษณ์อักษร regex ตาม "แยกวิเคราะห์" ในมืออื่น ๆ ...
ชาร์ลส์ดัฟฟี่

41
@CharlesDuffy สำหรับงานครั้งเดียวก็โอเคและสำหรับช่องว่างที่เราใช้ \ s +
quantum

68
@xiaomao แน่นอนหากต้องรู้ gotchas และวิธีแก้ปัญหาทั้งหมดเพื่อรับโซลูชัน 80% ที่ล้มเหลวในเวลาที่เหลือ "เหมาะกับคุณ" ฉันไม่สามารถหยุดคุณได้ ในขณะเดียวกันฉันก็อยู่ข้างรั้วโดยใช้ parsers ซึ่งทำงานกับ XML ที่ถูกต้องทางไวยากรณ์ 100%
Charles Duffy

374
ฉันต้องดึงข้อมูลออก ~ 10k pages ทั้งหมดด้วยเทมเพลต HTML เดียวกัน พวกเขาเกลื่อนไปด้วยข้อผิดพลาด HTML ที่ทำให้ parsers สำลักและสไตล์ของพวกเขาทั้งหมดเป็นแบบอินไลน์หรือ<font>อื่น ๆ : ไม่มีคลาสหรือ ID ที่จะช่วยนำทาง DOM หลังจากต่อสู้ทั้งวันด้วยวิธี "ถูกต้อง" ในที่สุดฉันก็เปลี่ยนมาใช้โซลูชัน regex และใช้งานได้ในหนึ่งชั่วโมง
Paul A Jungwirth

2037

ผมคิดว่าข้อบกพร่องที่นี่เป็นที่ HTML เป็นชัมประเภท 2 ไวยากรณ์ (บริบทฟรีไวยากรณ์)และ RegEx คือชัม 3 ประเภทไวยากรณ์ (ไวยากรณ์ปกติ) เนื่องจากไวยากรณ์ของ Type 2 มีความซับซ้อนมากกว่าไวยากรณ์ Type 3 (ดูลำดับชั้นของ Chomsky ) จึงเป็นไปไม่ได้ทางคณิตศาสตร์ที่จะแยกวิเคราะห์ XML ด้วย RegEx

แต่หลายคนจะพยายามบางคนอาจอ้างความสำเร็จ - แต่จนกว่าคนอื่นจะพบความผิดและทำให้คุณสับสนโดยสิ้นเชิง


225
OP ขอให้แยกเซตย่อยของ XHTML: แท็กเริ่มต้นที่ จำกัด สิ่งที่ทำให้ (X) HTML เป็น CFG นั้นมีศักยภาพที่จะมีองค์ประกอบระหว่างแท็กเริ่มต้นและแท็กสิ้นสุดขององค์ประกอบอื่น ๆ (เช่นในกฎไวยากรณ์A -> s A e) (X) HTML ไม่ได้มีคุณสมบัตินี้ภายในแท็กเริ่มต้น: แท็กเริ่มต้นไม่สามารถมีแท็กเริ่มต้นอื่น ๆ เซ็ตย่อยที่ OP พยายามแยกวิเคราะห์ไม่ใช่ CFG
LarsH

101
ในทางทฤษฎี CS ภาษาปกติเป็นส่วนย่อยที่เข้มงวดของภาษาบริบทฟรี แต่การใช้งานการแสดงผลปกติในการเขียนโปรแกรมภาษาหลักมีประสิทธิภาพมากขึ้น ดังที่noulakaz.net/weblog/2007/03/18/…อธิบายว่าสิ่งที่เรียกว่า "นิพจน์ทั่วไป" สามารถตรวจสอบตัวเลขที่สำคัญในเอกนารีซึ่งเป็นสิ่งที่การแสดงออกปกติจากทฤษฎี CS ไม่สามารถทำได้
Adam Mihalcin

11
@eyelidlessness: เหมือนกัน "เฉพาะถ้า" ใช้กับ CFG ทั้งหมดใช่ไหม? นั่นคือถ้าอินพุต (X) HTML ไม่ได้อยู่ในรูปแบบที่ดีนักแม้แต่ตัวแยกวิเคราะห์ XML แบบเต็มรูปแบบจะทำงานได้อย่างน่าเชื่อถือ บางทีถ้าคุณยกตัวอย่างข้อผิดพลาดทางไวยากรณ์ "(X) HTML ที่ใช้ในตัวแทนผู้ใช้โลกแห่งความจริง" ที่คุณอ้างถึงฉันจะเข้าใจสิ่งที่คุณได้รับดีกว่า
LarsH

82
@ AdamMihalcin ถูกต้องแน่นอน เอ็นจิ้นเรกซ์ที่ยังหลงเหลืออยู่ส่วนใหญ่มีประสิทธิภาพมากกว่าไวยากรณ์แบบ Chomsky Type 3 (เช่นการจับคู่ที่ไม่โลภ เอนจิ้นของ regex บางตัว (เช่น Perl's) นั้นกำลังทำให้ทัวริงสมบูรณ์ เป็นความจริงที่ว่าแม้จะเป็นเครื่องมือที่ไม่ดีสำหรับการแยกวิเคราะห์ HTML แต่อาร์กิวเมนต์ที่อ้างถึงนี้ไม่ใช่เหตุผลว่าทำไม
dubiousjim

27
นี่คือคำตอบ "เต็มและสั้นที่สุด" ที่นี่ มันนำพาผู้คนให้เรียนรู้พื้นฐานของไวยากรณ์และภาษาที่เป็นทางการและหวังว่าคณิตศาสตร์บางอย่างดังนั้นพวกเขาจะไม่เสียเวลากับสิ่งที่สิ้นหวังเช่นการแก้ปัญหางาน NP ในเวลาพหุนาม
mishmashru

1332

อย่าฟังคนพวกนี้ คุณสามารถแยกไวยากรณ์ที่ไม่มีบริบทด้วย regex โดยสิ้นเชิงหากคุณแบ่งงานเป็นชิ้นเล็ก ๆ คุณสามารถสร้างรูปแบบที่ถูกต้องด้วยสคริปต์ที่ทำสิ่งเหล่านี้ตามลำดับ:

  1. แก้ไขปัญหาการหยุดชะงัก
  2. สี่เหลี่ยมวงกลม
  3. แก้ไขปัญหาพนักงานขายเดินทางใน O (บันทึก n) หรือน้อยกว่า หากมีมากกว่านั้นคุณจะไม่มี RAM และเอ็นจิ้นจะหยุดทำงาน
  4. รูปแบบจะค่อนข้างใหญ่ดังนั้นตรวจสอบให้แน่ใจว่าคุณมีอัลกอริทึมที่บีบอัดข้อมูลแบบสุ่มโดยไม่สูญเสียข้อมูล
  5. เกือบแล้ว - แค่หารสิ่งทั้งหมดด้วยศูนย์ ง่าย peasy

ฉันยังไม่เสร็จส่วนสุดท้ายด้วยตนเอง แต่ฉันรู้ว่าฉันเข้าใกล้แล้ว มันทำให้การขว้างปาCthulhuRlyehWgahnaglFhtagnExceptions ด้วยเหตุผลบางอย่างดังนั้นฉันจะพอร์ตเพื่อ VB 6 On Error Resume Nextและการใช้งาน ฉันจะอัปเดตด้วยรหัสเมื่อฉันตรวจสอบประตูแปลก ๆ ที่เพิ่งเปิดในกำแพง อืมมม

ป.ล. ปิแอร์เดอแฟร์มาต์ยังคิดวิธีที่จะทำเช่นกัน แต่ส่วนต่างที่เขาเขียนนั้นไม่ใหญ่พอสำหรับรหัส


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

148
ปัญหามาร์จิ้นเล็ก ๆ ของแฟร์มาต์ได้รับการแก้ไขโดยซอฟท์แวร์แก้ไขข้อความที่ทันสมัย
kd4ttc

50
ปัญหามาร์จิ้นเล็ก ๆ ของแฟร์มาต์ได้รับการแก้ไขโดย Randall Munroe โดยการตั้งค่าฟอนต์ให้เป็นศูนย์: xkcd.com/1381
heltonbiker

29
FYI: ปัญหาของแฟร์มาต์ได้รับการแก้ไขจริงในปี 1995และใช้เวลาเพียงนักคณิตศาสตร์ 358 ปีในการทำเช่นนั้น
jmiserez

10
ฉันสามารถข้ามขั้นตอนหารหารด้วยศูนย์แบบเหนียว ๆ แทนโดยใช้เฟืองบราวเนียนที่ผลิตจากฟิวชั่นเย็น ... แม้ว่ามันจะใช้ได้เฉพาะเมื่อฉันลบค่าคงที่ทางดาราศาสตร์เท่านั้น
Tim Lehner

1072

คำเตือน : ใช้ parser ถ้าคุณมีตัวเลือก ที่กล่าวว่า ...

นี่คือ regex ที่ฉันใช้ (!) เพื่อจับคู่แท็ก HTML:

<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>

อาจไม่สมบูรณ์แบบ แต่ฉันรันโค้ดนี้ผ่านHTML จำนวนมาก โปรดทราบว่ามันยังจับสิ่งแปลก ๆ เช่น<a name="badgenerator"">ที่ปรากฏบนเว็บ

ฉันเดาว่าจะไม่ตรงกับแท็กที่มีอยู่ในตัวเองคุณอาจต้องการใช้ลักษณะลบของKobi :

<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+(?<!/\s*)>

หรือเพียงแค่รวมถ้าและถ้าไม่

ถึง downvoters:นี่คือรหัสที่ใช้งานได้จากผลิตภัณฑ์จริง ฉันสงสัยว่าใครก็ตามที่อ่านหน้านี้จะได้รับความประทับใจว่าเป็นที่ยอมรับได้ในสังคมในการใช้ regexes บน HTML

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


94
ฉันจะไปกับสิ่งที่ทำงานในสิ่งที่มีสติกว่าร้องไห้เกี่ยวกับการไม่เป็นสากลที่สมบูรณ์แบบ :-)
prajeesh kumar

55
มีคนใช้ CDATA ใน HTML หรือไม่
Danubian Sailor

16
ดังนั้นคุณไม่ได้แก้ปัญหาการแยกวิเคราะห์ด้วย regexp เท่านั้น แต่เป็นส่วนหนึ่งของตัวแยกวิเคราะห์ซึ่งอาจทำงานได้ PS: ผลิตภัณฑ์ทำงานไม่ได้หมายความว่ารหัสที่ดี ไม่มีความผิด แต่นี่คือวิธีการเขียนโปรแกรมอุตสาหกรรมทำงานและรับเงิน
mishmashru

32
เริ่มต้น regex ของคุณล้มเหลวในที่เป็นไปได้, HTML <!doctype html><title><</title>ที่ถูกต้องที่สั้นมาก: ง่าย'<!doctype html><title><</title>'.match(/<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>/g)ผลตอบแทนในขณะที่ควร["<!doctype html>", "<title>", "<</title>"] ["<title>", "</title>"]

2
หากเราแค่พยายามจับคู่และไม่ตรงกับตัวอย่างที่ให้ไว้ /<.( [^^r> เหมือนจริง ^^>>>>> / g ทำงาน :-) // javascript: '<p> <a href = "foo"> <br /> <ชม class = "foo" />'.match(/^^r> เหมือนกัน ^^>**)?>/g)
imma

506

มีคนที่จะบอกคุณว่าโลกกลม (หรือบางทีโลกอาจเป็นทรงกลม oblate หากพวกเขาต้องการใช้คำแปลก ๆ ) พวกเขากำลังโกหก

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

คุณสามารถอยู่ในความเป็นจริงของพวกเขาหรือใช้เม็ดสีแดง

เช่นเดียวกับท่านนายพลจอมพล (เขาเป็นญาติของชั้นจอมพล. NET หรือไม่?) ฉันเคยเห็นเร็กคอร์ดตามแนว Regex-Verse ของUnderverse Stack แล้วกลับมาพร้อมกับพลังความรู้ที่คุณไม่สามารถจินตนาการได้ ใช่ฉันคิดว่ามี Old One หรือ Two ปกป้องพวกเขา แต่พวกเขากำลังดูฟุตบอลบนทีวีดังนั้นจึงไม่ยาก

ฉันคิดว่ากรณี XML นั้นค่อนข้างง่าย RegEx (ในไวยากรณ์. NET), แฟบและโค้ดใน base64 เพื่อให้ง่ายต่อการเข้าใจโดยใจที่อ่อนแอของคุณควรเป็นดังนี้:

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
+r/vD34mUADO1P4/AQAA//8=

RegexOptions.ExplicitCaptureตัวเลือกที่จะเป็นชุด ELEMENTNAMEกลุ่มการจับภาพที่คุณกำลังมองหาอยู่ หากกลุ่มการดักจับERRORไม่ว่างเปล่าแสดงว่ามีข้อผิดพลาดในการแยกวิเคราะห์และ Regex หยุดทำงาน

หากคุณมีปัญหาในการแปลงกลับเป็น regex ที่มนุษย์สามารถอ่านได้สิ่งนี้จะช่วยได้:

static string FromBase64(string str)
{
    byte[] byteArray = Convert.FromBase64String(str);

    using (var msIn = new MemoryStream(byteArray))
    using (var msOut = new MemoryStream()) {
        using (var ds = new DeflateStream(msIn, CompressionMode.Decompress)) {
            ds.CopyTo(msOut);
        }

        return Encoding.UTF8.GetString(msOut.ToArray());
    }
}

หากคุณไม่แน่ใจไม่ฉันไม่ได้ล้อเล่น (แต่บางทีฉันโกหก) มันจะทำงาน. ฉันได้สร้างตันของการทดสอบหน่วยทดสอบและฉันได้ใช้งานได้ (ส่วนหนึ่งของ) สำหรับการทดสอบความสอดคล้อง มันเป็น tokenizer ไม่ใช่ตัวแยกวิเคราะห์เต็มรูปแบบดังนั้นมันจะแบ่ง XML เป็นโทเค็นส่วนประกอบเท่านั้น มันจะไม่แยก / รวม DTD

โอ้ ... ถ้าคุณต้องการซอร์สโค้ดของ regex ด้วยวิธีการเสริม:

regex เพื่อทำเครื่องหมาย xmlหรือregex แบบเต็ม


68
ท่านผู้ยิ่งใหญ่มันใหญ่มาก คำถามที่ใหญ่ที่สุดของฉันคือทำไม คุณรู้ไหมว่าภาษาสมัยใหม่ทั้งหมดมีตัวแยกวิเคราะห์ XML ใช่ไหม คุณสามารถทำทุกอย่างในแบบ 3 บรรทัดและตรวจสอบว่ามันใช้ได้ นอกจากนี้คุณยังไม่ตระหนักว่า regex บริสุทธิ์สรรพสิ่งสามารถที่จะทำบางสิ่งบางอย่าง? หากคุณไม่ได้สร้างตัวแยกวิเคราะห์รหัสไฮบริด regex / คำสั่งที่จำเป็น คุณสามารถบีบอัดข้อมูลแบบสุ่มด้วยหรือไม่
Justin Morgan

113
@ จัสตินฉันไม่ต้องการเหตุผล มันสามารถทำได้ (และมันก็ไม่ผิดกฎหมาย / ผิดศีลธรรม) ดังนั้นฉันได้ทำมัน ไม่มีข้อ จำกัด ทางจิตใจยกเว้นสิ่งที่เรายอมรับ (นโปเลียนฮิลล์) ... ภาษาสมัยใหม่สามารถแยกวิเคราะห์ XML? จริงๆ? และฉันคิดว่านั่นผิดกฎหมาย! :-)
xanatos

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

31
@Justin ดังนั้น Xml Parser จึงไม่มีข้อจำกัดความหมายในขณะที่ Regex ไม่ใช่ เพราะถ้า Xml Parser ไม่ใช่ข้อผิดพลาดฟรีโดยนิยามอาจมี xml ที่ทำให้มันพังและเรากลับไปที่ขั้นตอนที่ 0 สมมติว่าสิ่งนี้: ทั้ง Xml Parser และ Regex นี้พยายามแยกวิเคราะห์ "legal "XML พวกเขาสามารถแยก XML "ผิดกฎหมาย" บางส่วนได้ ข้อบกพร่องอาจทำให้ทั้งคู่ล้มเหลว C # XmlReader นั้นได้รับการทดสอบมากกว่า Regex นี้แน่นอน
xanatos

31
ไม่ไม่มีข้อผิดพลาด: 1) โปรแกรมทั้งหมดมีอย่างน้อยหนึ่งข้อบกพร่อง 2) โปรแกรมทั้งหมดมีซอร์สโค้ดอย่างน้อยหนึ่งบรรทัด 3) โดย # 1 และ # 2 และใช้การเหนี่ยวนำแบบลอจิคัลมันเป็นเรื่องง่ายที่จะพิสูจน์ว่าโปรแกรมใด ๆ ที่สามารถลดลงในบรรทัดเดียวของรหัสที่มีข้อบกพร่อง (จากการเรียนรู้ Perl)
Scott Weaver

299

ในเชลล์คุณสามารถวิเคราะห์HTMLโดยใช้sed :

  1. Turing.sed
  2. เขียน HTML parser (ทำการบ้าน)
  3. ???
  4. กำไร!

ที่เกี่ยวข้อง (ทำไมคุณไม่ควรใช้การจับคู่ regex):


3
ฉันเกรงว่าคุณไม่ได้รับเรื่องตลก @kenorb โปรดอ่านคำถามและคำตอบที่ยอมรับอีกครั้ง นี่ไม่ใช่เครื่องมือแยกวิเคราะห์ HTML ทั่วไปหรือเกี่ยวกับเครื่องมือแยกวิเคราะห์ HTML แต่เป็นเรื่องการแยกวิเคราะห์ HTML ผ่าน regexes
Palec

1
ไม่ @Abdul มันเป็นไปไม่ได้อย่างสมบูรณ์พิสูจน์ได้ (ในแง่คณิตศาสตร์)
Palec

3
ใช่คำตอบนั้นสรุปได้ดี @Abdul โปรดสังเกตว่าอย่างไรก็ตามการใช้งาน regex ไม่ใช่นิพจน์ปกติจริง ๆในแง่คณิตศาสตร์ - พวกมันมีโครงสร้างที่ทำให้พวกเขาแข็งแกร่งขึ้นซึ่งมักจะทัวริงสมบูรณ์ (เทียบเท่ากับไวยากรณ์ประเภท 0) การโต้เถียงกับความจริงนี้ แต่ก็ยังค่อนข้างถูกต้องในแง่ที่ regexes ไม่เคยหมายถึงความสามารถในการทำงานเช่นนี้
Palec

2
และยังไงก็ตามเรื่องตลกที่ฉันพูดถึงก็คือเนื้อหาของคำตอบนี้ก่อนการแก้ไข (รุนแรง) ของ kenorb โดยเฉพาะการแก้ไข 4, @Abdul
Palec

3
สิ่งที่ตลกคือ OP ไม่เคยขอให้แยกวิเคราะห์ html โดยใช้ regex เขาขอให้จับคู่ข้อความ (ซึ่งเป็น HTML) โดยใช้ regex ซึ่งมีเหตุผลอย่างสมบูรณ์แบบ
Paralife

274

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

ไมโครซอฟท์จริงมีส่วนของการปฏิบัติที่ดีที่สุดสำหรับการแสดงผลปกติใน .NET Frameworkและโดยเฉพาะพูดคุยเกี่ยวกับการพิจารณา [วัน] การป้อนข้อมูลแหล่งที่มาของ

นิพจน์ทั่วไปมีข้อ จำกัด แต่คุณได้พิจารณาสิ่งต่อไปนี้หรือไม่

กรอบ NET เป็นที่ไม่ซ้ำกันเมื่อมันมาถึงการแสดงผลปกติในการที่จะสนับสนุนBalancing กลุ่มนิยาม

ด้วยเหตุผลนี้ฉันเชื่อว่าคุณสามารถแยกวิเคราะห์ XML โดยใช้นิพจน์ทั่วไป อย่างไรก็ตามโปรดทราบว่ามันจะต้องเป็น XML ที่ถูกต้อง ( เบราว์เซอร์นั้นมีการอภัย HTML มากและอนุญาตให้ใช้ไวยากรณ์ XML ที่ไม่ดีภายใน HTML ) สิ่งนี้เป็นไปได้เนื่องจาก "Balancing Group Definition" จะทำให้เอ็นจินนิพจน์ทั่วไปทำหน้าที่เป็น PDA

อ้างอิงจากบทความ 1 ที่อ้างถึงข้างต้น:

. NET Expression Engine

ดังที่อธิบายไว้ข้างต้นโครงสร้างที่สมดุลอย่างเหมาะสมไม่สามารถอธิบายได้ด้วยนิพจน์ทั่วไป อย่างไรก็ตามเอ็นจิ้นนิพจน์ทั่วไป. NET มีโครงสร้างจำนวนน้อยที่อนุญาตให้สร้างโครงสร้างที่สมดุลได้รับการยอมรับ

  • (?<group>) - พุชผลลัพธ์ที่ถูกดักจับสแต็กด้วยกลุ่มชื่อ
  • (?<-group>) - ป๊อปอัปดักจับส่วนใหญ่ที่มีกลุ่มชื่อปิดกองซ้อน
  • (?(group)yes|no) - จับคู่ส่วนที่ใช่ถ้ามีกลุ่มที่มีกลุ่มชื่ออยู่ไม่เช่นนั้นจะไม่มีส่วนใด ๆ

โครงสร้างเหล่านี้อนุญาตให้นิพจน์ปกติ. NET เลียนแบบ PDA ที่ถูก จำกัด โดยอนุญาตให้มีการดำเนินการสแต็กรุ่นง่าย ๆ : push, pop และ empty การดำเนินการอย่างง่ายนั้นมีค่าเทียบเท่ากับการเพิ่มการลดลงและการเปรียบเทียบกับศูนย์ตามลำดับ สิ่งนี้อนุญาตให้เอ็นจินนิพจน์ปกติ. NET จดจำชุดย่อยของภาษาที่ไม่มีบริบทโดยเฉพาะอย่างยิ่งภาษาที่ต้องการตัวนับอย่างง่าย สิ่งนี้จะช่วยให้นิพจน์ทั่วไป. NET ที่ไม่ใช่แบบดั้งเดิมรู้จักโครงสร้างที่สมดุลอย่างเหมาะสม

พิจารณาการแสดงออกปกติต่อไปนี้:

(?=<ul\s+id="matchMe"\s+type="square"\s*>)
(?>
   <!-- .*? -->                  |
   <[^>]*/>                      |
   (?<opentag><(?!/)[^>]*[^/]>)  |
   (?<-opentag></[^>]*[^/]>)     |
   [^<>]*
)*
(?(opentag)(?!))

ใช้ธง:

  • แถวเดียว
  • IgnorePatternWhitespace (ไม่จำเป็นถ้าคุณยุบ regex และลบ whitespace ทั้งหมด)
  • IgnoreCase (ไม่จำเป็น)

อธิบายนิพจน์ปกติ (แบบอินไลน์)

(?=<ul\s+id="matchMe"\s+type="square"\s*>) # match start with <ul id="matchMe"...
(?>                                        # atomic group / don't backtrack (faster)
   <!-- .*? -->                 |          # match xml / html comment
   <[^>]*/>                     |          # self closing tag
   (?<opentag><(?!/)[^>]*[^/]>) |          # push opening xml tag
   (?<-opentag></[^>]*[^/]>)    |          # pop closing xml tag
   [^<>]*                                  # something between tags
)*                                         # match as many xml tags as possible
(?(opentag)(?!))                           # ensure no 'opentag' groups are on stack

คุณสามารถลองนี้ที่ดีกว่า .NET นิพจน์ปกติ Tester

ฉันใช้ตัวอย่างแหล่งที่มาของ:

<html>
<body>
<div>
   <br />
   <ul id="matchMe" type="square">
      <li>stuff...</li>
      <li>more stuff</li>
      <li>
          <div>
               <span>still more</span>
               <ul>
                    <li>Another &gt;ul&lt;, oh my!</li>
                    <li>...</li>
               </ul>
          </div>
      </li>
   </ul>
</div>
</body>
</html>

พบการแข่งขันนี้:

   <ul id="matchMe" type="square">
      <li>stuff...</li>
      <li>more stuff</li>
      <li>
          <div>
               <span>still more</span>
               <ul>
                    <li>Another &gt;ul&lt;, oh my!</li>
                    <li>...</li>
               </ul>
          </div>
      </li>
   </ul>

แม้ว่ามันจะออกมาแบบนี้จริง ๆ :

<ul id="matchMe" type="square">           <li>stuff...</li>           <li>more stuff</li>           <li>               <div>                    <span>still more</span>                    <ul>                         <li>Another &gt;ul&lt;, oh my!</li>                         <li>...</li>                    </ul>               </div>           </li>        </ul>

สุดท้ายนี้ผมมีความสุขจริงๆบทความเจฟฟ์แอด: แยก Html ธู Way ตลกพอมันอ้างอิงคำตอบสำหรับคำถามนี้ที่ปัจจุบันมีมากกว่า 4k โหวต


18
System.Textไม่ได้เป็นส่วนหนึ่งของ C # มันเป็นส่วนหนึ่งของ. NET
John Saunders

8
ในบรรทัดแรกของ regex ของคุณ ( (?=<ul\s*id="matchMe"\s*type="square"\s*>) # match start with <ul id="matchMe"...) ในระหว่าง "<ul" และ "id" ควรจะเป็น\s+ไม่ได้\s*ถ้าคุณอยากให้มันตรงกับ <Ulid = ... ;)
C0deH4cker

@ C0deH4cker คุณถูกต้องแสดงออกควรมีแทน\s+ \s*
Sam

4
ไม่ใช่ว่าฉันเข้าใจจริงๆ แต่ฉันคิดว่า regex ของคุณล้มเหลวในการ<img src="images/pic.jpg" />
Scheintod

3
@Scheintod ขอบคุณสำหรับความคิดเห็น ฉันอัปเดตรหัสแล้ว การแสดงออกก่อนหน้านี้ล้มเหลวสำหรับแท็กปิดตัวเองที่มี/ภายในซึ่งล้มเหลวสำหรับ<img src="images/pic.jpg" />html ของคุณ
แซม

258

ฉันขอแนะนำให้ใช้QueryPathสำหรับการแยกวิเคราะห์ XML และ HTML ใน PHP มันเป็นไวยากรณ์เดียวกับ jQuery โดยทั่วไปมีเพียงฝั่งเซิร์ฟเวอร์


8
@ Kyle — jQuery ไม่แยกวิเคราะห์ XML มันใช้ parser ในตัว (ถ้ามี) ดังนั้นคุณไม่จำเป็นต้อง jQuery ที่จะทำมัน แต่เป็นเพียงสองบรรทัดธรรมดาเก่า JavaScript หากไม่มี parser ในตัว jQuery จะไม่ช่วย
RobG

1
@RobG จริงๆแล้ว jQuery ใช้ DOM ไม่ใช่ parser ในตัว
Qix - MONICA ถูกยกเลิกเมื่อ

11
@ Qix - คุณควรบอกผู้แต่งเอกสารให้ดีกว่า: " jQuery.parseXML ใช้ฟังก์ชั่นการแยกวิเคราะห์ดั้งเดิมของเบราว์เซอร์ ... " ที่มา: jQuery.parseXML ()
RobG

6
มาที่นี่จากคำถามมส์ ( meta.stackexchange.com/questions/19478/the-many-memes-of-meta/ ...... ) ฉันชอบคำตอบหนึ่งในนั้นคือ 'Use jQuery'
Jorn

221

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

regex ที่แนะนำนั้นผิด แต่:

<([a-z]+) *[^/]*?>

ถ้าคุณเพิ่มสิ่งที่ regex โดย backtracking ก็สามารถบังคับให้ตรงกับสิ่งที่โง่เหมือน<a >>, [^/]เป็นบุตรเกินไป ยังทราบว่า<space>*[^/]*ซ้ำซ้อนเพราะ[^/]*ยังสามารถจับคู่ช่องว่าง

ข้อเสนอแนะของฉันจะเป็น

<([a-z]+)[^>]*(?<!/)>

อยู่ที่ไหน(?<! ... )(ใน Perl regexes) ลบมองหลัง มันอ่าน "a <จากนั้นคำจากนั้นสิ่งใดก็ตามที่ไม่ใช่> ส่วนสุดท้ายซึ่งอาจไม่ใช่ / ตามด้วย>"

โปรดทราบว่าสิ่งนี้อนุญาตให้สิ่งต่าง ๆ เช่น <a/ > (เช่นเดียวกับ regex ดั้งเดิม) ดังนั้นหากคุณต้องการบางสิ่งที่ จำกัด มากขึ้นคุณต้องสร้าง regex เพื่อจับคู่แอตทริบิวต์ที่คั่นด้วยช่องว่าง


29
+1 สำหรับการสังเกตว่าคำถามไม่ได้เกี่ยวกับการแยกวิเคราะห์แบบเต็ม (X) HTML มันเกี่ยวกับการจับคู่ (X) HTML แท็กเปิด
LarsH

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

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

2
คำตอบที่ให้ไว้ที่นี่จะล้มเหลวเมื่อค่าแอตทริบิวต์มีอักขระ '>' หรือ '/'
Martin L

สิ่งนี้จะทำงานอย่างไม่ถูกต้องใน HTML ที่มีความคิดเห็นหรือส่วนของ CData มันจะทำงานไม่ถูกต้องหากแอตทริบิวต์ที่ยกมามี>อักขระ ฉันเห็นด้วยกับสิ่งที่ OP แนะนำสามารถทำได้ด้วย regex แต่สิ่งที่นำเสนอในที่นี้อยู่ไกลเกินความเรียบง่าย
JacquesB

183

ลอง:

<([^\s]+)(\s[^>]*?)?(?<!/)>

มันคล้ายกับคุณ แต่สุดท้ายจะต้องไม่ถูกหลังจากเฉือนและยังยอมรับ>h1


107
<a href="foo" title="5> 3 "> โอ๊ะโอ </a>
Gareth

21
นั่นเป็นเรื่องจริงมากและฉันก็คิดเกี่ยวกับมัน แต่ฉันคิดว่า>สัญลักษณ์นั้นหนีไปที่ & gt;
Kobi

65
>ถูกต้องในค่าแอตทริบิวต์ อันที่จริงใน 'บัญญัติ XML' &gt;อนุกรมคุณต้องไม่ใช้ (ซึ่งไม่เกี่ยวข้องทั้งหมดยกเว้นที่จะเน้นว่า>ในค่าแอตทริบิวต์ไม่ได้เลยเป็นสิ่งที่ผิดปกติ.)
bobince

5
@Kobi: เครื่องหมาย exlamation (เครื่องหมายที่คุณวางไว้ท้ายสุด) หมายถึงอะไรใน regexp?
Marco Demaio

6
@bince: คุณแน่ใจนะ? ฉันไม่เข้าใจอีกต่อไปดังนั้น HTML นี้ก็ใช้ได้เช่นกัน:<div title="this tag is a <div></div>">hello</div>
Marco Demaio

179

ซุนวูนักยุทธศาสตร์ชาวจีนนายพลและปราชญ์ชาวจีนกล่าวว่า

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

ในกรณีนี้ศัตรูของคุณคือ HTML และคุณเป็นตัวคุณเองหรือ regex คุณอาจจะเป็น Perl ด้วย regex ที่ผิดปกติ รู้ HTML รู้จักตัวเอง.

ฉันได้แต่งไฮกุที่อธิบายลักษณะของ HTML

HTML has
complexity exceeding
regular language.

ฉันยังได้แต่งไฮกุซึ่งอธิบายลักษณะของ regex ใน Perl

The regex you seek
is defined within the phrase
<([a-zA-Z]+)(?:[^>]*[^/]*)?>

153
<?php
$selfClosing = explode(',', 'area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed');

$html = '
<p><a href="#">foo</a></p>
<hr/>
<br/>
<div>name</div>';

$dom = new DOMDocument();
$dom->loadHTML($html);
$els = $dom->getElementsByTagName('*');
foreach ( $els as $el ) {
    $nodeName = strtolower($el->nodeName);
    if ( !in_array( $nodeName, $selfClosing ) ) {
        var_dump( $nodeName );
    }
}

เอาท์พุท:

string(4) "html"
string(4) "body"
string(1) "p"
string(1) "a"
string(3) "div"

โดยทั่วไปเพียงกำหนดชื่อโหนดองค์ประกอบที่ปิดตัวเองโหลดสตริง html ทั้งหมดลงในไลบรารี DOM คว้าองค์ประกอบทั้งหมดวนรอบและกรองออกจากรายการที่ไม่ได้ปิดตัวเองและดำเนินการกับพวกเขา

ฉันแน่ใจว่าคุณรู้แล้วว่าคุณไม่ควรใช้ regex เพื่อจุดประสงค์นี้


1
หากคุณกำลังจัดการกับ XHTML จริงให้ผนวก getElementsByTagName ด้วยNSและระบุเนมสเปซ
meder omuraliev

148

ฉันไม่ทราบถึงความต้องการที่แน่นอนของคุณ แต่ถ้าคุณใช้. NET ด้วยคุณไม่สามารถใช้Html Agility Pack ได้ไม่ได้เหรอ?

ข้อความที่ตัดตอนมา:

เป็นไลบรารีรหัส. NET ที่อนุญาตให้คุณแยกวิเคราะห์ไฟล์ "out of the web" HTML parser อดทนกับ HTML "โลกแห่งความจริง" ที่ผิดรูปแบบ


137

คุณต้องการคนแรกที่ไม่ได้นำหน้าด้วย> /ดูที่นี่สำหรับรายละเอียดเกี่ยวกับวิธีการทำ มันเรียกว่าการมองในแง่ลบ

อย่างไรก็ตามการใช้งานไร้เดียงสาของสิ่งนั้นจะจบลงด้วยการจับคู่<bar/></foo>ในเอกสารตัวอย่างนี้

<foo><bar/></foo>

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


1
ใช่ฉันแน่ใจ การกำหนดแท็กทั้งหมดที่เปิดอยู่ในปัจจุบันจากนั้นเปรียบเทียบกับแท็กที่ปิดในอาร์เรย์แยกต่างหาก RegEx ทำให้สมองของฉันเจ็บ
เจฟฟ์

122

W3C อธิบายการแยกวิเคราะห์ในรูปแบบหลอก regexp:
ลิงค์ W3C

ตามลิงค์ var สำหรับQName, SและAttributeเพื่อให้ได้ภาพที่ชัดเจน
ตามที่คุณสามารถสร้าง regexp ที่ดีในการจัดการสิ่งต่าง ๆ เช่นการลอกแท็ก


5
นั่นไม่ใช่แบบฟอร์ม regexp แบบ psuedo นั่นเป็นรูปแบบ EBNF ตามที่ระบุไว้ที่นี่: ข้อมูลจำเพาะ XML ภาคผนวก 6
Rob G

106

หากคุณต้องการสิ่งนี้สำหรับ PHP:

PHP DOM ฟังก์ชั่นจะไม่ทำงานอย่างถูกต้องจนกว่าจะมีการจัดรูปแบบ XML ไม่ว่าการใช้งานของพวกเขาจะดีขึ้นเพียงใดสำหรับส่วนที่เหลือของมนุษยชาติ

simplehtmldomเป็นสิ่งที่ดี แต่ฉันพบว่ามันค่อนข้างบั๊กและหน่วยความจำค่อนข้างหนัก [จะขัดข้องในหน้าขนาดใหญ่]

ฉันไม่เคยใช้querypathดังนั้นจึงไม่สามารถให้ความเห็นเกี่ยวกับประโยชน์ของมัน

อีกสิ่งที่น่าลองคือDOMParserของฉันซึ่งเบามากกับทรัพยากรและฉันใช้อย่างมีความสุขมาระยะหนึ่งแล้ว ง่ายต่อการเรียนรู้และมีประสิทธิภาพ

สำหรับ Python และ Java ลิงก์ที่คล้ายกันถูกโพสต์

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


95

นี่คือทางออก:

<?php
// here's the pattern:
$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*(\/>|>)/';

// a string to parse:
$string = 'Hello, try clicking <a href="#paragraph">here</a>
    <br/>and check out.<hr />
    <h2>title</h2>
    <a name ="paragraph" rel= "I\'m an anchor"></a>
    Fine, <span title=\'highlight the "punch"\'>thanks<span>.
    <div class = "clear"></div>
    <br>';

// let's get the occurrences:
preg_match_all($pattern, $string, $matches, PREG_PATTERN_ORDER);

// print the result:
print_r($matches[0]);
?>

เพื่อทดสอบอย่างลึกซึ้งฉันป้อนในแท็กปิดอัตโนมัติสตริงเช่น:

  1. <ชั่วโมง />
  2. <br/>
  3. ฟรี

ฉันป้อนแท็กด้วย:

  1. หนึ่งคุณลักษณะ
  2. แอตทริบิวต์มากกว่าหนึ่งรายการ
  3. แอ็ตทริบิวต์ที่ค่าถูกผูกไว้ในเครื่องหมายคำพูดเดี่ยวหรือเป็นเครื่องหมายคำพูดคู่
  4. คุณลักษณะที่มีเครื่องหมายคำพูดเดี่ยวเมื่อตัวคั่นเป็นเครื่องหมายคำพูดคู่และในทางกลับกัน
  5. คุณลักษณะ "unpretty" ที่มีช่องว่างหน้าสัญลักษณ์ "=" หลังจากนั้นและทั้งก่อนและหลัง

หากคุณพบบางสิ่งที่ไม่สามารถใช้งานได้ในการพิสูจน์แนวคิดด้านบนฉันพร้อมใช้งานในการวิเคราะห์รหัสเพื่อพัฒนาทักษะของฉัน

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

$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*>/';

@ridgerunner ผู้ใช้สังเกตเห็นว่ารูปแบบที่ไม่อนุญาตให้แอตทริบิวต์ unquotedหรือคุณลักษณะที่มีค่าไม่มี ในกรณีนี้การปรับจูนอย่างละเอียดทำให้เรามีรูปแบบดังต่อไปนี้:

$pattern = '/<(\w+)(\s+(\w+)(\s*\=\s*(\'|"|)(.*?)\\5\s*)?)*\s*>/';

</ แก้ไข>

ทำความเข้าใจกับรูปแบบ

หากมีใครสนใจที่จะเรียนรู้เพิ่มเติมเกี่ยวกับรูปแบบนี้ฉันจะให้บางบรรทัด:

  1. sub-expression แรก (\ w +) ตรงกับชื่อแท็ก
  2. การแสดงออกที่สองย่อยมีรูปแบบของแอตทริบิวต์ มันประกอบด้วย:
    1. ช่องว่างอย่างน้อยหนึ่งช่อง \ s +
    2. ชื่อของแอตทริบิวต์ (\ w +)
    3. ช่องว่างหรือมากกว่านั้น \ s * (เป็นไปได้หรือไม่ออกจากช่องว่างที่นี่)
    4. สัญลักษณ์ "="
    5. อีกครั้งให้เว้นวรรคเป็นศูนย์หรือมากกว่า
    6. ตัวคั่นของค่าคุณลักษณะ, อัญประกาศเดี่ยวหรือคู่ ('| ") ในรูปแบบ, อัญประกาศเดี่ยวจะหนีเพราะมันสอดคล้องกับตัวคั่นสตริง PHP นิพจน์ย่อยนี้ถูกจับกับวงเล็บเพื่อให้สามารถอ้างอิง อีกครั้งเพื่อวิเคราะห์การปิดของแอตทริบิวต์นั่นคือเหตุผลว่าทำไมมันจึงสำคัญมาก
    7. ค่าของแอตทริบิวต์ที่ตรงกับเกือบทุกอย่าง: (. *?); ในไวยากรณ์เฉพาะนี้โดยใช้โลภจับคู่ (เครื่องหมายคำถามหลังเครื่องหมายดอกจัน) เครื่องยนต์ RegExp เปิดใช้งานตัวดำเนินการเหมือน "มองไปข้างหน้า" ซึ่งจับคู่อะไรก็ได้ แต่สิ่งที่ตามหลังนิพจน์ย่อยนี้
    8. ความสนุกมาที่นี่: ส่วนที่ 4 เป็นตัวดำเนินการย้อนกลับซึ่งหมายถึงนิพจน์ย่อยที่กำหนดไว้ก่อนในรูปแบบในกรณีนี้ฉันหมายถึงนิพจน์ย่อยที่สี่ซึ่งเป็นตัวคั่นแอ็ตทริบิวต์แรกที่พบ
    9. ช่องว่างเป็นศูนย์หรือมากกว่านั้น \ s *
    10. การแสดงออกย่อยคุณลักษณะที่สิ้นสุดที่นี่กับสเปคของศูนย์หรือเกิดขึ้นเป็นไปได้ที่กำหนดโดยเครื่องหมายดอกจัน
  3. จากนั้นเนื่องจากแท็กอาจลงท้ายด้วยช่องว่างก่อนสัญลักษณ์ ">" ช่องว่างศูนย์หรือมากกว่านั้นจะถูกจับคู่กับรูปแบบย่อย \ s *
  4. แท็กที่ตรงกันอาจลงท้ายด้วยสัญลักษณ์ ">" แบบง่ายหรือการปิด XHTML ที่เป็นไปได้ซึ่งใช้เครื่องหมายทับก่อน: (/> |>) แน่นอนว่าสแลชนั้นหนีออกไปเนื่องจากมันจะเกิดขึ้นพร้อมกับตัวคั่นนิพจน์ปกติ

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


12
<option selected>ไม่ตรงกับแท็กที่ถูกต้องมีคุณลักษณะที่มีค่าไม่มีคือ ยังไม่ตรงกับแท็กที่ถูกต้องกับค่าแอตทริบิวต์ unquoted <p id=10>คือ
ridgerunner

1
@ridgerunner: ขอบคุณมากสำหรับความคิดเห็นของคุณ ในกรณีนั้นรูปแบบต้องเปลี่ยนเล็กน้อย: $ pattern = '/ <(\ w +) (\ s + (\ w +) (\ s * \ = \ s * (\' | ") (. *?) \\ 5 \ s *)?) * \ s *> / '; ฉันทดสอบและใช้งานได้ในกรณีที่ไม่มีคุณลักษณะหรือคุณลักษณะที่ไม่มีการเสนอราคา
Emanuele Del Grande

ช่องว่างต่อหน้าชื่อแท็ก: < a href="http://wtf.org" >ฉันค่อนข้างแน่ใจว่าถูกกฎหมาย แต่คุณไม่ตรงกัน
Floris

7
ไม่ต้องขออภัยช่องว่างก่อนที่ชื่อจะผิดกฎหมาย นอกเหนือจากการเป็น "ค่อนข้างแน่ใจ" ทำไมคุณไม่ให้หลักฐานการคัดค้านของคุณ? ต่อไปนี้คือของฉันw3.org/TR/xml11/#sec-starttags ที่อ้างถึง XML 1.1 และคุณสามารถค้นหาHTML4 , 5 และ XHTML เดียวกันได้เนื่องจากการตรวจสอบ W3C จะเตือนถ้าคุณทำการทดสอบ ในฐานะที่เป็นกวี blah-blah-กวีคนอื่น ๆ ที่นี่ฉันยังไม่ได้รับการโต้แย้งที่ชาญฉลาดใด ๆ นอกเหนือจากคำตอบของฉันหลายร้อยลบเพื่อแสดงให้เห็นว่ารหัสของฉันล้มเหลวตามกฎของสัญญาที่ระบุไว้ในคำถาม ฉันยินดีต้อนรับพวกเขาเท่านั้น
Emanuele Del Grande

@ridgerunner แน่นอนความคิดเห็นของคุณฉลาดและยินดีต้อนรับ
Emanuele Del Grande

91

เมื่อใดก็ตามที่ฉันต้องการแยกบางอย่างจากเอกสาร HTML อย่างรวดเร็วฉันใช้ Tidy เพื่อแปลงเป็น XML แล้วใช้ XPath หรือ XSLT เพื่อรับสิ่งที่ฉันต้องการ ในกรณีของคุณมีดังนี้:

//p/a[@href='foo']

89

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


84

ฉันชอบแยก HTML ด้วยนิพจน์ทั่วไป ฉันไม่พยายามแยกวิเคราะห์ HTML ที่งี่เง่าที่ใช้งานไม่ได้ รหัสนี้เป็นตัวแยกวิเคราะห์หลักของฉัน (รุ่น Perl):

$_ = join "",<STDIN>; tr/\n\r \t/ /s; s/</\n</g; s/>/>\n/g; s/\n ?\n/\n/g;
s/^ ?\n//s; s/ $//s; print

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

มันง่ายพอที่จะ rejig Perl Perl สคริปต์ slurp- ทุกอย่าง - แรกของฉันเป็นสิ่งที่ดีในการสตรีมหากคุณต้องการประมวลผลหน้าเว็บจำนวนมหาศาล แต่มันไม่จำเป็นจริงๆ

ฉันพนันได้เลยว่าฉันจะถูกลงคะแนนสำหรับเรื่องนี้

แยก HTML


กับความคาดหวังของฉันนี้มี upvotes ดังนั้นฉันจะแนะนำการแสดงออกปกติที่ดีกว่า:

/(<.*?>|[^<]+)\s*/g    # get tags and text
/(\w+)="(.*?)"/g       # get attibutes

มันดีสำหรับ XML / XHTML

ด้วยความหลากหลายเล็กน้อยมันสามารถรับมือกับ HTML ที่ยุ่งเหยิง ... หรือแปลง HTML -> XHTML ก่อน


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


35
"ฉันไม่พยายามแยก HTML ที่งี่เง่าที่เสียหายโดยเจตนา" รหัสของคุณทราบความแตกต่างได้อย่างไร
Kevin Panko

ไม่สำคัญว่า HTML จะใช้งานไม่ได้หรือไม่ สิ่งนี้จะยังคงแยก HTML เป็นแท็กและข้อความ สิ่งเดียวที่ทำให้เหม็นได้คือถ้าผู้ใช้ใส่อักขระ <หรือ> ที่ไม่ใช้อักขระในข้อความหรือคุณลักษณะ ในทางปฏิบัติตัวแยก HTML ขนาดเล็กของฉันทำงานได้ดี ฉันไม่ต้องการที่หนุนหลังอันมหึมาที่เต็มไปด้วยฮิวริสติก วิธีแก้ปัญหาง่าย ๆ ไม่ใช่สำหรับทุกคน ... !
Sam Watkins

ฉันเพิ่ม regexps ที่ง่ายขึ้นสำหรับการแยกแท็กข้อความและคุณลักษณะสำหรับ XML / XHTML
Sam Watkins

(รับข้อผิดพลาดคุณลักษณะ 1) /(\w+)="(.*?)"/ถือว่าคำพูดสองครั้ง มันจะพลาดค่าในคำพูดเดียว ใน html เวอร์ชัน 4 และค่าที่ไม่ได้กล่าวถึงก่อนหน้านั้นจะได้รับอนุญาตหากเป็นคำง่ายๆ
David Andersson

(ได้รับคุณลักษณะข้อผิดพลาด 2) ตู่อาจตรงกับข้อความที่มีลักษณะเช่นแอตทริบิวต์ภายในแอตทริบิวต์เช่น/(\w+)="(.*?)"/ <img title="Nope down='up' for aussies" src="..." />หากนำไปใช้ทั่วโลกมันจะจับคู่สิ่งต่าง ๆ ในข้อความธรรมดาหรือในความคิดเห็น html
David Andersson

74

นี่คือโปรแกรมแยกวิเคราะห์ PHPที่แยกวิเคราะห์ HTML โดยใช้ regex ที่ไม่ดี ในฐานะผู้เขียนโครงการนี้ฉันสามารถบอกได้ว่าเป็นไปได้ที่จะแยกวิเคราะห์ HTML ด้วย regex แต่ไม่มีประสิทธิภาพ หากคุณต้องการโซลูชันด้านเซิร์ฟเวอร์ (เช่นเดียวกับที่ฉันทำกับปลั๊กอิน WordPress wp-Typographyของฉัน) สิ่งนี้ใช้ได้ผล


1
htmlawedเป็นอีกหนึ่งโครงการ PHP ที่แยกวิเคราะห์ HTML เพื่อกรองแปลง ฯลฯ มีรหัสที่ดีถ้าคุณสามารถหามันได้!
user594694

ไม่คุณไม่สามารถแยกวิเคราะห์ HTML ด้วย regex แต่สำหรับบางเซ็ตย่อยอาจใช้งานได้
mirabilos

71

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

ตัวอย่างเช่น:

$store =~ s/http:/http:\/\//gi;
$store =~ s/https:/https:\/\//gi;
$baseurl = $store;

if (!$query->param("ascii")) {
    $html =~ s/\s\s+/\n/gi;
    $html =~ s/<pre(.*?)>(.*?)<\/pre>/\[code]$2\[\/code]/sgmi;
}

$html =~ s/\n//gi;
$html =~ s/\r\r//gi;
$html =~ s/$baseurl//gi;
$html =~ s/<h[1-7](.*?)>(.*?)<\/h[1-7]>/\n\[b]$2\[\/b]\n/sgmi;
$html =~ s/<p>/\n\n/gi;
$html =~ s/<br(.*?)>/\n/gi;
$html =~ s/<textarea(.*?)>(.*?)<\/textarea>/\[code]$2\[\/code]/sgmi;
$html =~ s/<b>(.*?)<\/b>/\[b]$1\[\/b]/gi;
$html =~ s/<i>(.*?)<\/i>/\[i]$1\[\/i]/gi;
$html =~ s/<u>(.*?)<\/u>/\[u]$1\[\/u]/gi;
$html =~ s/<em>(.*?)<\/em>/\[i]$1\[\/i]/gi;
$html =~ s/<strong>(.*?)<\/strong>/\[b]$1\[\/b]/gi;
$html =~ s/<cite>(.*?)<\/cite>/\[i]$1\[\/i]/gi;
$html =~ s/<font color="(.*?)">(.*?)<\/font>/\[color=$1]$2\[\/color]/sgmi;
$html =~ s/<font color=(.*?)>(.*?)<\/font>/\[color=$1]$2\[\/color]/sgmi;
$html =~ s/<link(.*?)>//gi;
$html =~ s/<li(.*?)>(.*?)<\/li>/\[\*]$2/gi;
$html =~ s/<ul(.*?)>/\[list]/gi;
$html =~ s/<\/ul>/\[\/list]/gi;
$html =~ s/<div>/\n/gi;
$html =~ s/<\/div>/\n/gi;
$html =~ s/<td(.*?)>/ /gi;
$html =~ s/<tr(.*?)>/\n/gi;

$html =~ s/<img(.*?)src="(.*?)"(.*?)>/\[img]$baseurl\/$2\[\/img]/gi;
$html =~ s/<a(.*?)href="(.*?)"(.*?)>(.*?)<\/a>/\[url=$baseurl\/$2]$4\[\/url]/gi;
$html =~ s/\[url=$baseurl\/http:\/\/(.*?)](.*?)\[\/url]/\[url=http:\/\/$1]$2\[\/url]/gi;
$html =~ s/\[img]$baseurl\/http:\/\/(.*?)\[\/img]/\[img]http:\/\/$1\[\/img]/gi;

$html =~ s/<head>(.*?)<\/head>//sgmi;
$html =~ s/<object>(.*?)<\/object>//sgmi;
$html =~ s/<script(.*?)>(.*?)<\/script>//sgmi;
$html =~ s/<style(.*?)>(.*?)<\/style>//sgmi;
$html =~ s/<title>(.*?)<\/title>//sgmi;
$html =~ s/<!--(.*?)-->/\n/sgmi;

$html =~ s/\/\//\//gi;
$html =~ s/http:\//http:\/\//gi;
$html =~ s/https:\//https:\/\//gi;

$html =~ s/<(?:[^>'"]*|(['"]).*?\1)*>//gsi;
$html =~ s/\r\r//gi;
$html =~ s/\[img]\//\[img]/gi;
$html =~ s/\[url=\//\[url=/gi;

15
อย่าทำอย่างนี้ โปรด.
maletor

68

เกี่ยวกับคำถามของวิธีการนิพจน์ทั่วไปเพื่อแยก (x) HTML, คำตอบให้กับทุกคนที่พูดคุยเกี่ยวกับข้อ จำกัด บางอย่างคือ: คุณยังไม่ได้รับการฝึกอบรมมากพอที่จะปกครองบังคับของอาวุธที่มีประสิทธิภาพนี้เนื่องจากไม่มีใครที่นี่พูดเกี่ยวกับการเรียกซ้ำ

เพื่อนร่วมงานที่ไม่เชื่อเรื่องพระเจ้าของ RegExp แจ้งเตือนฉันถึงการสนทนานี้ซึ่งไม่แน่นอนเป็นครั้งแรกบนเว็บเกี่ยวกับหัวข้อเก่าและร้อนแรงนี้

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

หากตัวแยกวิเคราะห์ HTML (x) ต้องการการเรียกซ้ำตัวแยกวิเคราะห์ RegExp ที่ไม่มีการสอบถามซ้ำนั้นไม่เพียงพอสำหรับวัตถุประสงค์ มันเป็นโครงสร้างที่เรียบง่าย

ศิลปะสีดำของนิพจน์ทั่วไปยากที่จะต้นแบบดังนั้นอาจจะมีความเป็นไปได้ต่อไปเราปล่อยออกมาในขณะที่พยายามและทดสอบวิธีการแก้ปัญหาส่วนบุคคลของเราในการจับภาพทั้งเว็บในมือข้างหนึ่ง ... ดีผมแน่ใจว่าเกี่ยวกับเรื่องนี้ :)

นี่คือรูปแบบมายากล:

$pattern = "/<([\w]+)([^>]*?)(([\s]*\/>)|(>((([^<]*?|<\!\-\-.*?\-\->)|(?R))*)<\/\\1[\s]*>))/s";

แค่ลองดู.
มันเขียนเป็นสตริง PHP ดังนั้นโมเดอเรเตอร์ "s" ทำให้คลาสมีการขึ้นบรรทัดใหม่
นี่คือตัวอย่างข้อความในคู่มือ PHP ที่ฉันเขียนเมื่อเดือนมกราคม:การอ้างอิง

(โปรดทราบว่าฉันใช้ตัวดัดแปลง "m" อย่างไม่ถูกต้องควรลบแม้ว่าจะถูกทิ้งโดยเอ็นจิ้น RegExp เนื่องจากไม่มีการใช้ ^ หรือ $ anchorage)

ตอนนี้เราสามารถพูดเกี่ยวกับข้อ จำกัด ของวิธีนี้จากมุมมองที่มีข้อมูลเพิ่มเติม:

  1. ตามการใช้งานเฉพาะของเอ็นจิน RegExp การเรียกซ้ำอาจมีการ จำกัดจำนวนของรูปแบบที่ซ้อนกันแยกวิเคราะห์แต่ขึ้นอยู่กับภาษาที่ใช้
  2. แม้ว่าความเสียหาย (x) HTML ไม่ขับรถเข้าไปในข้อผิดพลาดอย่างรุนแรงจะไม่ถูกสุขอนามัย

อย่างไรก็ตามมันเป็นเพียงรูปแบบ RegExp แต่จะเปิดเผยความเป็นไปได้ในการพัฒนาการใช้งานที่มีประสิทธิภาพจำนวนมาก
ฉันเขียนรูปแบบนี้เพื่อเพิ่มประสิทธิภาพตัวแยกวิเคราะห์โคตรของแม่แบบเครื่องยนต์ที่ฉันสร้างขึ้นในกรอบงานของฉันและการแสดงนั้นยอดเยี่ยมมากทั้งในเวลาดำเนินการหรือในการใช้หน่วยความจำ (ไม่มีอะไรเกี่ยวข้องกับเครื่องมือแม่แบบอื่น ๆ


35
ฉันจะใส่ไว้ใน "Regex ซึ่งไม่อนุญาตให้มีมากกว่าในแอตทริบิวต์" bin ตรวจสอบกับ <input value = "is 5> 3?" />
Gareth

68
หากคุณใส่อะไรลงไปในรหัสการผลิตผู้ดูแลอาจจะถูกยิง คณะลูกขุนจะไม่ตัดสินเขา
aehiilrs

30
นิพจน์ทั่วไปไม่สามารถใช้งานได้เนื่องจากคำจำกัดความไม่สามารถเรียกซ้ำได้ การเพิ่มตัวดำเนินการแบบเรียกซ้ำไปยังนิพจน์ทั่วไปนั้นทำให้ CFG ที่มีไวยากรณ์ไม่ดีเท่านั้น ทำไมไม่ใช้บางสิ่งที่ออกแบบมาให้เรียกซ้ำในตอนแรกแทนที่จะแทรกการเรียกซ้ำเข้าไปในสิ่งที่ล้นแล้วด้วยฟังก์ชั่นภายนอก
Welbog

16
การคัดค้านของฉันไม่ใช่หนึ่งในการทำงาน แต่เป็นการลงทุนครั้งเดียว ปัญหาของ RegEx คือเมื่อคุณโพสต์สมุทรเล็ก ๆ น้อย ๆ ลงหนึ่งอันปรากฏว่าคุณทำบางอย่างได้อย่างมีประสิทธิภาพมากขึ้น ("ดูโค้ดหนึ่งบรรทัด!") และแน่นอนว่าไม่มีใครพูดถึงครึ่งชั่วโมง (หรือ 3) ว่าพวกเขาใช้เวลากับสูตรโกงและ (หวังว่า) ทดสอบการเปลี่ยนรูปแบบที่เป็นไปได้ทุกครั้ง และเมื่อคุณผ่านพ้นสิ่งที่ผู้ดูแลไปคิดหรือตรวจสอบความถูกต้องของรหัสพวกเขาจะไม่สามารถมองมันและเห็นว่ามันถูกต้อง จะต้องผ่าแสดงออกและเป็นหลักซ้ำมันทั้งหมดอีกครั้ง ...
Oorang

15
... ที่จะรู้ว่ามันดี และจะเกิดขึ้นแม้กับคนที่ดีกับ regex และฉันก็สงสัยว่าคนส่วนใหญ่ที่ไม่รู้ตัวจะรู้ดี ดังนั้นคุณใช้หนึ่งในฝันร้ายที่มีชื่อเสียงมากที่สุดในการบำรุงรักษาและรวมเข้ากับการเรียกซ้ำซึ่งเป็นฝันร้ายสำหรับการบำรุงรักษาอื่น ๆและฉันคิดกับตัวเองว่าสิ่งที่ฉันต้องการในโครงการของฉันคือคนที่ฉลาดน้อยกว่า เป้าหมายคือการเขียนโค้ดที่โปรแกรมเมอร์สามารถรักษาได้โดยไม่ทำลายโค้ด ฉันรู้ว่ามัน galls รหัสไปยังตัวหารร่วมน้อยที่สุด แต่การจ้างพรสวรรค์ที่ยอดเยี่ยมนั้นยากและบ่อยครั้งที่คุณ ...
Oorang

62

ดังที่หลายคนชี้ไปแล้ว HTML ไม่ใช่ภาษาปกติที่ทำให้แยกวิเคราะห์ได้ยาก โซลูชันของฉันคือเปลี่ยนเป็นภาษาปกติโดยใช้โปรแกรมที่เป็นระเบียบแล้วใช้ตัวแยกวิเคราะห์ XML เพื่อใช้ผลลัพธ์ มีตัวเลือกที่ดีมากมายสำหรับสิ่งนี้ โปรแกรมของฉันเขียนโดยใช้ Java พร้อมกับjtidy library เพื่อเปลี่ยน HTML เป็น XML จากนั้น Jaxen ไปเป็น xpath เป็นผลลัพธ์


61
<\s*(\w+)[^/>]*>

ชิ้นส่วนอธิบาย:

<: อักขระเริ่มต้น

\s*: อาจมีช่องว่างก่อนชื่อแท็ก (น่าเกลียด แต่เป็นไปได้)

(\w+): แท็กสามารถมีตัวอักษรและตัวเลข (h1) ดี\wตรงกับ '_' แต่มันไม่เจ็บฉันเดา หากใช้อยากรู้อยากเห็น ([a-zA-Z0-9] +) แทน

[^/>]*: อะไรก็ได้ยกเว้น>และ/จนกว่าจะปิด>

>: ปิด >

ไม่เกี่ยวข้องกับ

และสำหรับพวกที่ดูถูกเหยียดหยามพูดปกติว่าพวกเขามีพลังพอ ๆ กับภาษาปกติ:

a n ba n ba nซึ่งไม่ปกติและไม่ได้เป็นบริบทฟรีสามารถจับคู่กับ^(a+)b\1b\1$

Backreferencing FTW !


@GlitchMr นั่นคือประเด็นของเขา การแสดงออกตามปกติสมัยใหม่ไม่ใช่เทคนิคปกติและไม่มีเหตุผลใด ๆ สำหรับพวกเขา
alanaktion

3
@alanaktion: นิพจน์ปกติ "ทันสมัย" (อ่าน: ด้วยส่วนขยาย Perl) ไม่สามารถจับคู่ภายในO(MN)(M เป็นความยาวนิพจน์ปกติ, N เป็นความยาวข้อความ) การอ้างอิงกลับเป็นหนึ่งในสาเหตุของสิ่งนั้น การติดตั้งใน awk นั้นไม่มี backreferences และตรงกับทุกอย่างภายในO(MN)เวลา
Konrad Borowski

56

หากคุณเพียงแค่พยายามหาแท็กเหล่านั้น (โดยไม่มีความทะเยอทะยานของการแยกวิเคราะห์) ลองใช้นิพจน์ทั่วไปนี้:

/<[^/]*?>/g

ฉันเขียนใน 30 วินาทีและทดสอบที่นี่: http://gskinner.com/RegExr/

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


2
ฉันคิดว่าคุณหมายถึงแทน\/> \\>
Justin Morgan

ไม่\>เป็นสิ่งที่ฉันหมายถึง; ฉันไม่เคยตั้งใจจะแก้ไขการแสดงออกปกติของโพสต์เดิมของฉัน
Lonnie Best

2
FYI คุณไม่จำเป็นต้องหลบเลี่ยงวงเล็บเหลี่ยม แน่นอนว่ามันไม่มีอันตรายใด ๆ ที่จะหนีจากพวกมันไปได้ แต่ดูที่ความสับสนที่คุณสามารถหลีกเลี่ยงได้ ;)
Alan Moore

บางครั้งฉันก็หลบหนีโดยไม่จำเป็นเมื่อฉันไม่แน่ใจว่ามีบางอย่างเป็นตัวละครพิเศษหรือไม่ ฉันแก้ไขคำตอบแล้ว มันใช้งานได้เหมือนกัน แต่กระชับกว่า
Lonnie Best

ดูที่ตอนนี้ฉันไม่รู้ว่าทำไมฉันถึงคิดว่าคุณหมายถึง\/เพราะนั่นจะตรงข้ามกับข้อกำหนด บางทีฉันคิดว่าคุณกำลังเสนอรูปแบบตัวกรองเชิงลบ
Justin Morgan

54

ดูเหมือนว่าฉันกำลังพยายามจับคู่แท็กโดยไม่มี "/" ที่ท้าย ลองสิ่งนี้:

<([a-zA-Z][a-zA-Z0-9]*)[^>]*(?<!/)>

8
สิ่งนี้ใช้ไม่ได้ สำหรับอินพุต '<xa = "<b>" /> <y>' รายการที่ตรงกันคือ x และ y แม้ว่า x จะสิ้นสุดลง
ceving

51

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

regex อย่างง่าย ๆ<([^>"']|"[^"]*"|'[^']*')*>มักจะดีพอในกรณีเช่นที่ฉันเพิ่งพูดถึง มันเป็นทางออกที่ไร้เดียงสาทุกอย่างถูกพิจารณา แต่มันก็ไม่ถูกต้อง>สัญลักษณ์ที่ไม่มีการในค่าคุณลักษณะหากคุณกำลังมองหาเช่นเป็นแท็กที่คุณสามารถปรับให้เป็นtable</?table\b([^>"']|"[^"]*"|'[^']*')*>

เพียงแค่ให้ความรู้สึกของสิ่งที่ regex HTML "ขั้นสูง" จะมีลักษณะต่อไปนี้จะทำงานที่น่านับถืออย่างเป็นธรรมของการเลียนแบบพฤติกรรมเบราว์เซอร์ในโลกแห่งความจริงและขั้นตอนวิธีการแยก HTML5:

</?([A-Za-z][^\s>/]*)(?:=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)|[^>])*(?:>|$)

ต่อไปนี้ตรงกับคำจำกัดความที่ค่อนข้างเข้มงวดของแท็ก XML (แม้ว่าจะไม่ได้รับการพิจารณาสำหรับชุดอักขระ Unicode ที่อนุญาตในชื่อ XML):

<(?:([_:A-Z][-.:\w]*)(?:\s+[_:A-Z][-.:\w]*\s*=\s*(?:"[^"]*"|'[^']*'))*\s*/?|/([_:A-Z][-.:\w]*)\s*)>

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

ในตอนท้ายของวันให้ใช้เครื่องมือที่เหมาะสมที่สุดสำหรับงานแม้ในกรณีที่เครื่องมือนั้นเป็น regex


49

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

มีการโพสต์บล็อกที่ชัดเจนเกี่ยวกับการจับคู่องค์ประกอบ HTML ที่อยู่ด้านในสุดที่เขียนโดย Steven Levithan

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