เหตุใดจึงไม่สามารถใช้ regex เพื่อแยกวิเคราะห์ HTML / XML: คำอธิบายอย่างเป็นทางการในแง่ของคนธรรมดา


117

ไม่มีวันใดใน SO ที่ผ่านไปโดยไม่มีคำถามเกี่ยวกับการแยกวิเคราะห์ (X) HTML หรือ XML ด้วยนิพจน์ทั่วไปที่ถูกถาม

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

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

ข้อบกพร่องที่นี่คือ HTML เป็นไวยากรณ์ Chomsky Type 2 (ไวยากรณ์ฟรีบริบท) และ RegEx เป็นไวยากรณ์ Chomsky Type 3 (นิพจน์ทั่วไป)

หรือ:

นิพจน์ทั่วไปจับคู่ได้เฉพาะภาษาปกติ แต่ HTML เป็นภาษาที่ไม่มีบริบท

หรือ:

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

หรือ:

คำศัพท์ Pumping สำหรับภาษาทั่วไปคือสาเหตุที่คุณไม่สามารถทำเช่นนั้นได้

[เพื่อความเป็นธรรม: คำอธิบายข้างต้นส่วนใหญ่เชื่อมโยงไปยังหน้าวิกิพีเดีย แต่สิ่งเหล่านี้ไม่ง่ายต่อการเข้าใจมากกว่าคำตอบ]

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

แก้ไข:หลังจากอ่านคำตอบแรกแล้วฉันคิดว่าฉันควรจะชี้แจง: ฉันกำลังมองหา "การแปล" ที่อธิบายแนวคิดที่พยายามแปลโดยสังเขป: ในตอนท้ายของคำตอบผู้อ่านควรมีความคิดคร่าวๆ - ตัวอย่างเช่น - "ภาษาปกติ" และ "ไวยากรณ์ที่ไม่มีบริบท" หมายถึงอะไร ...


19
โปรดทราบว่าในแง่ของวิทยาการคอมพิวเตอร์ "นิพจน์ทั่วไป" แตกต่างอย่างมากจาก "การใช้งาน regex" ในปัจจุบัน (เครื่องมือ / API ที่คุณใช้ในภาษาโปรแกรม) กลุ่มหลังนี้สามารถ "จำ" สิ่งที่พวกเขาพบเจอและยังสามารถจับคู่รูปแบบที่กำหนดซ้ำ (ย่อย) ทำให้ตรงกัน / แยกวิเคราะห์ / จดจำได้มากกว่า "นิพจน์ทั่วไป" ตามทฤษฎี
Bart Kiers

1
@Bart: สิ่งนี้ใช้ได้กับภาษาที่ใช้คำว่า "นิพจน์ทั่วไปเท่านั้น POSIX ERE เป็นภาษาปกติอย่างแท้จริง
R .. GitHub STOP HELPING ICE

2
@R .. ดังนั้นคุณเรียก POSIX ว่า "การใช้งานสมัยใหม่": P. ด้วยความจริงจังทั้งหมด: ใช่คุณพูดถูกคนเหล่านั้นเป็นเรื่องปกติอย่างแท้จริง ฉันควรจะได้กล่าวว่า"... หลายคนที่ทันสมัยใช้งานวัน regex ..."หรือ"... PCRE regex การใช้งาน ..."
Bart Kiers

4
ฉันมีช่วงเวลาที่ยากลำบากในการใช้ภาษาโปรแกรมอย่างจริงจังซึ่งโดยพื้นฐานแล้วใช้ภาษาที่เข้มงวดในทางที่ผิดเพื่อประโยชน์ในการทำการตลาดให้กับโปรแกรมเมอร์ที่ไม่รู้เรื่อง ...
R .. GitHub STOP HELPING ICE

3
@R .. โชคไม่ดีที่การใช้งาน PCRE เรียกว่า "นิพจน์ทั่วไป" แต่การไม่ใช้ภาษาอย่างจริงจังนั้นเป็นการก้าวไปไกลเกินกว่าขั้นตอนเดียว IMO ฉันหมายความว่าคุณไม่ได้ใช้ Perl, Java, Python, Ruby, JavaScript, .NET ฯลฯ ไม่ร้ายแรงเพราะเหตุนี้หรือ?
Bart Kiers

