RegEx จับคู่แท็กที่เปิดอยู่ยกเว้นแท็กที่มีอยู่ในตัว XHTML
ฉันต้องการจับคู่แท็กเปิดทั้งหมดเหล่านี้:
<p>
<a href="foo">
แต่ไม่ใช่สิ่งเหล่านี้:
<br />
<hr class="foo" />
ฉันคิดสิ่งนี้ขึ้นมาและต้องการให้แน่ใจว่าฉันทำถูกต้อง ฉันกำลังจับไฟล์a-z
.
<([a-z]+) *[^/]*?>
ฉันเชื่อว่ามันบอกว่า:
- ค้นหาน้อยกว่าแล้ว
- ค้นหา (และจับภาพ) az หนึ่งครั้งขึ้นไปจากนั้น
- หาช่องว่างตั้งแต่ศูนย์ขึ้นไปจากนั้น
- ค้นหาอักขระศูนย์หรือมากกว่าครั้งโลภยกเว้น
/
แล้ว - ค้นหามากกว่า
ฉันมีสิทธิ์มั้ย? และที่สำคัญคุณคิดอย่างไร?
คำตอบ
คุณไม่สามารถแยกวิเคราะห์ [X] HTML ด้วยนิพจน์ทั่วไป เนื่องจาก regex ไม่สามารถแยกวิเคราะห์ HTML ได้ 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 ด้วยนิพจน์ทั่วไปคุณกำลังให้พวกเขาและวิธีการดูหมิ่นของพวกเขาซึ่งทำให้เราทุกคนต้องทำงานหนักอย่างไร้มนุษยธรรมสำหรับผู้ที่ไม่สามารถแสดงชื่อในเครื่องบินหลายภาษาขั้นพื้นฐาน HTML-plus-regexp จะทำให้ความรู้สึกของความรู้สึกเป็นของเหลวในขณะที่คุณสังเกตจิตใจของคุณจะเหี่ยวเฉาในการโจมตีของความสยองขวัญ ตัวแยกวิเคราะห์ HTML ที่ใช้Rege̿̔̉xเป็นมะเร็งที่กำลังฆ่า StackOverflowมันสายเกินไปมันสายเกินไปที่เราไม่สามารถบันทึกไว้ได้การล่วงละเมิดของChi͡ldทำให้มั่นใจได้ว่า regex จะกินเนื้อเยื่อที่มีชีวิตทั้งหมด (ยกเว้น HTML ซึ่งไม่สามารถทำได้ตามที่ทำนายไว้ก่อนหน้านี้) เจ้านายที่รักช่วยเราได้อย่างไรว่าใครจะรอดจากการระบาดนี้โดยใช้ regex เพื่อแยกวิเคราะห์ HTML ได้ทำให้มนุษยชาติถึงวาระแห่งการทรมานอันน่าสะพรึงกลัวและช่องโหว่ด้านความปลอดภัยโดยใช้ rege x เป็นเครื่องมือในการประมวลผล HTML สร้าง Brea ch ระหว่างโลกนี้กับดินแดนที่น่ากลัวของเอนทิตีc͒ͪo͛ͫrrupt (เช่นเอนทิตี SGML แต่มีความเสียหายมากกว่า) ซึ่งเป็นเพียงมุมมองของ โลกของ reg parsers อดีตเพื่อใช้ HTML จะอิน tantly ขนส่ง AP สติ rogrammer ของฉัน n ใน AW Orl d จากหยุดหย่อนกรีดร้องเขามา
ที่พาล SL
ithy regex ติดเชื้อ Wil ลิตรกิน HT ของคุณ parser ML, การประยุกต์ใช้และการดำรงอยู่ ตลอดเวลาเช่น Visual Basic แย่ลงเท่านั้นที่เขามาเขา com es do not fi ght h e com̡e̶s, ̕h̵i s un̨ho͞lyradiańcé de stro҉ying all enli̍̈́̂̈́ghtenment, HTML tags lea͠ki̧n͘gfr̶ǫm̡yo͟ureye͢s̸̛l̕ik͏e liq uid p ain, the song ประสบการณ์ใหม่
ssion แยก
จะ exti nguish เสียงของหมอคนตาลจากเอสพีที่นี่ฉันสามารถดูได้คุณสามารถเห็นมันมันเป็นเสื้อที่สวยงามเขาฉinal snuf
fing o ฉโกหกของผู้ชายทั้งหมดจะหายไป LL ฉัน SL OST TH E ม้าเขามา s เขา com
ES เขาร่วม
ฉัน
เซนต์เขา ichหรือ permeat ES อัลลิตร MY FAC E MY FACE ᵒhพระเจ้า n o NO Noo o บน Θหยุดทีเขานี้ * ̶͑̾̾ GL ES n ot rè̑ͧ̌aͨl̘̝̙̃ͤ͂̾̆ ZA̡͊͠͝LGΌ ISͮ̂҉̯͈͕̹̘̱ T O͇̹̺ͅƝ̴ȳ̳ TH̘ Ë͖́̉ ͠P̯͍̭O̚N̐Y̡ H̸̡̪̯ͨ͊̽̅̾̎Ȩ̬̩̾͛ͪ̈́̀́͘ ̶̧̨̱̹̭̯ͧ̾ͬC̷̙̲̝͖ͭ̏ͥͮ͟Oͮ͏̮̪̝͍M̲̖͊̒ͪͩͬ̚̚͜Ȇ̴̟̟͙̞ͩ͌͝ S̨̥̫͎̭ͯ̿̔̀ͅ
͎a̧͈͖r̽̾̈́͒͑e
คุณได้ลองใช้ตัวแยกวิเคราะห์ XML แทนหรือไม่?
หมายเหตุของผู้ดูแล
โพสต์นี้ถูกล็อกเพื่อป้องกันการแก้ไขเนื้อหาที่ไม่เหมาะสม โพสต์มีลักษณะตรงตามที่ควรจะเป็น - ไม่มีปัญหากับเนื้อหา โปรดอย่าตั้งค่าสถานะเพื่อความสนใจของเรา
แม้ว่าHTML โดยพลการที่มีเพียงนิพจน์ทั่วไปจะเป็นไปไม่ได้ แต่บางครั้งก็เหมาะสมที่จะใช้เพื่อแยกวิเคราะห์ชุด HTML ที่จำกัด และเป็นที่รู้จัก
หากคุณมีหน้า HTML ชุดเล็ก ๆ ที่ต้องการขูดข้อมูลจากนั้นจึงบรรจุลงในฐานข้อมูล regexes อาจทำงานได้ดี ตัวอย่างเช่นเมื่อเร็ว ๆ นี้ฉันต้องการทราบชื่อพรรคและเขตของผู้แทนรัฐบาลกลางออสเตรเลียซึ่งฉันได้รับจากเว็บไซต์ของรัฐสภา นี่เป็นงานที่ จำกัด เพียงครั้งเดียว
Regexes ทำงานได้ดีสำหรับฉันและตั้งค่าได้รวดเร็วมาก
ผมคิดว่าข้อบกพร่องที่นี่เป็นที่ HTML เป็นชัมประเภท 2 ไวยากรณ์ (บริบทฟรีไวยากรณ์)และการแสดงออกปกติคือชัม 3 ประเภทไวยากรณ์ (ไวยากรณ์ปกติ) เนื่องจากไวยากรณ์ประเภท 2 มีความซับซ้อนโดยพื้นฐานมากกว่าไวยากรณ์ประเภท 3 (ดูลำดับชั้นของ Chomsky ) จึงเป็นไปไม่ได้ทางคณิตศาสตร์ที่จะแยกวิเคราะห์ XML ด้วยนิพจน์ทั่วไป
แต่หลายคนจะพยายามและบางคนก็จะอ้างว่าประสบความสำเร็จ - แต่จนกว่าคนอื่นจะพบข้อผิดพลาดและทำให้คุณยุ่ง
อย่าไปฟังพวกนี้ คุณสามารถแยกวิเคราะห์ไวยากรณ์ที่ไม่มีบริบทด้วยนิพจน์ทั่วไปได้หากคุณแบ่งงานออกเป็นชิ้นเล็ก ๆ คุณสามารถสร้างรูปแบบที่ถูกต้องด้วยสคริปต์ที่ทำตามลำดับ:
- แก้ปัญหาการหยุดชะงัก
- สี่เหลี่ยมวงกลม
- ระบุปัญหาพนักงานขายการเดินทางใน O (log n) หรือน้อยกว่า ถ้ามากกว่านั้นแรมคุณจะหมดและเครื่องยนต์จะค้าง
- รูปแบบจะค่อนข้างใหญ่ดังนั้นตรวจสอบให้แน่ใจว่าคุณมีอัลกอริทึมที่บีบอัดข้อมูลแบบสุ่มโดยไม่สูญเสีย
- เกือบจะแล้ว - แค่หารทั้งสิ่งด้วยศูนย์ ง่าย peasy
ฉันยังทำส่วนสุดท้ายไม่เสร็จ แต่ฉันรู้ว่าฉันใกล้จะเสร็จแล้ว มันทำให้การขว้างปาCthulhuRlyehWgahnaglFhtagnException
s ด้วยเหตุผลบางอย่างดังนั้นฉันจะพอร์ตเพื่อ VB 6 On Error Resume Next
และการใช้งาน ฉันจะอัปเดตด้วยรหัสเมื่อฉันตรวจสอบประตูแปลก ๆ ที่เพิ่งเปิดในกำแพง อืม.
ป.ล. ปิแอร์เดอแฟร์มาต์ยังหาวิธีทำ แต่ระยะขอบที่เขาเขียนนั้นไม่ใหญ่พอสำหรับโค้ด
Disclaimer : ใช้ parser ถ้าคุณมีตัวเลือก ที่บอกว่า ...
นี่คือ regex ที่ฉันใช้ (!) เพื่อจับคู่แท็ก HTML:
<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>
มันอาจจะไม่สมบูรณ์แบบ แต่ฉันใช้โค้ดนี้ผ่านHTML จำนวนมาก โปรดทราบว่ามันจับสิ่งแปลก ๆ เช่น<a name="badgenerator"">
ที่ปรากฏบนเว็บ
ฉันเดาว่าจะทำให้ไม่ตรงกับแท็กที่มีอยู่ในตัวคุณอาจต้องการใช้รูปลักษณ์เชิงลบของKobi :
<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+(?<!/\s*)>
หรือเพียงแค่รวมถ้าและถ้าไม่
สำหรับผู้ลงคะแนน:นี่คือรหัสที่ใช้งานได้จากผลิตภัณฑ์จริง ฉันสงสัยว่าใครก็ตามที่อ่านหน้านี้จะได้รับความประทับใจว่าการใช้ regexes บน HTML เป็นที่ยอมรับของสังคม
Caveat : ฉันควรทราบว่า regex นี้ยังคงแยกย่อยต่อหน้าบล็อก CDATA ความคิดเห็นและองค์ประกอบของสคริปต์และสไตล์ ข่าวดีก็คือคุณสามารถกำจัดผู้ที่ใช้ regex ...
มีหลายคนที่จะบอกคุณว่าโลกกลม (หรือบางทีโลกก็เป็นทรงกลมเอียงถ้าพวกเขาต้องการใช้คำแปลก ๆ ) พวกเขากำลังโกหก
มีหลายคนที่จะบอกคุณว่านิพจน์ทั่วไปไม่ควรเรียกซ้ำ พวกเขากำลัง จำกัด คุณ พวกเขาจำเป็นต้องปราบคุณและพวกเขาก็ทำโดยทำให้คุณไม่รู้
คุณสามารถอยู่ในความเป็นจริงของพวกเขาหรือกินยาเม็ดสีแดง
เช่นเดียวกับ Lord Marshal (เขาเป็นญาติกับคลาส Marshal .NET หรือไม่) ฉันได้เห็นRegex-Verse ของ
Underverse
Stack และกลับมาพร้อมกับความรู้เกี่ยวกับ
พลังที่
คุณนึกไม่ถึง ใช่ฉันคิดว่ามี Old One หรือสองคนคอยปกป้องพวกเขา แต่พวกเขากำลังดูฟุตบอลทางทีวีดังนั้นมันจึงไม่ใช่เรื่องยาก
ฉันคิดว่ากรณี 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());
}
}
หากคุณไม่แน่ใจไม่ฉันไม่ได้ล้อเล่น (แต่บางทีฉันอาจจะโกหก) มันจะทำงาน. ฉันได้สร้างตันของการทดสอบหน่วยทดสอบและฉันได้ใช้งานได้ (ส่วนหนึ่งของ) สำหรับการทดสอบความสอดคล้อง มันเป็นโทเค็นไม่ใช่ตัวแยกวิเคราะห์แบบเต็มดังนั้นมันจะแยก XML ออกเป็นโทเค็นคอมโพเนนต์เท่านั้น จะไม่แยกวิเคราะห์ / รวม DTD
อ้อ ... ถ้าคุณต้องการซอร์สโค้ดของ regex ด้วยวิธีการเสริม:
regex เพื่อโทเค็น xmlหรือregex แบบเต็ม
ในเชลล์คุณสามารถแยกวิเคราะห์HTMLโดยใช้sed :
- ทัวริงที่ผ่านมา
- เขียนโปรแกรมแยกวิเคราะห์ HTML (การบ้าน)
- ???
- กำไร!
ที่เกี่ยวข้อง (ทำไมคุณไม่ควรใช้การจับคู่นิพจน์ทั่วไป):
- ถ้าคุณชอบการแสดงออกปกติมากทำไมคุณไม่แต่งงานกับพวกเขา?
- นิพจน์ทั่วไป: ตอนนี้คุณมีปัญหาสองประการ
- แฮ็กเจลทำความสะอาด HTML ของ stackoverflow.com
ฉันยอมรับว่าเครื่องมือที่เหมาะสมในการแยกวิเคราะห์ XML และโดยเฉพาะ HTMLคือตัวแยกวิเคราะห์ไม่ใช่เครื่องมือนิพจน์ทั่วไป อย่างไรก็ตามเช่นเดียวกับที่คนอื่น ๆ ได้ชี้ให้เห็นบางครั้งการใช้ regex นั้นเร็วกว่าง่ายกว่าและทำให้งานเสร็จถ้าคุณรู้รูปแบบข้อมูล
Microsoft มีส่วนของแนวทางปฏิบัติที่ดีที่สุดสำหรับนิพจน์ทั่วไปใน. NET Frameworkและพูดถึงการพิจารณา [ing] แหล่งข้อมูลเข้าโดยเฉพาะ
นิพจน์ทั่วไปมีข้อ จำกัด แต่คุณได้พิจารณาสิ่งต่อไปนี้แล้วหรือยัง?
กรอบ NET เป็นที่ไม่ซ้ำกันเมื่อมันมาถึงการแสดงผลปกติในการที่จะสนับสนุนBalancing กลุ่มนิยาม
- ดูการจับคู่โครงสร้างที่สมดุลกับนิพจน์ทั่วไปของ. NET
- ดูนิพจน์ทั่วไปของ. NET: Regex และ Balanced Matching
- ดูเอกสารของ Microsoft เกี่ยวกับBalancing Group Definitions
ด้วยเหตุนี้ฉันเชื่อว่าคุณสามารถแยกวิเคราะห์ XML โดยใช้นิพจน์ทั่วไป อย่างไรก็ตามโปรดทราบว่าต้องเป็น XML ที่ถูกต้อง ( เบราว์เซอร์ให้อภัย HTML มากและอนุญาตให้ใช้ไวยากรณ์ XML ที่ไม่ถูกต้องภายใน HTML ) สิ่งนี้เป็นไปได้เนื่องจาก "การกำหนดกลุ่มการปรับสมดุล" จะอนุญาตให้เอ็นจินนิพจน์ทั่วไปทำหน้าที่เป็น PDA
อ้างจากบทความ 1 ที่อ้างถึงข้างต้น:
.NET Regular Expression Engine
ตามที่อธิบายไว้ข้างต้นโครงสร้างที่สมดุลอย่างเหมาะสมไม่สามารถอธิบายได้ด้วยนิพจน์ทั่วไป อย่างไรก็ตามเอ็นจินนิพจน์ทั่วไป. NET มีโครงสร้างสองสามแบบที่อนุญาตให้จดจำโครงสร้างที่สมดุลได้
(?<group>)
- ผลักดันผลลัพธ์ที่จับบนกองการจับภาพด้วยกลุ่มชื่อ(?<-group>)
- แสดงการจับภาพสูงสุดอันดับต้น ๆ พร้อมกับกลุ่มชื่อจากกองการจับภาพ(?(group)yes|no)
- จับคู่ส่วนใช่หากมีกลุ่มที่มีกลุ่มชื่อเป็นอย่างอื่นไม่ตรงกับส่วนใดส่วนหนึ่งโครงสร้างเหล่านี้อนุญาตให้นิพจน์ทั่วไป. NET เลียนแบบ PDA ที่ถูก จำกัด โดยการอนุญาตการดำเนินการสแต็กเวอร์ชันง่ายๆ: พุชป๊อปและว่างเปล่า การดำเนินการอย่างง่ายนั้นค่อนข้างเทียบเท่ากับการเพิ่มการลดและการเปรียบเทียบกับศูนย์ตามลำดับ สิ่งนี้ช่วยให้เอ็นจินนิพจน์ทั่วไป. NET สามารถจดจำชุดย่อยของภาษาที่ไม่มีบริบทโดยเฉพาะอย่างยิ่งภาษาที่ต้องการตัวนับธรรมดาเท่านั้น สิ่งนี้จะช่วยให้นิพจน์ทั่วไป. NET ที่ไม่ใช่แบบดั้งเดิมสามารถจดจำโครงสร้างที่สมดุลอย่างเหมาะสมของแต่ละบุคคลได้
พิจารณานิพจน์ทั่วไปต่อไปนี้:
(?=<ul\s+id="matchMe"\s+type="square"\s*>)
(?>
<!-- .*? --> |
<[^>]*/> |
(?<opentag><(?!/)[^>]*[^/]>) |
(?<-opentag></[^>]*[^/]>) |
[^<>]*
)*
(?(opentag)(?!))
ใช้แฟล็ก:
- แถวเดียว
- IgnorePatternWhitespace (ไม่จำเป็นถ้าคุณยุบ regex และลบช่องว่างทั้งหมด)
- 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 >ul<, 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 >ul<, 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 >ul<, oh my!</li> <li>...</li> </ul> </div> </li> </ul>
สุดท้ายนี้ผมมีความสุขจริงๆบทความเจฟฟ์แอด: แยก Html ธู Way ตลกพอมันอ้างถึงคำตอบสำหรับคำถามนี้ซึ่งปัจจุบันมีผู้โหวตมากกว่า 4k
ฉันขอแนะนำให้ใช้QueryPathเพื่อแยกวิเคราะห์ XML และ HTML ใน PHP โดยพื้นฐานแล้วมันเป็นไวยากรณ์แบบเดียวกับ jQuery เพียง แต่อยู่ทางฝั่งเซิร์ฟเวอร์
แม้ว่าคำตอบที่คุณไม่สามารถแยกวิเคราะห์ HTML ด้วยนิพจน์ทั่วไปจะถูกต้อง แต่ก็ใช้ไม่ได้กับที่นี่ OP เพียงต้องการแยกวิเคราะห์แท็ก HTML หนึ่งแท็กด้วย regexes และนั่นคือสิ่งที่สามารถทำได้ด้วยนิพจน์ทั่วไป
regex ที่แนะนำไม่ถูกต้องแม้ว่า:
<([a-z]+) *[^/]*?>
ถ้าคุณเพิ่มสิ่งที่ regex โดย backtracking ก็สามารถบังคับให้ตรงกับสิ่งที่โง่เหมือน<a >>
, [^/]
เป็นบุตรเกินไป นอกจากนี้โปรดทราบว่า<space>*[^/]*
มีความซ้ำซ้อนเนื่องจาก[^/]*
สามารถจับคู่ช่องว่างได้เช่นกัน
ข้อเสนอแนะของฉันคือ
<([a-z]+)[^>]*(?<!/)>
ที่ไหน(?<! ... )
เป็น (ใน regexes Perl) ลบรูปลักษณ์ที่อยู่เบื้องหลัง มันอ่านว่า "a <แล้วคำหนึ่งแล้วอะไรก็ตามที่ไม่ใช่ a> ซึ่งสุดท้ายอาจไม่ใช่ a / ตามด้วย>"
โปรดทราบว่าสิ่งนี้อนุญาตให้ทำสิ่งต่างๆเช่น<a/ >
(เช่นเดียวกับ regex ดั้งเดิม) ดังนั้นหากคุณต้องการบางสิ่งที่เข้มงวดมากขึ้นคุณต้องสร้าง regex เพื่อจับคู่แอตทริบิวต์คู่โดยคั่นด้วยช่องว่าง
ลอง:
<([^\s]+)(\s[^>]*?)?(?<!/)>
คล้ายกับของคุณ แต่สุดท้าย>
ต้องไม่อยู่หลังเครื่องหมายทับและยอมรับh1
ด้วย
ซุนวูนักยุทธศาสตร์ทั่วไปและนักปรัชญาของจีนโบราณกล่าวว่า:
ว่ากันว่าหากคุณรู้จักศัตรูและรู้จักตัวเองคุณสามารถชนะการต่อสู้ได้เป็นร้อยครั้งโดยไม่มีการสูญเสียแม้แต่ครั้งเดียว หากคุณรู้จักตัวเอง แต่ไม่ใช่คู่ต่อสู้คุณอาจชนะหรืออาจแพ้ หากคุณไม่รู้จักตัวเองหรือศัตรูของคุณคุณจะเป็นอันตรายต่อตัวเองเสมอ
ในกรณีนี้ศัตรูของคุณคือ HTML และคุณก็เป็นตัวคุณเองหรือ regex คุณอาจเป็น Perl ที่มี regex ผิดปกติ รู้จัก HTML รู้จักตัวเอง.
ฉันได้แต่งไฮกุที่อธิบายลักษณะของ HTML
HTML has
complexity exceeding
regular language.
ฉันยังแต่งไฮกุที่อธิบายธรรมชาติของนิพจน์ทั่วไปใน Perl
The regex you seek
is defined within the phrase
<([a-zA-Z]+)(?:[^>]*[^/]*)?>
<?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 เพื่อจุดประสงค์นี้
ฉันไม่ทราบความต้องการที่แท้จริงของคุณสำหรับสิ่งนี้ แต่ถ้าคุณใช้. NET ด้วยคุณจะใช้Html Agility Packไม่ได้หรือ?
ข้อความที่ตัดตอนมา:
เป็นไลบรารีรหัส. NET ที่ให้คุณแยกวิเคราะห์ไฟล์ HTML "นอกเว็บ" โปรแกรมแยกวิเคราะห์มีความอดทนอย่างมากกับ HTML ที่มีรูปแบบไม่ถูกต้อง "โลกแห่งความจริง"
คุณต้องการสิ่งแรกที่>
ไม่นำหน้าด้วยไฟล์/
. ดูที่นี่เพื่อดูรายละเอียดเกี่ยวกับวิธีการทำเช่นนั้น มันเรียกว่ามองในแง่ลบเบื้องหลัง
อย่างไรก็ตามการนำไปใช้อย่างไร้เดียงสาจะจบลงด้วยการจับคู่<bar/></foo>
ในเอกสารตัวอย่างนี้
<foo><bar/></foo>
คุณสามารถให้ข้อมูลเพิ่มเติมเกี่ยวกับปัญหาที่คุณกำลังพยายามแก้ไขได้หรือไม่? คุณทำซ้ำผ่านแท็กโดยใช้โปรแกรมหรือไม่?
W3C อธิบายการแยกวิเคราะห์ในรูปแบบ regexp หลอก:
W3C Link
ตามลิงค์ var สำหรับQName
, S
และAttribute
เพื่อให้ได้ภาพที่ชัดเจน
จากนั้นคุณสามารถสร้าง regexp ที่ค่อนข้างดีเพื่อจัดการกับสิ่งต่างๆเช่นการลอกแท็ก
หากคุณต้องการสิ่งนี้สำหรับ PHP:
PHP DOM ฟังก์ชั่นจะไม่ทำงานอย่างถูกต้องจนกว่าจะมีการจัดรูปแบบ XML ไม่ว่าการใช้งานของพวกเขาจะดีกว่าสำหรับมนุษยชาติที่เหลือมากเพียงใด
simplehtmldomเป็นสิ่งที่ดี แต่ฉันพบว่ามันค่อนข้างบั๊กและมันค่อนข้างหนักหน่วยความจำ [จะผิดพลาดในหน้าเว็บขนาดใหญ่]
ฉันไม่เคยใช้คิวรีพา ธจึงไม่สามารถแสดงความคิดเห็นเกี่ยวกับประโยชน์ของมันได้
อีกอันที่ต้องลองคือDOMParserของฉันซึ่งมีทรัพยากรน้อยมากและฉันใช้งานอย่างมีความสุขมาระยะหนึ่งแล้ว ง่ายต่อการเรียนรู้และมีประสิทธิภาพ
สำหรับ Python และ Java มีการโพสต์ลิงก์ที่คล้ายกัน
สำหรับผู้ลงคะแนน - ฉันเขียนชั้นเรียนของฉันก็ต่อเมื่อตัวแยกวิเคราะห์ XML พิสูจน์แล้วว่าไม่สามารถทนต่อการใช้งานจริงได้ การลงคะแนนทางศาสนาเพียงแค่ป้องกันไม่ให้มีการโพสต์คำตอบที่เป็นประโยชน์โปรดเก็บสิ่งต่างๆไว้ในมุมมองของคำถาม
นี่คือวิธีแก้ปัญหา:
<?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]);
?>
เพื่อทดสอบอย่างละเอียดฉันได้ป้อนแท็กปิดอัตโนมัติของสตริงเช่น:
- <ชม />
- <br/>
- <br>
ฉันยังใส่แท็กด้วย:
- หนึ่งแอตทริบิวต์
- มากกว่าหนึ่งแอตทริบิวต์
- แอตทริบิวต์ที่มีการผูกค่าไว้ในเครื่องหมายคำพูดเดี่ยวหรือเครื่องหมายคำพูดคู่
- แอตทริบิวต์ที่มีเครื่องหมายคำพูดเดี่ยวเมื่อตัวคั่นเป็นเครื่องหมายคำพูดคู่และในทางกลับกัน
- แอตทริบิวต์ "ไม่สวย" โดยมีช่องว่างก่อนสัญลักษณ์ "=" ตามหลังและทั้งก่อนและหลัง
หากคุณพบบางสิ่งที่ไม่ได้ผลในการพิสูจน์แนวคิดข้างต้นฉันพร้อมให้วิเคราะห์โค้ดเพื่อพัฒนาทักษะของฉัน
<EDIT>ฉันลืมไปว่าคำถามจากผู้ใช้คือหลีกเลี่ยงการแยกวิเคราะห์แท็กปิดตัวเอง ในกรณีนี้รูปแบบจะง่ายกว่าโดยเปลี่ยนเป็น:
$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*>/';
@ridgerunner ผู้ใช้สังเกตเห็นว่ารูปแบบที่ไม่อนุญาตให้แอตทริบิวต์ unquotedหรือคุณลักษณะที่มีค่าไม่มี ในกรณีนี้การปรับแต่งอย่างละเอียดทำให้เรามีรูปแบบต่อไปนี้:
$pattern = '/<(\w+)(\s+(\w+)(\s*\=\s*(\'|"|)(.*?)\\5\s*)?)*\s*>/';
</EDIT>
ทำความเข้าใจกับรูปแบบ
หากมีใครสนใจที่จะเรียนรู้เพิ่มเติมเกี่ยวกับรูปแบบนี้ฉันให้บรรทัด:
- นิพจน์ย่อยแรก (\ w +) ตรงกับชื่อแท็ก
- นิพจน์ย่อยที่สองมีรูปแบบของแอตทริบิวต์ ประกอบด้วย:
- ช่องว่างอย่างน้อยหนึ่งช่อง +
- ชื่อของแอตทริบิวต์ (\ w +)
- ช่องว่างศูนย์หรือมากกว่า * (เป็นไปได้หรือไม่เว้นว่างไว้ที่นี่)
- สัญลักษณ์ "="
- อีกครั้งช่องว่างเป็นศูนย์หรือมากกว่า
- ตัวคั่นของค่าแอตทริบิวต์เครื่องหมายคำพูดเดี่ยวหรือคู่ ('| ") ในรูปแบบเครื่องหมายคำพูดเดี่ยวจะถูกหลีกเลี่ยงเนื่องจากเกิดขึ้นพร้อมกับตัวคั่นสตริง PHP นิพจน์ย่อยนี้ถูกจับด้วยวงเล็บเพื่อให้สามารถอ้างอิงได้ อีกครั้งเพื่อแยกวิเคราะห์การปิดแอตทริบิวต์นั่นเป็นเหตุผลว่าทำไมจึงสำคัญมาก
- ค่าของแอตทริบิวต์ที่จับคู่โดยเกือบทุกอย่าง: (. *?); ในรูปแบบเฉพาะนี้โดยใช้การจับคู่แบบโลภ (เครื่องหมายคำถามหลังเครื่องหมายดอกจัน) เอ็นจิน RegExp จะเปิดใช้ตัวดำเนินการที่เหมือน "มองไปข้างหน้า" ซึ่งจะจับคู่อะไรก็ได้ แต่จะเป็นอะไรที่ตามมา
- ความสนุกมาที่นี่: ส่วน \ 4 เป็นตัวดำเนินการ backreferenceซึ่งหมายถึงนิพจน์ย่อยที่กำหนดไว้ก่อนหน้าในรูปแบบในกรณีนี้ฉันกำลังอ้างถึงนิพจน์ย่อยที่สี่ซึ่งเป็นตัวคั่นแอตทริบิวต์แรกที่พบ
- ช่องว่างศูนย์หรือมากกว่า *
- นิพจน์ย่อยแอตทริบิวต์จะสิ้นสุดที่นี่โดยมีข้อกำหนดของเหตุการณ์ที่เป็นไปได้เป็นศูนย์หรือมากกว่าซึ่งกำหนดโดยเครื่องหมายดอกจัน
- จากนั้นเนื่องจากแท็กอาจลงท้ายด้วยช่องว่างก่อนสัญลักษณ์ ">" ช่องว่างที่เป็นศูนย์หรือมากกว่าจะจับคู่กับรูปแบบย่อย \ s *
- แท็กที่จะจับคู่อาจลงท้ายด้วยสัญลักษณ์ ">" แบบธรรมดาหรือปิด XHTML ที่เป็นไปได้ซึ่งใช้เครื่องหมายทับข้างหน้า: (/> |>) แน่นอนว่าเครื่องหมายทับจะหนีไปเนื่องจากมันเกิดขึ้นพร้อมกับตัวคั่นนิพจน์ทั่วไป
เคล็ดลับเล็ก ๆ : เพื่อให้วิเคราะห์โค้ดนี้ได้ดีขึ้นจำเป็นต้องดูที่ซอร์สโค้ดที่สร้างขึ้นเนื่องจากฉันไม่ได้ระบุอักขระพิเศษ HTML ใด ๆ ที่หลบหนี
เมื่อใดก็ตามที่ฉันต้องการดึงข้อมูลบางอย่างออกจากเอกสาร HTML อย่างรวดเร็วฉันใช้ Tidy เพื่อแปลงเป็น XML จากนั้นใช้ XPath หรือ XSLT เพื่อรับสิ่งที่ฉันต้องการ ในกรณีของคุณสิ่งนี้:
//p/a[@href='foo']
ฉันเคยใช้เครื่องมือโอเพนซอร์สชื่อHTMLParserมาก่อน ออกแบบมาเพื่อแยกวิเคราะห์ HTML ในรูปแบบต่างๆและตอบสนองวัตถุประสงค์ได้ดีทีเดียว สามารถแยกวิเคราะห์ HTML เป็น treenode อื่นและคุณสามารถใช้ API เพื่อดึงแอตทริบิวต์ออกจากโหนดได้อย่างง่ายดาย ตรวจสอบและดูว่าสิ่งนี้สามารถช่วยคุณได้หรือไม่
ฉันชอบแยกวิเคราะห์ 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 เป็นต้นฉันไม่ได้ล้อเล่น :) สนุก
มันง่ายพอที่จะเปลี่ยนสคริปต์ Perl แรกของฉันให้กลายเป็นสิ่งที่ดีในการสตรีมหากคุณต้องการประมวลผลหน้าเว็บขนาดใหญ่ แต่มันไม่จำเป็นจริงๆ
แยก HTML
นิพจน์ทั่วไปที่ดีกว่า:
/(<.*?>|[^<]+)\s*/g # Get tags and text
/(\w+)="(.*?)"/g # Get attibutes
เหมาะสำหรับ XML / XHTML
ด้วยรูปแบบเล็กน้อยสามารถรับมือกับ HTML ที่ยุ่งเหยิง ... หรือแปลง HTML -> XHTML ก่อน
วิธีที่ดีที่สุดในการเขียนนิพจน์ทั่วไปคือในรูปแบบLex / Yaccไม่ใช่แบบทึบแสงหนึ่งบรรทัดหรือแสดงความผิดปกติแบบหลายบรรทัดที่แสดงความคิดเห็น ฉันไม่ได้ทำที่นี่ แต่; สิ่งเหล่านี้แทบไม่จำเป็นต้องใช้เลย
นี่คือโปรแกรมแยกวิเคราะห์ที่ใช้PHPซึ่งแยกวิเคราะห์ HTML โดยใช้ regex ที่ไม่สุภาพ ในฐานะผู้เขียนโครงการนี้ฉันสามารถบอกคุณได้ว่าเป็นไปได้ที่จะแยกวิเคราะห์ HTML ด้วย regex แต่ไม่มีประสิทธิภาพ หากคุณต้องการโซลูชันฝั่งเซิร์ฟเวอร์ (เช่นเดียวกับที่ฉันทำกับปลั๊กอิน wp-Typography WordPressของฉัน) สิ่งนี้ก็ใช้ได้
มีบาง regexes ที่ดีสำหรับการเปลี่ยน HTML ที่มี BBCode อยู่ที่นี่ สำหรับนักพูดทุกคนโปรดทราบว่าเขาไม่ได้พยายามแยกวิเคราะห์ HTML อย่างเต็มที่เพียงเพื่อล้างมัน เขาอาจจะสามารถฆ่าแท็กที่ "โปรแกรมแยกวิเคราะห์" ธรรมดา ๆ ของเขาไม่สามารถเข้าใจได้
ตัวอย่างเช่น:
$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;
เกี่ยวกับคำถามเกี่ยวกับวิธีการแสดงออกปกติในการแยกวิเคราะห์ (x) HTML คำตอบของทุกคนที่พูดถึงขีด จำกัด บางประการคือคุณไม่ได้รับการฝึกฝนมากพอที่จะควบคุมพลังของอาวุธทรงพลังนี้เนื่องจากไม่มีใครพูดถึงการเรียกซ้ำ .
เพื่อนร่วมงานที่ไม่เชื่อเรื่องพระเจ้าในการแสดงออกเป็นประจำแจ้งให้ฉันทราบการสนทนานี้ซึ่งไม่ใช่ครั้งแรกในเว็บเกี่ยวกับประเด็นเก่าและประเด็นร้อนนี้
หลังจากอ่านบางโพสต์สิ่งแรกที่ฉันทำคือมองหาสตริง "? R" ในชุดข้อความนี้ อย่างที่สองคือการค้นหาเกี่ยวกับ "การเรียกซ้ำ"
ไม่วัวศักดิ์สิทธิ์ไม่พบการแข่งขัน เนื่องจากไม่มีใครพูดถึงกลไกหลักที่ตัวแยกวิเคราะห์ถูกสร้างขึ้นในไม่ช้าฉันก็รู้ว่าไม่มีใครเข้าใจ
หากตัวแยกวิเคราะห์ HTML (x) ต้องการการเรียกซ้ำตัวแยกวิเคราะห์นิพจน์ทั่วไปที่ไม่มีการเรียกซ้ำจะไม่เพียงพอสำหรับวัตถุประสงค์ มันเป็นโครงสร้างง่ายๆ
ศิลปะสีดำของการแสดงออกปกติเป็นเรื่องยากที่จะโทดังนั้นอาจจะมีความเป็นไปได้ต่อไปเราปล่อยออกมาในขณะที่พยายามและทดสอบวิธีการแก้ปัญหาส่วนบุคคลของเราในการจับภาพทั้งเว็บในมือข้างหนึ่ง ... ดีผมแน่ใจว่าเกี่ยวกับเรื่องนี้ :)
นี่คือรูปแบบมายากล:
$pattern = "/<([\w]+)([^>]*?)(([\s]*\/>)|(>((([^<]*?|<\!\-\-.*?\-\->)|(?R))*)<\/\\1[\s]*>))/s";
แค่ลองดู. มันเขียนเป็นสตริง PHP ดังนั้นโมดิฟายเออร์ "s" ทำให้คลาสรวมขึ้นบรรทัดใหม่
นี่คือตัวอย่างหมายเหตุในคู่มือ PHP ที่ฉันเขียนเมื่อเดือนมกราคม: ข้อมูลอ้างอิง
(โปรดระวังในบันทึกย่อนั้นฉันใช้โมดิฟายเออร์ "m" อย่างไม่ถูกต้องควรลบออกแม้ว่าจะถูกทิ้งโดยเอ็นจินนิพจน์ทั่วไปเนื่องจากไม่มีการใช้^
หรือการ$
ยึด)
ตอนนี้เราสามารถพูดถึงขีด จำกัด ของวิธีนี้จากมุมมองที่มีข้อมูลมากขึ้น:
- ตามการใช้งานเฉพาะของกลไกการแสดงออกปกติการเรียกซ้ำอาจมีข้อ จำกัด ในจำนวนรูปแบบที่ซ้อนกันที่แยกวิเคราะห์ได้ แต่ขึ้นอยู่กับภาษาที่ใช้
- แม้ว่าจะเสียหาย แต่ (x) HTML ก็ไม่ทำให้เกิดข้อผิดพลาดร้ายแรง มันไม่ได้ปรุงแต่ง
อย่างไรก็ตามมันเป็นเพียงรูปแบบการแสดงออกปกติ แต่เปิดเผยถึงความเป็นไปได้ในการพัฒนาการนำไปใช้งานที่มีประสิทธิภาพมากมาย
ฉันเขียนรูปแบบนี้เพื่อเพิ่มพลังให้กับตัวแยกวิเคราะห์การสืบเชื้อสายแบบเรียกซ้ำของเอนจินเทมเพลตที่ฉันสร้างขึ้นในเฟรมเวิร์กของฉันและการแสดงนั้นยอดเยี่ยมมากทั้งในเวลาดำเนินการหรือในการใช้หน่วยความจำ (ไม่มีอะไรเกี่ยวข้องกับเอ็นจิ้นเทมเพลตอื่นที่ใช้ไวยากรณ์เดียวกัน)
อย่างที่หลาย ๆ คนได้ชี้ให้เห็นแล้ว HTML ไม่ใช่ภาษาปกติซึ่งอาจทำให้ยากต่อการแยกวิเคราะห์ วิธีแก้ปัญหาของฉันคือเปลี่ยนเป็นภาษาปกติโดยใช้โปรแกรมที่เป็นระเบียบแล้วใช้ตัวแยกวิเคราะห์ XML เพื่อใช้ผลลัพธ์ มีตัวเลือกที่ดีมากมายสำหรับสิ่งนี้ โปรแกรมของฉันเขียนโดยใช้ Java กับไลบรารีjtidyเพื่อเปลี่ยน HTML เป็น XML จากนั้น Jaxen ถึง xpath เป็นผลลัพธ์
<\s*(\w+)[^/>]*>
ส่วนอธิบาย:
<
: ตัวละครเริ่มต้น
\s*
: อาจมีช่องว่างก่อนชื่อแท็ก (น่าเกลียด แต่เป็นไปได้)
(\w+)
: แท็กสามารถประกอบด้วยตัวอักษรและตัวเลข (h1) ดี\w
ตรงกับ '_' แต่มันไม่เจ็บฉันเดา หากสงสัยให้ใช้ ([a-zA-Z0-9] +) แทน
[^/>]*
: ทุกอย่างยกเว้น>
และ/
จนกว่าจะปิด>
>
: ปิด >
ไม่เกี่ยวข้อง
และสำหรับเพื่อนที่ดูถูกการแสดงออกปกติโดยกล่าวว่าพวกเขามีพลังพอ ๆ กับภาษาทั่วไปเท่านั้น:
a n ba n ba nซึ่งไม่ปกติและไม่มีบริบทฟรีสามารถจับคู่ได้^(a+)b\1b\1$
การอ้างอิงย้อนกลับFTW !
หากคุณเพียงแค่พยายามค้นหาแท็กเหล่านั้น (โดยไม่มีความทะเยอทะยานในการแยกวิเคราะห์) ให้ลองใช้นิพจน์ทั่วไปนี้:
/<[^/]*?>/g
ฉันเขียนมันใน 30 วินาทีและทดสอบที่นี่: http://gskinner.com/RegExr/
ตรงกับประเภทของแท็กที่คุณกล่าวถึงโดยไม่สนใจประเภทที่คุณบอกว่าคุณต้องการละเว้น
สำหรับฉันแล้วดูเหมือนว่าคุณกำลังพยายามจับคู่แท็กโดยไม่มีเครื่องหมาย "/" ต่อท้าย ลองสิ่งนี้:
<([a-zA-Z][a-zA-Z0-9]*)[^>]*(?<!/)>
เป็นความจริงที่ว่าเมื่อเขียนโปรแกรมมักจะดีที่สุดที่จะใช้ตัวแยกวิเคราะห์และ API เฉพาะแทนนิพจน์ทั่วไปเมื่อจัดการกับ HTML โดยเฉพาะอย่างยิ่งหากความถูกต้องเป็นสิ่งสำคัญยิ่ง (เช่นหากการประมวลผลของคุณอาจมีผลกระทบด้านความปลอดภัย) อย่างไรก็ตามฉันไม่ได้อธิบายถึงมุมมองที่เชื่อว่าไม่ควรประมวลผลมาร์กอัปสไตล์ XML ด้วยนิพจน์ทั่วไป มีหลายกรณีที่นิพจน์ทั่วไปเป็นเครื่องมือที่ยอดเยี่ยมสำหรับงานเช่นเมื่อทำการแก้ไขเพียงครั้งเดียวในโปรแกรมแก้ไขข้อความแก้ไขไฟล์ XML ที่ใช้งานไม่ได้หรือจัดการกับรูปแบบไฟล์ที่ดูเหมือน แต่ไม่ใช่ XML มากนัก มีปัญหาบางอย่างที่ต้องระวัง แต่ปัญหาเหล่านี้ผ่านไม่ได้หรือไม่จำเป็นต้องเกี่ยวข้องด้วยซ้ำ
นิพจน์ทั่วไปที่เรียบง่าย<([^>"']|"[^"]*"|'[^']*')*>
มักจะดีพอในบางกรณีเช่นที่ฉันเพิ่งกล่าวถึง เป็นวิธีแก้ปัญหาที่ไร้เดียงสาทุกสิ่งที่พิจารณา แต่อนุญาตให้ใช้>
สัญลักษณ์ที่ไม่ได้เข้ารหัสในค่าแอตทริบิวต์ได้อย่างถูกต้อง หากคุณกำลังมองหาเช่นเป็นแท็กที่คุณสามารถปรับให้เป็น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 อื่น)
ในตอนท้ายของวันให้ใช้เครื่องมือที่เหมาะสมที่สุดสำหรับงานแม้ในกรณีที่เครื่องมือนั้นเป็นนิพจน์ทั่วไป
แม้ว่าการใช้นิพจน์ทั่วไปเพื่อจุดประสงค์นั้นจะไม่เหมาะสมและมีประสิทธิภาพในบางครั้งนิพจน์ทั่วไปก็ให้วิธีแก้ปัญหาอย่างรวดเร็วสำหรับปัญหาการจับคู่แบบง่าย ๆ และในมุมมองของฉันมันไม่ใช่เรื่องน่ากลัวที่จะใช้นิพจน์ทั่วไปสำหรับงานเล็กน้อย
มีบล็อกโพสต์ที่ชัดเจนเกี่ยวกับการจับคู่องค์ประกอบ HTML ด้านในสุดที่เขียนโดย Steven Levithan