นี่คือคำสั่ง perlreguts ที่สามารถเรียกใช้ในผู้ให้บริการโฮสติ้งฟรีของ OnWorks โดยใช้หนึ่งในเวิร์กสเตชันออนไลน์ฟรีของเรา เช่น Ubuntu Online, Fedora Online, โปรแกรมจำลองออนไลน์ของ Windows หรือโปรแกรมจำลองออนไลน์ของ MAC OS
โครงการ:
ชื่อ
perlreguts - คำอธิบายของเอ็นจินนิพจน์ทั่วไปของ Perl
DESCRIPTION
เอกสารนี้เป็นความพยายามที่จะส่องแสงให้กับความกล้าของเอ็นจิ้น regex และวิธีการ
ทำงาน เอ็นจิ้น regex แสดงถึงส่วนสำคัญของโค้ดเบส perl แต่คือ
ค่อนข้างเข้าใจได้ไม่ดี เอกสารนี้เป็นความพยายามเพียงเล็กน้อยในการแก้ไขปัญหานี้
สถานการณ์. มาจากประสบการณ์ของผู้เขียน ข้อคิดเห็นในซอร์สโค้ด อื่นๆ
เอกสารเกี่ยวกับเอ็นจิ้น regex ข้อเสนอแนะเกี่ยวกับรายชื่อเมล perl5-porters และไม่ต้องสงสัยอื่น ๆ
สถานที่เช่นกัน
ข้อสังเกต! ควรเข้าใจให้ชัดเจนว่าพฤติกรรมและโครงสร้างที่กล่าวถึงในเรื่องนี้
แสดงถึงสถานะของเครื่องยนต์ตามที่ผู้เขียนเข้าใจในขณะที่เขียน มัน
is ไม่ คำจำกัดความ API เป็นคู่มือภายในสำหรับผู้ที่ต้องการแฮ็ค
โปรแกรม regex หรือทำความเข้าใจว่าเอ็นจิ้น regex ทำงานอย่างไร ผู้อ่านเอกสารนี้คือ
คาดว่าจะเข้าใจไวยากรณ์ regex ของ Perl และการใช้งานโดยละเอียด หากคุณต้องการเรียนรู้
เกี่ยวกับพื้นฐานของนิพจน์ทั่วไปของ Perl โปรดดูที่ perlre และถ้าคุณต้องการเปลี่ยน
regex engine ของคุณเอง ดู perlreapi
ภาพรวม
A รวดเร็ว หมายเหตุ on เงื่อนไขการใช้บริการ
มีการถกเถียงกันว่าควรจะพูดว่า "regexp" หรือ "regex" ในเอกสารนี้เราจะ
ใช้คำว่า "regex" เว้นแต่จะมีเหตุผลพิเศษที่จะไม่ทำ ในกรณีนี้เราจะ
อธิบายว่าทำไม.
เมื่อพูดถึง regexes เราต้องแยกความแตกต่างระหว่างรูปแบบซอร์สโค้ดกับ
แบบฟอร์มภายในของพวกเขา ในเอกสารนี้ เราจะใช้คำว่า "รูปแบบ" เมื่อเราพูดถึง
รูปแบบข้อความ ซอร์สโค้ด และคำว่า "โปรแกรม" เมื่อเราพูดถึงเรื่องภายใน
การเป็นตัวแทน สิ่งเหล่านี้สอดคล้องกับข้อกำหนด S-regex และ B-regex ว่า มาร์ค เจสัน โดมินัส
ใช้ในบทความเรื่อง "Rx" ([1] ใน "REFERENCES")
อะไร is a ปกติ การแสดงออก เครื่องยนต์?
เอ็นจิ้นนิพจน์ทั่วไปคือโปรแกรมที่ใช้ชุดของข้อจำกัดที่ระบุในa
ภาษาขนาดเล็ก แล้วนำข้อจำกัดเหล่านั้นไปใช้กับสตริงเป้าหมาย และกำหนด
ไม่ว่าสตริงจะเป็นไปตามข้อจำกัดหรือไม่ See perlre สำหรับคำนิยามแบบเต็มของ
ภาษา.
ในแง่ที่ไม่ค่อยโอ่อ่า ส่วนแรกของงานคือการเปลี่ยนรูปแบบเป็นสิ่งที่
คอมพิวเตอร์สามารถใช้หาจุดตรงกันในสตริงและส่วนที่สองได้อย่างมีประสิทธิภาพ
กำลังดำเนินการค้นหาเอง
ในการทำเช่นนี้ เราจำเป็นต้องสร้างโปรแกรมโดยแยกวิเคราะห์ข้อความ จากนั้นเราต้องดำเนินการ
โปรแกรมหาจุดในสตริงที่ตรงกัน และเราต้องทำให้เต็มที่
อย่างมีประสิทธิภาพ
โครงสร้าง of a นิพจน์ทั่วไป โครงการ
จุดสูง ชั้น
แม้ว่ามันจะค่อนข้างสับสนและบางคนคัดค้านคำศัพท์ แต่ก็คุ้มค่า
กำลังดูความคิดเห็นที่ได้รับใน regexp.h เป็นเวลาหลายปี:
is เป็นหลัก a เชิงเส้น การเข้ารหัส of a ไม่กำหนด จำกัดสถานะ เครื่อง (หรือที่รู้จัก
วากยสัมพันธ์ แผนภูมิ or "ทางรถไฟ ปกติ รูปร่าง" in การแยกวิเคราะห์ เทคโนโลยี)
คำว่า "รูปแบบปกติของทางรถไฟ" ค่อนข้างลึกลับ โดยมี "แผนภาพ/แผนภูมิไวยากรณ์" หรือ
"แผนภาพรถไฟ/แผนภูมิ" เป็นคำศัพท์ทั่วไป อย่างไรก็ตามมันให้ประโยชน์
ภาพจิตของโปรแกรม regex: แต่ละโหนดสามารถคิดได้ว่าเป็นหน่วยของแทร็กด้วย a
ทางเข้าเดียวและในกรณีส่วนใหญ่เป็นจุดออกเดียว (มีทางแยกที่แยกจากกัน
แต่มีไม่มากนักทางสถิติ) และทั้งรูปแบบเป็นเลย์เอาต์ที่มีรายการเดียวและรายการเดียว
จุดทางออก กระบวนการจับคู่สามารถคิดได้ว่าเป็นรถที่เคลื่อนที่ไปตามราง
โดยเส้นทางเฉพาะผ่านระบบจะถูกกำหนดโดยตัวอักษรอ่านที่
แต่ละจุดเชื่อมต่อที่เป็นไปได้ รถตกจากรางได้ทุกเมื่อ แต่อาจเท่านั้น
ดำเนินการตราบเท่าที่มันตรงกับแทร็ก
ดังนั้นรูปแบบ "/foo(?:\w+|\d+|\s+)bar/" สามารถคิดได้เป็นแผนภูมิต่อไปนี้:
[เริ่ม]
|
|
-
- - -
<\w+> <\d+> <\s+>
- - -
-
|
|
[จบ]
ความจริงก็คือนิพจน์ทั่วไปของ Perl ในปัจจุบันมีมากขึ้น
ซับซ้อนกว่าโครงสร้างแบบนี้ แต่การมองเห็นด้วยวิธีนี้สามารถช่วยได้เมื่อพยายาม
รับแบริ่งของคุณและตรงกับการใช้งานในปัจจุบันอย่างใกล้ชิด
เพื่อให้แม่นยำยิ่งขึ้น เราจะบอกว่าโปรแกรม regex คือการเข้ารหัสของกราฟ แต่ละโหนด
ในกราฟสอดคล้องกับส่วนของรูปแบบ regex เดิม เช่น สตริงตามตัวอักษร
หรือสาขาและมีตัวชี้ไปยังโหนดที่เป็นตัวแทนขององค์ประกอบถัดไปที่จะจับคู่
เนื่องจาก "node" และ "opcode" มีความหมายอื่นอยู่แล้วในแหล่ง Perl เราจะเรียก
โหนดในโปรแกรม regex "regops"
โปรแกรมแสดงด้วยอาร์เรย์ของโครงสร้าง "regnode" ซึ่งอย่างน้อยหนึ่งรายการ
เป็นตัวแทนของ regop เดียวของโปรแกรม โครงสร้าง "regnode" เป็นโครงสร้างที่เล็กที่สุดที่จำเป็น
และมีโครงสร้างสนามซึ่งใช้ร่วมกับโครงสร้างขนาดใหญ่อื่นๆ ทั้งหมด
พอยน์เตอร์ "ถัดไป" ของ regops ทั้งหมดยกเว้น "BRANCH" ใช้การต่อกัน "ต่อไป"
ตัวชี้ที่มี "สาขา" ที่ปลายทั้งสองของมันคือการเชื่อมต่อสองทางเลือก [ที่นี่เรามี
หนึ่งในการพึ่งพาไวยากรณ์ที่ละเอียดอ่อน: บุคคล "BRANCH" (ตรงข้ามกับคอลเล็กชัน
ของพวกเขา) ไม่เคยเชื่อมต่อกับสิ่งใดเนื่องจากลำดับความสำคัญของตัวดำเนินการ]
ตัวถูกดำเนินการของ regop บางประเภทเป็นสตริงตามตัวอักษร สำหรับคนอื่น ๆ มันเป็น regop ชั้นนำ
ลงในโปรแกรมย่อย โดยเฉพาะอย่างยิ่ง ตัวถูกดำเนินการของโหนด "สาขา" คือ regop แรกของ
สาขา.
หมายเหตุ: ตามที่อุปมาทางรถไฟบอกไว้ นี่คือ ไม่ โครงสร้างต้นไม้: หางของ
branch เชื่อมต่อกับสิ่งที่ตามหลังชุดของ "BRANCH" ก็เหมือนเส้นเดียว
ของรางรถไฟที่แยกเมื่อเข้าไปในสถานีหรือลานรถไฟแล้วมาบรรจบกันเหมือนเดิม
ออกมาอีกด้านหนึ่ง
ลงทะเบียน
โครงสร้างพื้นฐานของ regop ถูกกำหนดใน regexp.h ดังต่อไปนี้:
โครงสร้าง regnode {
ธง U8; /* วัตถุประสงค์ต่าง ๆ บางครั้งถูกแทนที่ */
ประเภท U8; /* ค่า Opcode ตามที่ระบุโดย regnodes.h */
U16 next_off; /* ออฟเซ็ตใน regnode ขนาด */
};
โครงสร้างคล้าย "เร็กโนด" ที่มีขนาดใหญ่กว่าถูกกำหนดไว้ใน regcomp.h. พวกเขาเกือบจะเหมือน
คลาสย่อยที่มีฟิลด์เดียวกันกับ "regnode" โดยอาจมีฟิลด์เพิ่มเติม
ตามโครงสร้างและในบางกรณีความหมายเฉพาะ (และชื่อ) ของ . บางอย่าง
ฟิลด์ฐานถูกแทนที่ ต่อไปนี้เป็นคำอธิบายที่สมบูรณ์ยิ่งขึ้น
"regnode_1"
"regnode_2"
โครงสร้าง "regnode_1" มีส่วนหัวเหมือนกัน ตามด้วยอาร์กิวเมนต์สี่ไบต์เดียว
โครงสร้าง "regnode_2" มีอาร์กิวเมนต์สองไบต์แทน:
regnode_1 U32 arg1;
regnode_2 U16 arg1; U16 อาร์จี2;
"regnode_string"
โครงสร้าง "regnode_string" ที่ใช้สำหรับสตริงตามตัวอักษร ทำตามส่วนหัวด้วยหนึ่ง-
ความยาวไบต์แล้วข้อมูลสตริง สตริงถูกเสริมที่ส่วนท้ายด้วยศูนย์ไบต์ดังนั้น
ว่าความยาวทั้งหมดของโหนดนั้นมีหลายสี่ไบต์:
regnode_string ถ่านสตริง [1];
U8 str_len; /* แทนที่แฟล็ก */
"regnode_charclass"
คลาสอักขระวงเล็บแสดงโดยโครงสร้าง "regnode_charclass" ซึ่ง
มีอาร์กิวเมนต์สี่ไบต์และบิตแมป 32 ไบต์ (256 บิต) ที่ระบุซึ่ง
อักขระในช่วง Latin1 จะรวมอยู่ในชั้นเรียน
regnode_charclass U32 arg1;
ถ่านบิตแมป[ANYOF_BITMAP_SIZE];
ธงต่างๆ ที่มีชื่อขึ้นต้นด้วย "ANYOF_" ใช้สำหรับสถานการณ์พิเศษ ข้างต้น
การแข่งขัน Latin1 และสิ่งต่าง ๆ ที่ไม่รู้จักจนกระทั่งรันไทม์ถูกเก็บไว้ใน "Perl's pprivate
โครงสร้าง".
"regnode_charclass_posixl"
นอกจากนี้ยังมีรูปแบบที่ใหญ่กว่าของโครงสร้างคลาสถ่านที่ใช้แทน POSIX char
คลาสภายใต้การจับคู่ "/l" เรียกว่า "regnode_charclass_posixl" ซึ่งมีเพิ่มเติม
บิตแมป 32 บิตระบุว่ามีคลาสถ่าน POSIX ใดบ้าง
regnode_charclass_posixl U32 arg1;
ถ่านบิตแมป[ANYOF_BITMAP_SIZE];
ธงคลาส U32;
regnodes.h กำหนดอาร์เรย์ที่เรียกว่า "regarglen[]" ซึ่งให้ขนาดของแต่ละ opcode ใน
หน่วยของ "ขนาด regnode" (4 ไบต์) มาโครใช้ในการคำนวณขนาดของโหนด "EXACT"
ตามฟิลด์ "str_len"
regops ถูกกำหนดใน regnodes.h ซึ่งเกิดจาก regcomp.sym by regcomp.pl.
ปัจจุบันจำนวนการทำซ้ำสูงสุดที่เป็นไปได้จำกัดอยู่ที่ 256 โดยมีประมาณ
ใช้ไปแล้วหนึ่งในสี่
ชุดมาโครช่วยให้เข้าถึงฟิลด์ได้ง่ายขึ้นและสม่ำเสมอมากขึ้น ได้แก่
"OP()" ซึ่งใช้เพื่อกำหนดประเภทของโครงสร้างคล้าย "regnode" "NEXT_OFF()",
ซึ่งเป็นการชดเชยไปยังโหนดถัดไป (เพิ่มเติมในภายหลัง); "ARG()", "ARG1()", "ARG2()",
"ARG_SET()" และเทียบเท่าสำหรับการอ่านและตั้งค่าอาร์กิวเมนต์ และ "STR_LEN()"
"STRING()" และ "OPERAND()" สำหรับจัดการสตริงและประเภทแบริ่ง regop
อะไร ลงทะเบียนใหม่ is ต่อไป?
มีแนวคิดที่แตกต่างกันสามประการของ "ถัดไป" ในเอ็นจิ้น regex และเป็นสิ่งสำคัญที่จะ
ทำให้พวกเขาชัดเจน
· มี "regnode ถัดไป" จาก regnode ที่กำหนด ซึ่งเป็นค่าที่ไม่ค่อยมีประโยชน์
เว้นแต่ว่าบางครั้งมันก็จับคู่กันในแง่ของคุณค่ากับคนอื่น ๆ และนั่น
บางครั้งรหัสจะถือว่าสิ่งนี้เป็นเช่นนั้นเสมอ
· มี "regop ถัดไป" จาก regop/regnode ที่กำหนด นี่คือ regop ทางกายภาพ
อยู่หลังอันปัจจุบัน ตามที่กำหนดโดยขนาดของ reop ปัจจุบัน นี่คือ
มักจะมีประโยชน์ เช่น เมื่อทิ้งโครงสร้าง เราใช้คำสั่งนี้เพื่อสำรวจ
บางครั้งรหัสจะถือว่า "regnode ถัดไป" เหมือนกับ "regop ถัดไป" หรือ
กล่าวอีกนัยหนึ่งถือว่าขนาดของประเภท regop ที่กำหนดจะเป็นหนึ่งเสมอ
regnode ขนาดใหญ่
· มี "regnext" จาก regop ที่กำหนด นี่คือ regop ที่เข้าถึงโดย
กระโดดไปข้างหน้าด้วยค่าของ "NEXT_OFF()" หรือในบางกรณีสำหรับการกระโดดไกลโดย
ฟิลด์ "arg1" ของโครงสร้าง "regnode_1" รูทีนย่อย "regnext()" จัดการสิ่งนี้
โปร่งใส นี่คือตัวตายตัวแทนทางตรรกะของโหนด ซึ่งในบางกรณี เช่น
ของสาขา "สาขา" มีความหมายพิเศษ
กระบวนการ ภาพรวมสินค้า
พูดอย่างกว้าง ๆ การจับคู่สตริงกับรูปแบบเกี่ยวข้องกับสิ่งต่อไปนี้
ขั้นตอน
A. การรวบรวม
1. การแยกวิเคราะห์ขนาด
2. การแยกวิเคราะห์เพื่อการก่อสร้าง
3. การเพิ่มประสิทธิภาพและการวิเคราะห์ Peep-hole
ข. การประหารชีวิต
4. ตำแหน่งเริ่มต้นและการเพิ่มประสิทธิภาพแบบไม่มีการแข่งขัน
5. การทำงานของโปรแกรม
ในกรณีที่ขั้นตอนเหล่านี้เกิดขึ้นในการดำเนินการจริงของโปรแกรม Perl จะถูกกำหนดโดยว่า
รูปแบบเกี่ยวข้องกับการสอดแทรกตัวแปรสตริงใดๆ หากมีการสอดแทรกเกิดขึ้น
การรวบรวมเกิดขึ้นที่รันไทม์ หากไม่เป็นเช่นนั้น คอมไพล์จะดำเนินการที่คอมไพล์
เวลา. (ตัวแก้ไข "/o" จะเปลี่ยนสิ่งนี้ เช่นเดียวกับ "qr//" ในระดับหนึ่ง) เครื่องยนต์
ไม่ได้สนใจมากขนาดนั้น
การรวบรวม
รหัสนี้อยู่ใน .เป็นหลัก regcomp.c, พร้อมกับไฟล์ส่วนหัว regcomp.h, regexp.h
และ regnodes.h.
การรวบรวมเริ่มต้นด้วย "pregcomp()" ซึ่งส่วนใหญ่เป็นตัวห่อหุ้มการเริ่มต้นซึ่ง
ฟาร์มทำงานเป็นกิจวัตรอีกสองอย่างสำหรับการยกของหนัก: อย่างแรกคือ "reg()" ซึ่งก็คือ
จุดเริ่มต้นสำหรับการแยกวิเคราะห์ ประการที่สอง "study_chunk()" มีหน้าที่ในการเพิ่มประสิทธิภาพ
การเริ่มต้นใน "pregcomp()" ส่วนใหญ่เกี่ยวข้องกับการสร้างและการกรอกข้อมูลของรายการพิเศษ
โครงสร้าง "RExC_state_t" (นิยามใน regcomp.c). กิจวัตรที่ใช้ภายในเกือบทั้งหมดใน
regcomp.h นำตัวชี้ไปยังโครงสร้างเหล่านี้เป็นอาร์กิวเมนต์แรกด้วยชื่อ
"pRExC_state". โครงสร้างนี้ใช้เพื่อจัดเก็บสถานะการคอมไพล์และมีหลายรายการ
ฟิลด์ ในทำนองเดียวกัน มีมาโครจำนวนมากที่ทำงานบนตัวแปรนี้: อะไรก็ได้ที่มีลักษณะ
เช่น "RExC_xxxx" เป็นมาโครที่ทำงานบนตัวชี้/โครงสร้างนี้
วจีวิภาค for ขนาด
ในการผ่านนี้ รูปแบบการป้อนข้อมูลจะถูกแยกวิเคราะห์เพื่อคำนวณว่าจำเป็นต้องใช้พื้นที่เท่าใด
สำหรับแต่ละ regop เราจะต้องปล่อย ขนาดยังใช้กำหนดว่ายาว
จะต้องกระโดดในโปรแกรม
ขั้นตอนนี้ควบคุมโดยมาโคร "SIZE_ONLY" ที่ตั้งค่าไว้
การแยกวิเคราะห์ดำเนินการค่อนข้างมากเหมือนกับที่ทำในระหว่างขั้นตอนการก่อสร้าง ยกเว้น
ที่กิจวัตรส่วนใหญ่จะลัดวงจรเพื่อเปลี่ยนฟิลด์ขนาด "RExC_size" และไม่ทำ
สิ่งอื่นใด
วจีวิภาค for การก่อสร้าง
เมื่อกำหนดขนาดของโปรแกรมแล้ว รูปแบบจะถูกแยกวิเคราะห์อีกครั้ง แต่สิ่งนี้
เวลาจริง ตอนนี้ "SIZE_ONLY" จะเป็นเท็จ และการก่อสร้างจริงอาจเกิดขึ้นได้
"reg()" คือจุดเริ่มต้นของกระบวนการแยกวิเคราะห์ มีหน้าที่แยกวิเคราะห์ตามอำเภอใจ
กลุ่มของรูปแบบจนถึงส่วนท้ายของสตริงหรือวงเล็บปิดแรกมัน
พบเจอในรูปแบบ ซึ่งหมายความว่าสามารถใช้เพื่อแยกวิเคราะห์ regex ระดับบนสุดหรือ any
ส่วนภายในวงเล็บจัดกลุ่ม นอกจากนี้ยังจัดการกับ "parens พิเศษ" ที่ Perl's
regexes มี ตัวอย่างเช่น เมื่อแยกวิเคราะห์ "/x(?:foo)y/" "reg()" จะถูกเรียกที่จุดหนึ่ง
เพื่อแยกวิเคราะห์จาก "?" จนถึงและรวมถึง ")"
นอกจากนี้ "reg()" มีหน้าที่แยกวิเคราะห์สาขาตั้งแต่หนึ่งสาขาขึ้นไปจาก
แบบแผน และสำหรับ "จบมัน" โดยตั้งค่าตัวชี้ถัดไปอย่างถูกต้อง ตามลำดับ
ในการแยกวิเคราะห์มันเรียกซ้ำ ๆ ว่า "regbranch()" ซึ่งรับผิดชอบ
จัดการได้ถึง "|" ตัวแรก สัญลักษณ์ที่เห็น
"regbranch()" กลับเรียก "regpiece()" ซึ่งจัดการ "สิ่งของ" ตามด้วยตัวระบุ
เพื่อแยกวิเคราะห์ "สิ่งของ" เรียก "regatom()" นี่คือกิจวัตรระดับต่ำสุด
ซึ่งแยกสตริงคงที่ คลาสอักขระ และสัญลักษณ์พิเศษต่างๆ เช่น
"$" หาก "regatom()" พบอักขระ "(" ก็จะเรียกว่า "reg()"
รูทีน "regtail()" ถูกเรียกโดยทั้ง "reg()" และ "regbranch()" เพื่อ "ตั้งค่า
ตัวชี้หาง" อย่างถูกต้อง เมื่อดำเนินการแล้วเราไปถึงปลายกิ่งเราต้องไป
ไปยังโหนดหลังวงเล็บการจัดกลุ่ม อย่างไรก็ตาม เมื่อแยกวิเคราะห์ เราไม่รู้ว่า
สิ้นสุดจะอยู่จนกว่าเราจะไปถึงที่นั่นดังนั้นเมื่อเราทำเราต้องกลับไปอัปเดตออฟเซ็ตเป็น
เหมาะสม. "regtail" ใช้เพื่อทำให้ง่ายขึ้น
ความละเอียดอ่อนของกระบวนการแยกวิเคราะห์หมายความว่า regex เช่น "/foo/" เดิมถูกแยกวิเคราะห์
สลับกับสาขาเดียว ภายหลังเท่านั้นที่ผู้เพิ่มประสิทธิภาพ
แปลงการสลับสาขาเดียวให้เป็นรูปแบบที่ง่ายกว่า
วิเคราะห์คำ โทร กราฟ และ a ไวยากรณ์
กราฟการโทรมีลักษณะดังนี้:
reg() # แยกวิเคราะห์ regex ระดับบนสุดหรือภายใน
#ผู้ปกครอง
regbranch() # แยกวิเคราะห์สาขาเดียวของการสลับ
regpiece() # แยกวิเคราะห์รูปแบบตามด้วยปริมาณ
regatom() # แยกวิเคราะห์รูปแบบง่าย ๆ
regclass() # ใช้เพื่อจัดการคลาส
reg() # ใช้เพื่อจัดการวงเล็บ
#รูปแบบย่อย
....
...
retail() # จบสาขา
...
regtail() # จบลำดับสาขา ผูกแต่ละอัน
#กิ่งหางถึงหาง
# ลำดับ
# (ใหม่) ในโหมดดีบักนี่คือ
# regtail_study()
รูปแบบไวยากรณ์อาจเป็นดังนี้:
อะตอม : คงที่ | ระดับ
ปริมาณ : '*' | '+' | '?' | '{นาที,สูงสุด}'
_สาขา: ชิ้น
| ชิ้น _สาขา
| ไม่มีอะไร
สาขา: _branch
| _สาขา '|' สาขา
กลุ่ม : '(' สาขา ')'
_piece: อะตอม | กลุ่ม
ชิ้น : _ชิ้น
| _ชิ้นปริมาณ
วจีวิภาค ภาวะแทรกซ้อน
ความหมายของคำอธิบายข้างต้นคือรูปแบบที่มีวงเล็บซ้อนกัน
จะส่งผลให้กราฟการเรียกที่วนผ่าน "reg()", "regbranch()", "regpiece()",
"regatom()", "reg()", "สาขาย่อย ()" ฯลฯ หลายครั้งจนถึงระดับรังที่ลึกที่สุด
ถึง. รูทีนทั้งหมดข้างต้นจะคืนค่าตัวชี้ไปที่ "regnode" ซึ่งมักจะเป็น
regnode ล่าสุดที่เพิ่มลงในโปรแกรม อย่างไรก็ตาม ภาวะแทรกซ้อนอย่างหนึ่งก็คือ เร็ก() ส่งกลับค่า NULL
สำหรับการแยกวิเคราะห์ไวยากรณ์ "(?:)" สำหรับตัวแก้ไขแบบฝัง ตั้งค่าสถานะ "TRYAGAIN" NS
"TRYAGAIN" แพร่กระจายขึ้นไปจนกว่าจะถูกจับ ในบางกรณีโดย "regatom()" แต่
อย่างอื่นโดยไม่มีเงื่อนไขโดย "regbranch()" จึงไม่มีวันหวนคืนกลับมาโดย
"regbranch()" ถึง "reg()" แฟล็กนี้อนุญาตให้ตรวจพบรูปแบบเช่น "(?i)+" เป็น
ข้อผิดพลาด (ปริมาณ ดังต่อไปนี้ ไม่มีอะไร in นิพจน์ทั่วไป; โดดเด่น by <- ที่นี่ in ม/(?ผม)+ <- ที่นี่ /).
ความซับซ้อนอีกประการหนึ่งคือการแสดงที่ใช้สำหรับโปรแกรมจะแตกต่างกันหากต้องการ
เพื่อจัดเก็บ Unicode แต่ก็ไม่สามารถทราบได้แน่ชัดเสมอไปว่าจนถึง
กลางทางผ่านการแยกวิเคราะห์ การแสดง Unicode สำหรับโปรแกรมนั้นใหญ่กว่า และไม่สามารถ
จับคู่ได้อย่างมีประสิทธิภาพ (ดู "การสนับสนุน Unicode และการแปลเป็นภาษาท้องถิ่น" ด้านล่างสำหรับรายละเอียดเพิ่มเติม
ว่าเพราะเหตุใด) หากรูปแบบมี Unicode ตามตัวอักษร ก็ชัดเจนว่าโปรแกรมต้องการ
เพื่อจัดเก็บ Unicode มิฉะนั้น parser จะมองในแง่ดีว่ายิ่งมีประสิทธิภาพมากขึ้น
สามารถใช้การแสดงแทนได้ และเริ่มการปรับขนาดบนพื้นฐานนี้ แต่ถ้าเป็นเช่นนั้น
พบบางสิ่งในรูปแบบที่ต้องจัดเก็บเป็น Unicode เช่น "\x{...}"
Escape Sequence แทนตัวอักษรตามตัวอักษรแล้วนี่หมายความว่าทั้งหมดก่อนหน้านี้
ขนาดที่คำนวณแล้วจำเป็นต้องทำใหม่ โดยใช้ค่าที่เหมาะสมสำหรับ Unicode
การเป็นตัวแทน ในปัจจุบัน โครงสร้างนิพจน์ทั่วไปทั้งหมดที่สามารถเรียกสิ่งนี้ได้คือ
แยกวิเคราะห์ด้วยรหัสใน "regatom()"
เพื่อหลีกเลี่ยงการสูญเสียงานเมื่อจำเป็นต้องเริ่มการทำงานใหม่ การกำหนดขนาดจะถูกยกเลิก - "regatom()"
คืนค่า NULL ทันที โดยตั้งค่าสถานะ "RESTART_UTF8" (การดำเนินการนี้ถูกห่อหุ้ม
โดยใช้มาโคร "REQUIRE_UTF8") คำขอรีสตาร์ทนี้ได้รับการเผยแพร่ในสายการโทรใน
คล้ายคลึงกันจน "จับ" ใน "Perl_re_op_compile()" ซึ่งเป็นเครื่องหมายของรูปแบบ
ที่มี Unicode และรีสตาร์ทการปรับขนาด นอกจากนี้ยังเป็นไปได้สำหรับการก่อสร้าง
ภายในบล็อคโค้ดรันไทม์จะต้องแสดง Unicode ซึ่งก็คือ
ส่งสัญญาณโดย "S_compile_runtime_code()" คืนค่าเท็จเป็น "Perl_re_op_compile()"
ก่อนหน้านี้การรีสตาร์ทถูกใช้งานโดยใช้ "longjmp" ใน "regatom()" กลับไปเป็น "setjmp"
ใน "Perl_re_op_compile()" แต่สิ่งนี้พิสูจน์แล้วว่ามีปัญหาเนื่องจากตัวหลังมีขนาดใหญ่
ฟังก์ชันที่มีตัวแปรอัตโนมัติจำนวนมากซึ่งโต้ตอบกับสิ่งที่เกิดขึ้นได้ไม่ดี
ควบคุมการไหลของ "setjmp"
การแก้ปัญหา เอาท์พุต
ในเวอร์ชันพัฒนา 5.9.x ของ Perl คุณสามารถ "ใช้ re Debug => 'PARSE'" เพื่อดูบางอย่างได้
ติดตามข้อมูลเกี่ยวกับกระบวนการแยกวิเคราะห์ เราจะเริ่มด้วยรูปแบบง่ายๆ และ
สร้างรูปแบบที่ซับซ้อนมากขึ้น
ดังนั้นเมื่อเราแยก "/foo/" เราจะเห็นบางอย่างเช่นตารางต่อไปนี้ ด้านซ้ายแสดงว่าคืออะไร
กำลังแยกวิเคราะห์และตัวเลขระบุว่าจะไปที่ใดในครั้งต่อไป สิ่งที่อยู่ใน
ขวาคือผลลัพธ์การติดตามของกราฟ เลือกชื่อย่อให้สั้นลง
หนาแน่นบนหน้าจอ 'tsdy' เป็นรูปแบบพิเศษของ "regtail()" ซึ่งทำหน้าที่พิเศษบางอย่าง
การวิเคราะห์
>foo< 1 ลงทะเบียน
บร.น
ชิ้น
อะตอม
>< 4 tsdy~ แน่นอน (ที่แน่นอน) (1)
~ แนบกับ END (3) ออฟเซ็ตเป็น2
โปรแกรมผลลัพธ์จะมีลักษณะดังนี้:
1: แน่นอน (3)
3: END(0)
อย่างที่คุณเห็น แม้ว่าเราจะแยกกิ่งและกิ่งออก ท้ายที่สุดมันก็เป็นเพียง
อะตอม. โปรแกรมสุดท้ายแสดงให้เราเห็นว่าสิ่งต่างๆ ทำงานอย่างไร เรามี regop "EXACT" ตามด้วย an
"จบ" อีกครั้ง ตัวเลขในวงเล็บระบุว่า "regnext" ของโหนดไปที่ใด NS
ไม่ได้ใช้ "regnext" ของ "END" regop เนื่องจาก regops "END" หมายความว่าเราจับคู่สำเร็จแล้ว
ตัวเลขทางด้านซ้ายระบุตำแหน่งของ regop ในอาร์เรย์ regnode
ตอนนี้ มาลองรูปแบบที่ยากขึ้น เราจะเพิ่มปริมาณดังนั้นตอนนี้เรามีรูปแบบ
"/foo+/". เราจะเห็นว่า "regbranch()" เรียก "regpiece()" สองครั้ง
>foo+< 1 ลงทะเบียน
บร.น
ชิ้น
อะตอม
>o+< 3 ชิ้น
อะตอม
>< 6 หาง~ แน่นอน (1)
7 tsdy~ แน่นอน (ที่แน่นอน) (1)
~ พลัส (จบ) (3)
~ แนบกับ END (6) ออฟเซ็ตเป็น3
และเราลงเอยด้วยโปรแกรม:
1: แน่นอน (3)
3: PLUS(6)
4: แน่นอน (0)
6: END(0)
ตอนนี้เรามีกรณีพิเศษ regop "EXACT" มี "regnext" เท่ากับ 0 นั่นเป็นเพราะถ้ามัน
ตรงกันก็ควรพยายามจับคู่ตัวเองอีกครั้ง reop "PLUS" จัดการกับความล้มเหลวที่แท้จริง
ของ regop "EXACT" และดำเนินการอย่างเหมาะสม (ไปที่ regnode 6 หาก "EXACT" ตรงกันที่
อย่างน้อยหนึ่งครั้งหรือล้มเหลวหากไม่เป็นเช่นนั้น)
สำหรับสิ่งที่ซับซ้อนกว่านี้มาก: "/x(?:foo*|b[a][rR])(foo|bar)$/"
>x(?:foo*|b... 1 ลงทะเบียน
บร.น
ชิ้น
อะตอม
>(?:foo*|b[... 3 ชิ้น
อะตอม
>?:foo*|b[a... reg
>foo*|b[a][... brnc
ชิ้น
อะตอม
>o*|b[a][rR... 5 ชิ้น
อะตอม
>|b[a][rR])... 8 หาง~ แน่นอน (3)
>b[a][rR])(... 9 ปีที่แล้ว
10 ชิ้น
อะตอม
>[a][rR])(f... 12 ชิ้น
อะตอม
>a][rR])(สำหรับ... คลาส
>[rR])(foo|... 14 หาง~ แน่นอน (10)
ชิ้น
อะตอม
>rR])(foo|b...คลาส
>)(foo|bar)... 25 หาง~ แน่นอน (12)
หาง~ สาขา (3)
26 tsdy~ สาขา (จบ) (9)
~ แนบกับ TAIL (25) ชดเชยเป็น 16
tsdy~ ถูกต้อง (ที่แน่นอน) (4)
~ สตาร์ (จบ) (6)
~ แนบกับ TAIL (25) ชดเชยเป็น 19
tsdy~ แน่นอน (แน่นอน) (10)
~ แน่นอน (แน่นอน) (12)
~ อะไรก็ได้[Rr] (จบ) (14)
~ แนบกับ TAIL (25) ชดเชยเป็น 11
>(foo|bar)$< หาง~ แน่นอน (1)
ชิ้น
อะตอม
>foo|bar)$< ลงทะเบียน
28 บร
ชิ้น
อะตอม
>|บาร์)$< 31 หาง~ เปิด1 (26)
>บาร์)$< brnc
32 ชิ้น
อะตอม
>)$< 34 หาง~ สาขา (28)
36 tsdy~ สาขา (จบ) (31)
~ แนบกับ CLOSE1 (34) ชดเชยเป็น3
tsdy~ ถูกต้อง (แน่นอน) (29)
~ แนบกับ CLOSE1 (34) ชดเชยเป็น5
tsdy~ ถูกต้อง (แน่นอน) (32)
~ แนบกับ CLOSE1 (34) ชดเชยเป็น2
>$< หาง~ สาขา (3)
~ สาขา (9)
~ หาง (25)
ชิ้น
อะตอม
>< 37 หาง~ เปิด1 (26)
~ สาขา (28)
~ สาขา (31)
~ ปิด1 (34)
38 tsdy~ แน่นอน (ที่แน่นอน) (1)
~ สาขา (สิ้นสุด) (3)
~ สาขา (สิ้นสุด) (9)
~ หาง (จบ) (25)
~ เปิด1 (สิ้นสุด) (26)
~ สาขา (สิ้นสุด) (28)
~ สาขา (สิ้นสุด) (31)
~ ปิด1 (จบ) (34)
~ EOL (จบ) (36)
~ แนบกับ END (37) ออฟเซ็ตเป็น1
ส่งผลให้โปรแกรม
1: แน่นอน (3)
3: สาขา(9)
4: แน่นอน (6)
6: STAR(26)
7: แน่นอน (0)
9: สาขา(25)
10: แน่นอน (14)
12: ปรับให้เหมาะสม (2 โหนด)
14: อะไรก็ได้[Rr](26)
25: หาง(26)
26: เปิด 1(28)
28: ไตร่ตรอง(34)
[StS:1 Wds:2 Cs:6 Uq:5 #Sts:7 Mn:3 Mx:3 Stcls:bf]
30: ปรับให้เหมาะสม (4 โหนด)
34: ปิด1(36)
36: นามสกุล EOL(37)
37: END(0)
ที่นี่เราสามารถเห็นโปรแกรมที่ซับซ้อนมากขึ้นด้วยการเพิ่มประสิทธิภาพที่หลากหลายในการเล่น ที่
regnode 10 เราเห็นตัวอย่างที่คลาสอักขระที่มีเพียงหนึ่งอักขระในนั้นคือ
กลายเป็นโหนด "EXACT" เรายังสามารถดูได้ว่าการสลับทั้งหมดถูกเปลี่ยนเป็น a . ที่ไหน
โหนด "TRIE-EXACT" ด้วยเหตุนี้ เร็กโหนดบางตัวจึงถูกทำเครื่องหมายว่าปรับให้เหมาะสมที่สุด
ห่างออกไป. เราจะเห็นได้ว่าสัญลักษณ์ "$" ถูกแปลงเป็น "EOL" regop ซึ่งเป็นแบบพิเศษ
โค้ดที่มองหา "\n" หรือส่วนท้ายของสตริง
ตัวชี้ถัดไปสำหรับ "BRANCH" น่าสนใจตรงที่มันชี้ให้เห็นถึงตำแหน่งที่ควรดำเนินการ
ไปถ้าสาขาล้มเหลว เมื่อดำเนินการ หากเครื่องยนต์พยายามเคลื่อนที่จากสาขาไปยัง a
"regnext" ที่ไม่ใช่สาขาแล้วเครื่องจะรู้ว่ากิ่งทั้งชุด
ล้มเหลว.
มองลอด การเพิ่มประสิทธิภาพ และ การวิเคราะห์
เครื่องมือนิพจน์ทั่วไปสามารถเป็นเครื่องมือที่มีน้ำหนักมาก บนสายยาวและซับซ้อน
แบบแผนอาจต้องทำงานมากมายเพื่อหาคู่และตัดสินใจมากกว่านี้
ว่าไม่สามารถจับคู่ได้ พิจารณาสถานการณ์เช่นรูปแบบต่อไปนี้
'อาบาบาบาบาบาบาบาบาบาบา' =~ /(a|b)*z/
ส่วน "(a|b)*" สามารถจับคู่กับทุกตัวอักษรในสตริง และล้มเหลวทุกครั้งเพราะ
ไม่มี "z" ในสตริง เห็นได้ชัดว่าเราสามารถหลีกเลี่ยงการใช้เอ็นจิ้น regex เว้นแต่
มี "z" ในสตริง ในทำนองเดียวกันในรูปแบบเช่น:
/foo(\w+)บาร์/
ในกรณีนี้ เราทราบดีว่าสตริงต้องมี "foo" ซึ่งต้องตามด้วย "bar"
เราสามารถใช้การจับคู่ Fast Boyer-Moore ตามที่ใช้ใน "fbm_instr()" เพื่อค้นหาตำแหน่ง
ของสตริงเหล่านี้ ถ้าไม่มีก็ไม่ต้องไปทำอะไรมากมาย
เครื่องยนต์ regex ราคาแพง ยิ่งไปกว่านั้น ถ้าพวกมันมีอยู่ เราก็สามารถใช้ตำแหน่งของพวกเขาเพื่อ
ลดพื้นที่การค้นหาที่เอ็นจิ้น regex จำเป็นต้องครอบคลุมเพื่อพิจารณาว่าทั้งหมด
ตรงกับรูปแบบ
มีรูปแบบต่างๆ ที่สามารถนำมาใช้เพื่ออำนวยความสะดวกในการเพิ่มประสิทธิภาพได้
ตามบรรทัดเหล่านี้:
· ตรึงสตริงคงที่
· สตริงคงที่ลอยตัว
· ข้อกำหนดความยาวขั้นต่ำและสูงสุด
· เริ่มเรียน
· ตำแหน่งต้น/ปลายสาย
อีกรูปแบบหนึ่งของการเพิ่มประสิทธิภาพที่อาจเกิดขึ้นได้คือการเพิ่มประสิทธิภาพ "peep-hole" หลังการแยกวิเคราะห์
โดยที่โครงสร้างที่ไม่มีประสิทธิภาพจะถูกแทนที่ด้วยโครงสร้างที่มีประสิทธิภาพมากกว่า "TAIL" กลับมาอีกครั้ง
ซึ่งใช้ระหว่างการแยกวิเคราะห์เพื่อทำเครื่องหมายปลายกิ่งและปลายกลุ่มคือ
ตัวอย่างของสิ่งนี้ regops เหล่านี้ถูกใช้เป็นที่ยึดระหว่างการก่อสร้างและ "เสมอ
ตรงกัน" จึงสามารถ "ปรับให้เหมาะสม" โดยทำให้สิ่งที่ชี้ไปที่จุด "หาง"
ไปที่สิ่งที่ "TAIL" ชี้ไป ดังนั้นจึง "ข้าม" โหนด
การปรับให้เหมาะสมอีกอย่างหนึ่งที่อาจเกิดขึ้นได้ก็คือการผสาน ""EXACT" ซึ่งเป็นที่ที่ two
โหนด "EXACT" ที่ต่อเนื่องกันจะถูกรวมเป็น regop เดียว รูปแบบที่ก้าวร้าวมากยิ่งขึ้นของ
นี่คือลำดับสาขาของรูปแบบ "EXACT BRANCH ... EXACT" สามารถแปลงเป็น
กลุ่ม "TRIE-EXACT"
ทั้งหมดนี้เกิดขึ้นในรูทีน "study_chunk()" ซึ่งใช้โครงสร้างพิเศษ
"scan_data_t" เพื่อจัดเก็บการวิเคราะห์ที่ดำเนินการ และทำ "peep-hole"
การเพิ่มประสิทธิภาพตามที่เป็นไป
รหัสที่เกี่ยวข้องกับ "study_chunk()" นั้นคลุมเครืออย่างยิ่ง ระวัง. :-)
การกระทำ
การดำเนินการ regex โดยทั่วไปประกอบด้วยสองขั้นตอน ขั้นแรกคือการหาจุดเริ่มต้น
ชี้ไปที่สตริงที่เราควรจับคู่และอันที่สองกำลังเรียกใช้ regop
ล่าม.
หากเราสามารถบอกได้ว่าไม่มีจุดเริ่มต้นที่ถูกต้อง เราก็ไม่ต้องรันคำสั่ง
ล่ามเลย ในทำนองเดียวกัน หากเรารู้จากขั้นตอนการวิเคราะห์ว่าเราไม่สามารถตรวจจับ a . ได้
ทางลัดไปยังตำแหน่งเริ่มต้นเราตรงไปที่ล่าม
จุดเริ่มต้นสองจุดคือ "re_intuit_start()" และ "pregexec()" กิจวัตรเหล่านี้มี
ความสัมพันธ์ร่วมประเวณีระหว่างประเวณีกับคาบเกี่ยวกันระหว่างหน้าที่และ "pregexec()"
อาจเรียก "re_intuit_start()" ได้ด้วยตัวเอง อย่างไรก็ตาม ส่วนอื่น ๆ ของแหล่ง Perl
รหัสอาจเรียกเข้าอย่างใดอย่างหนึ่งหรือทั้งสองอย่าง
การดำเนินการของล่ามเองเคยเป็นแบบเรียกซ้ำ แต่ต้องขอบคุณความพยายามของ
Dave Mitchell ในแทร็กการพัฒนา 5.9.x ที่มีการเปลี่ยนแปลง: ตอนนี้สแต็กภายในคือ
รักษาไว้บนฮีปและรูทีนทำซ้ำได้อย่างเต็มที่ นี้สามารถทำให้มันยุ่งยากเป็น
รหัสค่อนข้างอนุรักษ์นิยมเกี่ยวกับสถานะที่เก็บ ผลลัพธ์ที่ได้คือสอง
บรรทัดที่ต่อเนื่องกันในโค้ดสามารถทำงานในบริบทที่แตกต่างกันโดยสิ้นเชิงเนื่องจาก
การเรียกซ้ำจำลอง
เริ่มต้น ตำแหน่ง และ ไม่ตรงกัน การเพิ่มประสิทธิภาพ
"re_intuit_start()" มีหน้าที่จัดการจุดเริ่มต้นและการปรับให้เหมาะสมแบบไม่แมทช์เช่น
กำหนดโดยผลการวิเคราะห์ที่ทำโดย "study_chunk()" (และอธิบายไว้ใน "Peep-
การเพิ่มประสิทธิภาพและการวิเคราะห์หลุม")
โครงสร้างพื้นฐานของกิจวัตรนี้คือพยายามค้นหาจุดเริ่มต้นและ/หรือจุดสิ้นสุดของ
โดยที่รูปแบบสามารถจับคู่ได้ และเพื่อให้แน่ใจว่าสตริงนั้นยาวพอที่จะจับคู่กับ
ลวดลาย. พยายามใช้วิธีที่มีประสิทธิภาพมากกว่าวิธีที่มีประสิทธิภาพน้อยกว่าและอาจ
เกี่ยวข้องกับการตรวจสอบข้อ จำกัด อย่างมากเพื่อค้นหาตำแหน่งในสตริงที่
การแข่งขัน ตัวอย่างเช่นอาจพยายามกำหนดว่าสตริงที่กำหนดจะต้องไม่ใช่เฉพาะ
ปัจจุบัน แต่มีอักขระจำนวนหนึ่งก่อนสิ้นสุดสตริงหรืออะไรก็ตาม
มันเรียกกิจวัตรอื่นๆ อีกหลายอย่าง เช่น "fbm_instr()" ซึ่งทำ Fast Boyer Moore
การจับคู่และ "find_byclass()" ซึ่งมีหน้าที่ในการค้นหาการเริ่มต้นโดยใช้ first
regop บังคับในโปรแกรม
เมื่อเป็นไปตามเกณฑ์การปรับให้เหมาะสมแล้ว "reg_try()" จะถูกเรียกเพื่อดำเนินการ
การจับคู่.
โครงการ การปฏิบัติ
"pregexec()" เป็นจุดเริ่มต้นหลักสำหรับการรัน regex มันมีการสนับสนุนสำหรับ
เริ่มต้นสถานะของล่าม regex โดยเรียกใช้ "re_intuit_start()" หากจำเป็นและ
เรียกใช้ล่ามบนสตริงจากตำแหน่งเริ่มต้นต่างๆ ตามต้องการ เมื่อเป็น
จำเป็นต้องใช้ล่าม regex "pregexec()" เรียก "regtry()"
"regtry()" เป็นจุดเริ่มต้นของล่าม regex คาดว่าเป็นข้อโต้แย้ง a
ตัวชี้ไปยังโครงสร้าง "regmatch_info" และตัวชี้ไปยังสตริง ส่งกลับจำนวนเต็ม 1
สำหรับความสำเร็จและ 0 สำหรับความล้มเหลว โดยพื้นฐานแล้วมันเป็นตัวห่อหุ้มการตั้งค่ารอบ ๆ "regmatch()"
"regmatch" เป็น "recursive loop" หลักของล่าม มันคือสวิตซ์ยักษ์
คำสั่งที่ใช้เครื่องของรัฐโดยที่สถานะที่เป็นไปได้คือ regops
ตัวเอง รวมทั้งสถานะระดับกลางและสถานะความล้มเหลวเพิ่มเติมอีกจำนวนหนึ่ง บางส่วนของ
สถานะถูกนำไปใช้เป็นรูทีนย่อย แต่จำนวนมากเป็นโค้ดแบบอินไลน์
เบ็ดเตล็ด
Unicode และ Localisation ระบบขอใช้บริการ
เมื่อจัดการกับสตริงที่มีอักขระที่ไม่สามารถแสดงโดยใช้แปด-
ชุดอักขระบิต Perl ใช้การแทนค่าภายในที่เป็นเวอร์ชันอนุญาตของ
การเข้ารหัส UTF-8 ของ Unicode[2] นี้ใช้ไบต์เดียวเพื่อแสดงอักขระจากASCII
ชุดอักขระ และลำดับตั้งแต่สองไบต์ขึ้นไปสำหรับอักขระอื่นๆ ทั้งหมด (ดู
perlunitut สำหรับข้อมูลเพิ่มเติมเกี่ยวกับความสัมพันธ์ระหว่างการเข้ารหัส UTF-8 และ Perl
utf8 ความแตกต่างไม่สำคัญสำหรับการสนทนานี้)
ไม่ว่าคุณจะมองอย่างไร การสนับสนุน Unicode จะสร้างความเจ็บปวดให้กับเอ็นจิ้น regex
เคล็ดลับที่อาจใช้ได้เมื่อคุณมีอักขระที่เป็นไปได้ 256 ตัวมักจะไม่ปรับขนาดเป็น
จัดการขนาดของชุดอักขระ UTF-8 สิ่งที่คุณสามารถทำได้ด้วย ASCII
อาจไม่เป็นจริงกับ Unicode ตัวอย่างเช่น ใน ASCII สามารถสันนิษฐานได้ว่า
"sizeof(char1) == sizeof(char2)" แต่ใน UTF-8 ไม่ใช่ การพับเคส Unicode ทำได้มากมาย
ซับซ้อนกว่ากฎง่ายๆ ของ ASCII และแม้ว่าจะไม่ได้ใช้ Unicode แต่เพียงอย่างเดียว
การเข้ารหัสแบบไบต์เดี่ยวที่แปลแล้ว สิ่งต่างๆ อาจเป็นเรื่องยาก (เช่น ละติน ขนาดเล็ก จดหมาย
SHARP S (U+00DF, ss) ควรตรงกับ 'SS' ในการจับคู่แบบไม่คำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ที่แปลแล้ว)
สิ่งที่แย่กว่านั้นคือการสนับสนุน UTF-8 นั้นเป็นส่วนเสริมในภายหลังของเอ็นจิ้น regex (ตามที่มัน
คือต้อง Perl) และสิ่งนี้จำเป็นต้องทำให้สิ่งต่าง ๆ ซับซ้อนขึ้นมาก แน่นอนมันคือ
ออกแบบเอ็นจิ้น regex ได้ง่ายกว่าโดยคำนึงถึงการรองรับ Unicode ตั้งแต่เริ่มต้น
เพื่อแก้ไขให้เป็นอันที่ไม่ใช่
regops เกือบทั้งหมดที่เกี่ยวข้องกับการดูสตริงอินพุตมีสองกรณี หนึ่งกรณีสำหรับ UTF-8
และหนึ่งไม่ได้ อันที่จริง มันมักจะซับซ้อนกว่านั้น เนื่องจากรูปแบบอาจเป็น UTF-8 as
ดี.
ต้องใช้ความระมัดระวังเมื่อทำการเปลี่ยนแปลงเพื่อให้แน่ใจว่าคุณจัดการ UTF-8 อย่างถูกต้องทั้ง
ในเวลาคอมไพล์และ ณ เวลาดำเนินการ รวมทั้งเมื่อสตริงและรูปแบบเป็น
ไม่ตรงกัน
ฐาน โครงสร้าง
โครงสร้าง "regexp" ที่อธิบายใน perlreapi เป็นเรื่องปกติสำหรับเอ็นจิ้น regex ทั้งหมด สองของมัน
ฟิลด์มีไว้สำหรับการใช้งานส่วนตัวของเอ็นจิ้น regex ที่รวบรวมรูปแบบ
เหล่านี้คือ "intflags" และสมาชิกส่วนตัว "ส่วนตัว" เป็นตัวชี้โมฆะไปยัง
โครงสร้างตามอำเภอใจที่มีการใช้งานและการจัดการเป็นความรับผิดชอบของการรวบรวม
เครื่องยนต์. Perl จะไม่แก้ไขค่าใดค่าหนึ่งเหล่านี้ ในกรณีของเครื่องยนต์สต็อก
โครงสร้างที่ชี้โดย "private" เรียกว่า "regexp_internal"
ฟิลด์ "ส่วนตัว" และ "intflags" มีข้อมูลเฉพาะสำหรับแต่ละเอ็นจิ้น
มีสองโครงสร้างที่ใช้เก็บนิพจน์ทั่วไปที่คอมไพล์แล้ว หนึ่ง "regexp"
โครงสร้างที่อธิบายไว้ใน perlreapi นั้นถูกเติมโดยเครื่องยนต์ที่กำลังเป็นอยู่ ใช้แล้วบ้าง
ของฟิลด์ที่อ่านโดย Perl เพื่อใช้งานสิ่งต่าง ๆ เช่นการทำให้เป็นสตริงของ "qr//"
โครงสร้างอื่นชี้ไปที่ "ส่วนตัว" ของโครงสร้าง "regexp" และอยู่ในส่วนเพิ่มเติม
เป็น "intflags" ในโครงสร้างเดียวกันที่ถือว่าเป็นคุณสมบัติของเอ็นจิ้น regex ซึ่ง
รวบรวมนิพจน์ทั่วไป
โครงสร้าง regexp มีข้อมูลทั้งหมดที่ Perl จำเป็นต้องรับรู้เพื่อให้ทำงานได้อย่างถูกต้อง
ด้วยนิพจน์ทั่วไป ประกอบด้วยข้อมูลเกี่ยวกับการเพิ่มประสิทธิภาพที่ Perl สามารถใช้เพื่อ
ตรวจสอบว่าควรใช้เอ็นจิ้น regex หรือไม่และข้อมูลการควบคุมอื่น ๆ ที่
จำเป็นในการดำเนินการรูปแบบอย่างถูกต้องในบริบทต่าง ๆ เช่นเป็นรูปแบบที่ยึดไว้
ในทางใดทางหนึ่งหรือแฟล็กใดที่ใช้ในระหว่างการคอมไพล์หรือว่าโปรแกรมมี
โครงสร้างพิเศษที่ Perl ต้องระวัง
นอกจากนี้ยังมีสองฟิลด์ที่มีไว้สำหรับการใช้งานส่วนตัวของ regex
เครื่องยนต์ที่รวบรวมรูปแบบ เหล่านี้คือ "intflags" และสมาชิกส่วนตัว NS
"ส่วนตัว" เป็นตัวชี้โมฆะไปยังโครงสร้างตามอำเภอใจซึ่งการใช้งานและการจัดการคือ
ความรับผิดชอบของคอมไพล์เอ็นจิ้น Perl จะไม่แก้ไขค่าใดค่าหนึ่งเหล่านี้
ดังที่ได้กล่าวไว้ก่อนหน้านี้ ในกรณีของเอ็นจิ้นเริ่มต้น "ส่วนตัว" จะเป็นตัวชี้
ไปยังโครงสร้าง regexp_internal ซึ่งเก็บโปรแกรมที่คอมไพล์แล้วและข้อมูลเพิ่มเติม
ที่เป็นส่วนตัวสำหรับการนำเอ็นจิน regex ไปใช้งาน
Perl's "ส่วนตัว" โครงสร้าง
โครงสร้างต่อไปนี้ใช้เป็นโครงสร้าง "ส่วนตัว" โดยเอ็นจิ้น regex ของ Perl ตั้งแต่มัน
เฉพาะเจาะจงสำหรับ Perl เป็นเพียงค่าความอยากรู้ในการใช้งานเครื่องมืออื่น ๆ
typedef โครงสร้าง regexp_internal {
U32 * ออฟเซ็ต; /* ชดเชยคำอธิบายประกอบ 20001228 MJD
* ข้อมูลเกี่ยวกับการทำแผนที่โปรแกรมไปยัง
*สตริง*/
regnode *regstclass; /* ตัวเลือก startclass ตามที่ระบุหรือ
* สร้างโดยเครื่องมือเพิ่มประสิทธิภาพ */
โครงสร้าง reg_data *ข้อมูล; /* ใช้ข้อมูลเบ็ดเตล็ดเพิ่มเติม
* โดยโปรแกรม เคยทำ
* ง่ายต่อการโคลนและอิสระโดยพลการ
* ข้อมูลที่ regops ต้องการ มักจะ
* ฟิลด์ ARG ของ regop คือ index
* ลงในโครงสร้างนี้ */
โปรแกรม regnode[1]; /* คบหาสมาคมกับ
* คอมไพเลอร์ */
} regexp_internal;
"ชดเชย"
ออฟเซ็ตเก็บการแมปออฟเซ็ตใน "โปรแกรม" เพื่อออฟเซ็ตในสตริง "พรีคอม"
ใช้โดยดีบักเกอร์ regex ภาพของ ActiveState เท่านั้น
"ชั้นทะเบียน"
regop พิเศษที่ใช้โดย "re_intuit_start()" เพื่อตรวจสอบว่ารูปแบบสามารถจับคู่ได้ที่
ตำแหน่งที่แน่นอน ตัวอย่างเช่น หากเอ็นจิ้น regex รู้ว่ารูปแบบนั้นต้อง
เริ่มต้นด้วย 'Z' จากนั้นจะสามารถสแกนสตริงจนกว่าจะพบแล้วเปิด
เครื่องยนต์ regex จากที่นั่น รูทีนที่จัดการสิ่งนี้เรียกว่า "find_by_class()"
บางครั้งฟิลด์นี้จะชี้ไปที่ regop ที่ฝังอยู่ในโปรแกรม และบางครั้งก็
ชี้ไปที่พื้นที่สังเคราะห์อิสระที่สร้างขึ้นโดยเครื่องมือเพิ่มประสิทธิภาพ
"ข้อมูล"
ฟิลด์นี้ชี้ไปที่โครงสร้าง "reg_data" ซึ่งถูกกำหนดดังนี้
โครงสร้าง reg_data {
จำนวน U32;
U8 *อะไร;
ข้อมูลเป็นโมฆะ*[1];
};
โครงสร้างนี้ใช้สำหรับจัดการโครงสร้างข้อมูลที่เอ็นจิน regex ต้องการ
จัดการเป็นพิเศษในระหว่างการโคลนหรือการทำงานฟรีบนผลิตภัณฑ์ที่คอมไพล์ แต่ละ
องค์ประกอบในอาร์เรย์ข้อมูลมีองค์ประกอบที่สอดคล้องกันในอาร์เรย์ใด ในระหว่าง
regops การรวบรวมที่ต้องการโครงสร้างพิเศษที่เก็บไว้จะเพิ่มองค์ประกอบให้กับแต่ละ
อาร์เรย์โดยใช้ add_data() ประจำแล้วเก็บดัชนีใน regop
"โปรแกรม"
โปรแกรมที่คอมไพล์แล้ว ฝังอยู่ในโครงสร้างเพื่อให้สามารถปฏิบัติกับโครงสร้างทั้งหมดได้เป็น
หยดเดียว
ใช้ perlreguts ออนไลน์โดยใช้บริการ onworks.net