คำตอบ:


117

ให้ความสำคัญกับสิ่งนี้:

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

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

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

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


นอกจากนี้ยังมีคำอธิบายที่ค่อนข้างดีเกี่ยวกับ จำกัด ออโตมาตาที่นี่: youtube.com/watch?v=vhiiia1_hC4
GDP2

55

ความจริงที่ HTML ไม่ได้เป็นตัวแทนของภาษาปกติคือปลาชนิดหนึ่งสีแดง นิพจน์ทั่วไปและภาษาปกติฟังดูคล้ายกันแต่ไม่ใช่ - พวกเขามีแหล่งกำเนิดเดียวกัน แต่มีระยะห่างที่น่าสังเกตระหว่าง "ภาษาปกติ" ทางวิชาการกับพลังการจับคู่ปัจจุบันของเอ็นจิ้น ในความเป็นจริงเกือบทุกที่ทันสมัยเครื่องมือแสดงออกปกติสนับสนุนคุณสมบัติที่ไม่ปกติ - (.*)\1ตัวอย่างง่ายๆคือ ซึ่งใช้ backreferencing เพื่อให้ตรงกับลำดับซ้ำของตัวละคร - ตัวอย่างหรือ123123 bonbonการจับคู่โครงสร้างแบบวนซ้ำ / สมดุลทำให้สิ่งเหล่านี้สนุกยิ่งขึ้น

Wikipedia กล่าวไว้อย่างสวยงามในคำพูดของLarry Wall :

'นิพจน์ทั่วไป' [... ] เกี่ยวข้องเพียงเล็กน้อยกับนิพจน์ทั่วไปจริง อย่างไรก็ตามคำนี้เติบโตขึ้นพร้อมกับความสามารถของเครื่องมือจับคู่รูปแบบของเราดังนั้นฉันจะไม่พยายามต่อสู้กับความจำเป็นทางภาษาที่นี่ อย่างไรก็ตามโดยทั่วไปฉันจะเรียกพวกเขาว่า "regexes" (หรือ "regexen" เมื่อฉันอยู่ในอารมณ์แองโกล - แซกซอน)

"นิพจน์ทั่วไปจับคู่ได้เฉพาะภาษาทั่วไปเท่านั้น" อย่างที่คุณเห็นไม่มีอะไรมากไปกว่าการเข้าใจผิดที่ระบุไว้โดยทั่วไป

แล้วทำไมไม่ล่ะ?

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

  • HTML ที่ถูกต้องนั้นยาก / ซับซ้อนกว่าที่คุณคิด
  • HTML ที่ "ถูกต้อง" มีหลายประเภท - สิ่งที่ใช้ได้ใน HTML เช่นไม่ถูกต้องใน XHTML
  • มากของฟรีฟอร์ม HTML พบบนอินเทอร์เน็ตเป็นไม่ได้อยู่แล้วที่ถูกต้อง ไลบรารี HTML ทำงานได้ดีในการจัดการกับสิ่งเหล่านี้เช่นกันและได้รับการทดสอบในหลายกรณีทั่วไปเหล่านี้
  • บ่อยครั้งที่เป็นไปไม่ได้ที่จะจับคู่ส่วนหนึ่งของข้อมูลโดยไม่แยกวิเคราะห์ข้อมูลทั้งหมด ตัวอย่างเช่นคุณอาจมองหาชื่อเรื่องทั้งหมดและลงเอยด้วยการจับคู่ภายในความคิดเห็นหรือสตริงตามตัวอักษร <h1>.*?</h1>อาจเป็นความพยายามอย่างกล้าหาญในการค้นหาชื่อเรื่องหลัก แต่อาจพบ:

    <!-- <h1>not the title!</h1> -->

    หรือแม้กระทั่ง:

    <script>
    var s = "Certainly <h1>not the title!</h1>";
    </script>

ประเด็นสุดท้ายสำคัญที่สุด:

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

บทสรุปที่ดีของเรื่องและแสดงความคิดเห็นที่สำคัญเมื่อผสม Regex และ HTML อาจจะเหมาะสมที่สามารถพบได้ในบล็อกของเจฟฟ์แอด: แยก Html ธู Way

เมื่อใดควรใช้นิพจน์ทั่วไปเพื่อแยกวิเคราะห์ HTML

ในกรณีส่วนใหญ่ควรใช้ XPath กับโครงสร้าง DOM ที่ไลบรารีสามารถให้คุณได้ดีกว่า อย่างไรก็ตามสำหรับความคิดเห็นที่เป็นที่นิยมมีบางกรณีที่ฉันอยากแนะนำอย่างยิ่งให้ใช้ regex ไม่ใช่ไลบรารีตัวแยกวิเคราะห์

กำหนดเงื่อนไขบางประการเหล่านี้:

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

4
นี่เป็นงานเขียนที่ชัดเจนและสวยงามมากเมื่อ (ไม่ถึง) ใช้ regex ในการแยกวิเคราะห์ HTML แต่ก็แทบจะไม่ได้คำตอบสำหรับคำถามของฉัน ฉันขอแนะนำให้คุณย้ายไปที่คำถามนี้แทนได้ไหม ฉันคิดว่ามันจะทำให้คุณมีชื่อเสียงมากขึ้นที่นั่น แต่ - เหนือสิ่งอื่นใด - ฉันคิดว่านั่นจะเป็นสถานที่ที่ผู้เยี่ยมชมในอนาคตจะพบว่ามีความเกี่ยวข้องมากขึ้น (มีความคิดเห็นโดย @Bart Kiers สำหรับคำถามของฉันที่เตือนผู้เข้าชมถึง "พลังพิเศษ" ของเครื่องยนต์ regex สมัยใหม่)
แม็ค

1
@mac - ขอบคุณมาก ที่จริงฉันลองคิดดู ฉันรู้ว่าฉันไม่ได้ตอบคำถามของคุณ แต่ฉันไม่คิดว่าคำถามนั้นถูกต้องโดยพื้นฐาน - คุณขอให้อธิบายเหตุผลที่ผิด ... คุณมีความคิดที่ดีบางทีคำถามอื่นอาจจะเหมาะสมกว่า ...
Kobi

19

เนื่องจาก HTML สามารถมีการซ้อนกันได้ไม่ จำกัด<tags><inside><tags and="<things><that><look></like></tags>"></inside></each></other>และ regex ไม่สามารถรับมือกับสิ่งนั้นได้เนื่องจากไม่สามารถติดตามประวัติของสิ่งที่สืบเชื้อสายมาและออกมาได้

โครงสร้างง่ายๆที่แสดงให้เห็นถึงความยาก:

<body><div id="foo">Hi there!  <div id="bar">Bye!</div></div></body>

99.9% ของรูทีนการดึงข้อมูลที่ใช้นิพจน์ทั่วไปจะไม่สามารถให้ทุกอย่างในdivID ได้อย่างถูกต้องfooเนื่องจากไม่สามารถบอกแท็กปิดสำหรับ div นั้นจากแท็กปิดสำหรับbardiv ได้ นั่นเป็นเพราะพวกเขาไม่มีทางพูดว่า "โอเคตอนนี้ฉันสืบเชื้อสายมาเป็นสองในสองหารแล้วดังนั้นการปิด div ถัดไปที่ฉันเห็นจะดึงฉันกลับออกมาหนึ่งและอันหลังจากนั้นคือแท็กปิดสำหรับอันแรก" . โดยทั่วไปโปรแกรมเมอร์จะตอบสนองโดยการคิด regexes กรณีพิเศษสำหรับสถานการณ์ที่เฉพาะเจาะจงซึ่งจะทำลายทันทีที่มีการนำแท็กเข้ามาภายในมากขึ้นfooและต้องเสียค่าใช้จ่ายจำนวนมากในเวลาและความยุ่งยาก นี่คือสาเหตุที่ผู้คนคลั่งไคล้ในเรื่องทั้งหมด


1
ขอบคุณคำตอบ แต่คำถามของฉันไม่ใช่ "ทำไมฉันไม่สามารถใช้ regex ... " คำถามของฉันเกี่ยวกับการ "แปล" คำอธิบายอย่างเป็นทางการที่ฉันให้ไว้! :)
แมค

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

ขออภัยบางทีฉันอาจไม่ชัดเจนในคำถามของฉัน (ยินดีต้อนรับคำแนะนำสำหรับการปรับปรุงแก้ไข!) แต่ฉันกำลังมองหาคำตอบที่อธิบาย "การแปล" ด้วย คำตอบของคุณไม่ได้ชี้แจงแนวคิดของ 'ภาษาปกติ' หรือ 'ภาษาที่ไม่มีบริบท' ...
แมค

5
การอธิบายคำศัพท์เหล่านั้นจะเป็นเรื่องทางเทคนิคพอ ๆ กับศัพท์แสงและการเบี่ยงเบนความสนใจจากความหมายที่แท้จริงที่ภาษาที่มีความแม่นยำทั้งหมดกำลังเกิดขึ้นนั่นคือสิ่งที่ฉันโพสต์
Ianus Chiaroscuro

4
<(\w+)(?:\s+\w+="[^"]*")*>(?R)*</\1>|[\w\s!']+ตรงกับตัวอย่างโค้ดของคุณ
Kobi

9

ภาษาปกติคือภาษาที่สามารถจับคู่ได้โดยเครื่องที่มีสถานะ จำกัด

(การทำความเข้าใจเครื่องจักร Finite State เครื่องกดลงและเครื่องทัวริงเป็นหลักสูตรของหลักสูตร CS ของวิทยาลัยปีที่สี่)

พิจารณาเครื่องต่อไปนี้ซึ่งรู้จักสตริง "hi"

(Start) --Read h-->(A)--Read i-->(Succeed)
  \                  \
   \                  -- read any other value-->(Fail) 
    -- read any other value-->(Fail)

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

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


2
"การทำความเข้าใจเครื่องจักร Finite State เครื่องกดลงและเครื่องทัวริงเป็นหลักสูตรของหลักสูตร CS ระดับ 300" ฉันเข้าใจว่านี่เป็นความพยายามที่จะระบุว่าหัวข้อนั้นยาก / ก้าวหน้าเพียงใด แต่ฉันไม่คุ้นเคยกับระบบโรงเรียนที่คุณอ้างถึงโปรดชี้แจงด้วยวิธีที่ไม่เจาะจงประเทศได้หรือไม่ ขอบคุณ! :)
แม็ค

1
ฉันได้อัปเดตแล้ว ฉันไม่รู้ว่ามันยากเกินไปที่จะเข้าใจเพียงแค่อธิบายในโพสต์สแตกล้น
Sean McMillan

6

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

ในการแยกวิเคราะห์ XML, C หรือภาษาอื่น ๆ ด้วยการซ้อนองค์ประกอบภาษาโดยพลการคุณต้องจำไว้ว่าคุณอยู่ลึกแค่ไหน นั่นคือคุณต้องสามารถนับวงเล็บปีกกา / วงเล็บ / แท็กได้

คุณไม่สามารถนับด้วยหน่วยความจำ จำกัด อาจมีระดับรั้งมากกว่าที่คุณมี! คุณอาจสามารถแยกวิเคราะห์ภาษาของคุณบางส่วนที่ จำกัด จำนวนระดับการซ้อนกันได้ แต่มันจะน่าเบื่อมาก


6

ไวยากรณ์เป็นคำจำกัดความอย่างเป็นทางการว่าคำสามารถไปไหนได้บ้าง ยกตัวอย่างเช่นคำคุณศัพท์ preceed นามแต่ทำตามคำนามin English grammar en la gramática españolaไม่มีบริบทหมายความว่าแกรมมาร์ทั่วโลกในทุกบริบท ตามบริบทหมายถึงมีกฎเพิ่มเติมในบางบริบท

ใน C #, ตัวอย่างเช่นusingหมายถึงสิ่งที่แตกต่างกันในที่ด้านบนของไฟล์กว่าusing System; using (var sw = new StringWriter (...))ตัวอย่างที่เกี่ยวข้องเพิ่มเติมคือโค้ดต่อไปนี้ภายในโค้ด:

void Start ()
{
    string myCode = @"
    void Start()
    {
       Console.WriteLine (""x"");
    }
    ";
}

นี่เป็นคำตอบที่เข้าใจได้
บุคคลที่

แต่ไม่มีบริบทไม่ได้หมายความว่าปกติ ภาษาของ paranthesis ที่ตรงกันนั้นไม่มีบริบท แต่ไม่ใช่ภาษาปกติ
Taemyr

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

4

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

ตัวอย่างเช่นการเขียนนิพจน์ทั่วไปเพื่อจับคู่ได้เป็นอย่างดี

<price>10.65</price>

แต่ถ้ารหัสของคุณถูกต้องแล้ว:

  • ต้องมีช่องว่างหลังชื่อองค์ประกอบทั้งในแท็กเริ่มต้นและแท็กปิดท้าย

  • หากเอกสารอยู่ในเนมสเปซควรอนุญาตให้ใช้คำนำหน้าเนมสเปซได้

  • ควรอนุญาตและละเว้นแอตทริบิวต์ที่ไม่รู้จักที่ปรากฏในแท็กเริ่มต้น (ขึ้นอยู่กับความหมายของคำศัพท์เฉพาะ)

  • อาจต้องเผื่อช่องว่างก่อนและหลังค่าทศนิยม (อีกครั้งขึ้นอยู่กับกฎโดยละเอียดของคำศัพท์ XML เฉพาะ)

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

  • อาจจำเป็นต้องให้การวินิจฉัยหากอินพุตไม่ถูกต้อง

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


2

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

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

พิจารณาตัวอย่างเช่น

(?:
    <!\-\-[\S\s]*?\-\->
    |
    <([\w\-\.]+)[^>]*?
    (?:
        \/>
        |
        >
        (?:
            [^<]
            |
            (?R)
        )*
        <\/\1>
    )
)

สิ่งนี้จะค้นหาแท็ก XML หรือความคิดเห็นที่ถูกต้องถัดไปและจะพบได้ก็ต่อเมื่อมีการสร้างเนื้อหาทั้งหมดอย่างถูกต้อง (นิพจน์นี้ได้รับการทดสอบโดยใช้ Notepad ++ ซึ่งใช้ไลบรารี regex ของ Boost C ++ ซึ่งใกล้เคียงกับ PCRE อย่างใกล้ชิด)

นี่คือวิธีการทำงาน:

  1. กลุ่มแรกตรงกับความคิดเห็น สิ่งนี้จำเป็นต้องมาก่อนเพื่อที่จะจัดการกับรหัสที่แสดงความคิดเห็นซึ่งอาจทำให้เกิดการแฮงค์ได้
  2. หากไม่ตรงกันระบบจะมองหาจุดเริ่มต้นของแท็ก โปรดทราบว่าจะใช้วงเล็บเพื่อจับชื่อ
  3. แท็กนี้จะลงท้ายด้วย a />ทำให้แท็กเสร็จสมบูรณ์หรือจะลงท้ายด้วย a >ซึ่งในกรณีนี้แท็กจะดำเนินต่อไปโดยตรวจสอบเนื้อหาของแท็ก
  4. ระบบจะแยกวิเคราะห์ต่อไปจนกว่าจะถึงจุดหนึ่ง<ซึ่งจะเรียกคืนกลับไปที่จุดเริ่มต้นของนิพจน์เพื่อให้สามารถจัดการกับความคิดเห็นหรือแท็กใหม่ได้
  5. มันจะดำเนินต่อไปเรื่อย ๆ จนกว่าจะมาถึงจุดสิ้นสุดของข้อความหรือเมื่อ<ไม่สามารถแยกวิเคราะห์ได้ แน่นอนว่าการไม่ตรงกับเจตจำนงทำให้ต้องเริ่มกระบวนการใหม่ มิฉะนั้น<สันนิษฐานว่าเป็นจุดเริ่มต้นของแท็กปิดสำหรับการวนซ้ำนี้ การใช้การอ้างอิงย้อนกลับภายในแท็กปิด<\/\1>จะตรงกับแท็กเปิดสำหรับการวนซ้ำปัจจุบัน (ความลึก) มีกลุ่มจับเพียงกลุ่มเดียวดังนั้นการจับคู่ครั้งนี้จึงเป็นเรื่องธรรมดา สิ่งนี้ทำให้เป็นอิสระจากชื่อของแท็กที่ใช้แม้ว่าคุณจะสามารถแก้ไขกลุ่มการจับภาพเพื่อจับเฉพาะแท็กที่เฉพาะเจาะจงได้หากคุณต้องการ
  6. ณ จุดนี้มันจะเริ่มต้นจากการเรียกซ้ำในปัจจุบันขึ้นไปอีกระดับหรือจบลงด้วยการแข่งขัน

ตัวอย่างนี้แก้ไขปัญหาในการจัดการกับช่องว่างหรือการระบุเนื้อหาที่เกี่ยวข้องผ่านการใช้กลุ่มอักขระที่เป็นเพียงการปฏิเสธ<หรือ>หรือในกรณีของความคิดเห็นโดยการใช้[\S\s]ซึ่งจะจับคู่อะไรก็ได้รวมถึงการคืนค่าขนส่งและการขึ้นบรรทัดใหม่แม้ในบรรทัดเดียว โหมดต่อไปจนกว่าจะถึง -->. ดังนั้นจึงถือว่าทุกอย่างถูกต้องจนกว่าจะถึงสิ่งที่มีความหมาย

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

เรื่องสั้นขนาดยาว: ใช้ตัวแยกวิเคราะห์ XML สำหรับงานจริงและใช้สิ่งนี้หากคุณต้องการเล่นกับ regexes


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

2
นอกจากนี้ยังมีอินพุตที่มีรูปแบบดีที่ regex ไม่ตรงกัน ตัวอย่างเช่นไม่อนุญาตให้เว้นวรรคหลังชื่อในแท็กปิดท้าย ข้อบกพร่องเหล่านี้ส่วนใหญ่แก้ไขได้ง่าย แต่เมื่อคุณแก้ไขข้อบกพร่องทั้งหมดแล้วคุณจะพบสิ่งที่ใช้ไม่ได้ทั้งหมด และแน่นอนว่า gotcha ที่แท้จริงก็คือคุณไม่เพียง แต่ต้องการให้ตัวแยกวิเคราะห์ให้คำตอบใช่ / ไม่ใช่คุณต้องการให้มันส่งข้อมูลไปยังแอปพลิเคชันที่ทำสิ่งที่เป็นประโยชน์กับมัน
Michael Kay

0

อย่าแยกวิเคราะห์ XML / HTML ด้วย regex ให้ใช้ตัวแยกวิเคราะห์ XML / HTML ที่เหมาะสมและมีประสิทธิภาพ สอบถาม

ทฤษฎี:

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

realLife ©®™เครื่องมือในชีวิตประจำวันในไฟล์ :

คุณสามารถใช้สิ่งใดสิ่งหนึ่งต่อไปนี้:

xmllintมักจะติดตั้งโดยค่าเริ่มต้นด้วยlibxml2xpath1 (ตรวจสอบว่า wrapper ของฉันมีเอาต์พุตที่คั่นด้วยบรรทัดใหม่

xmlstarletสามารถแก้ไขเลือกแปลง ... ไม่ได้ติดตั้งโดยค่าเริ่มต้น xpath1

xpathติดตั้งผ่านโมดูล XML ของ perl :: XPath, xpath1

xidel xpath 3

saxon-lintโครงการของฉันเองห่อหุ้มด้วยห้องสมุด Saxon-HE Java ของ Michael Kay, xpath3

หรือคุณสามารถใช้ภาษาระดับสูงและ libs ที่เหมาะสมฉันนึกถึง:

ของlxml( from lxml import etree)

's XML::LibXML, XML::XPath, XML::Twig::XPath,HTML::TreeBuilder::XPath

, ตรวจสอบตัวอย่างนี้

DOMXpath, ตรวจสอบตัวอย่างนี้


ตรวจสอบ: การใช้นิพจน์ทั่วไปกับแท็ก HTML

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