JavaRush /จาวาบล็อก /Random-TH /RegEx: 20 ขั้นตอนสั้นๆ เพื่อเชี่ยวชาญนิพจน์ทั่วไป ส่วนที่...
Artur
ระดับ
Tallinn

RegEx: 20 ขั้นตอนสั้นๆ เพื่อเชี่ยวชาญนิพจน์ทั่วไป ส่วนที่ 3

เผยแพร่ในกลุ่ม
RegEx: 20 ขั้นตอนสั้นๆ เพื่อเชี่ยวชาญนิพจน์ทั่วไป ส่วนที่ 1 RegEx: 20 ขั้นตอนสั้นๆ เพื่อเชี่ยวชาญนิพจน์ทั่วไป ส่วนที่ 2: ในส่วนนี้ เราจะพูดถึงสิ่งที่ซับซ้อนกว่านี้เล็กน้อย แต่การควบคุมพวกมันเหมือนเมื่อก่อนจะไม่ใช่เรื่องยาก ฉันขอย้ำอีกครั้งว่าจริงๆ แล้ว RegEx นั้นง่ายกว่าที่คิดในตอนแรก และคุณไม่จำเป็นต้องเป็นนักวิทยาศาสตร์จรวดเพื่อที่จะเชี่ยวชาญและเริ่มใช้งานในทางปฏิบัติ ต้นฉบับภาษาอังกฤษของบทความนี้อยู่ที่นี่ 20 ขั้นตอนสั้นๆ เพื่อเชี่ยวชาญนิพจน์ทั่วไป  ส่วนที่ 3 - 1

ขั้นตอนที่ 11: วงเล็บ()เป็นการจับกลุ่ม

20 ขั้นตอนสั้นๆ เพื่อเชี่ยวชาญนิพจน์ทั่วไป  ส่วนที่ 3 - 2ในปัญหาสุดท้าย เราค้นหาค่าจำนวนเต็มและค่าตัวเลขทศนิยม (จุด) ประเภทต่างๆ แต่กลไกของนิพจน์ทั่วไปไม่ได้แยกความแตกต่างระหว่างค่าทั้งสองประเภทนี้ เนื่องจากทุกอย่างถูกบันทึกไว้ในนิพจน์ทั่วไปขนาดใหญ่เพียงนิพจน์เดียว เราสามารถบอกเอ็นจิ้นนิพจน์ทั่วไปให้แยกความแตกต่างระหว่างการจับคู่ประเภทต่างๆ หากเราใส่รูปแบบย่อของเราไว้ในวงเล็บ:
รูปแบบ: ([AZ])|([az]) 
string:   ประธานาธิบดีคนปัจจุบันของโบลิเวียคือ Evo Morales
ตรงกัน: ^^^ ^^^^^^^ ^^^^^^^^^ ^^ ^^^^^^^ ^^ ^^^ ^^^^^^^ 
กลุ่ม:    122 2222222 122222222 22 1222222 22 122 1222222  
( ตัวอย่าง ) นิพจน์ทั่วไปข้างต้นกำหนดกลุ่มการจับภาพสองกลุ่มที่ได้รับการจัดทำดัชนีเริ่มต้นที่ 1 กลุ่มการจับภาพกลุ่มแรกตรงกับอักษรตัวพิมพ์ใหญ่ตัวเดียว และกลุ่มการจับภาพที่สองจะตรงกับตัวอักษรตัวพิมพ์เล็กตัวใดตัวหนึ่ง ด้วยการใช้เครื่องหมาย 'หรือ' |และวงเล็บ()เป็นกลุ่มการจับ เราสามารถกำหนดนิพจน์ทั่วไปรายการเดียวที่ตรงกับสตริงหลายประเภท หากเราใช้สิ่งนี้กับ regex การค้นหาแบบยาว/ลอยจากส่วนก่อนหน้าของบทความ กลไก regex จะบันทึกการจับคู่ที่เกี่ยวข้องในกลุ่มที่เหมาะสม โดยการตรวจสอบว่าสตริงย่อยตรงกับกลุ่มใด เราสามารถระบุได้ทันทีว่าเป็นค่าทศนิยมหรือค่ายาว:
รูปแบบ: (\d*\.\d+[fF]|\d+\.\d*[fF]|\d+[fF])|(\d+[lL]) 
สตริง:   42L 12 x 3.4f 6l 3.3 0F LF .2F 0.
ตรงกัน: ^^^ ^^^^ ^^ ^^ ^^^ 
กลุ่ม:    222 1111 22 11 111  
( ตัวอย่าง ) นิพจน์ทั่วไปนี้ค่อนข้างซับซ้อน และเพื่อให้เข้าใจได้ดีขึ้น เรามาดูรายละเอียดแต่ละรูปแบบกันดีกว่า:
( // ตรงกับสตริงย่อย "float" ใด ๆ
  \d*\.\d+[fF]
  |
  \d+\.\d*[fF]
  |
  \d+[เอฟเอฟ]
)
| // หรือ
( // ตรงกับสตริงย่อย "ยาว" ใด ๆ
  \d+[ลิตร]
)
เครื่องหมาย|และกลุ่มการจับในวงเล็บ()ช่วยให้เราจับคู่สตริงย่อยประเภทต่างๆ ได้ ในกรณีนี้ เรากำลังจับคู่ตัวเลขทศนิยม "float" หรือจำนวนเต็มยาว "long"
(
  \d*\.\d+[fF] // 1+ หลักทางด้านขวาของจุดทศนิยม
  |
  \d+\.\d*[fF] // 1+ หลักทางด้านซ้ายของจุดทศนิยม
  |
  \d+[fF] // ไม่มีจุด มีเพียง 1+ หลักเท่านั้น
)
|
(
  \d+[lL] // ไม่มีจุด มีเพียง 1+ หลักเท่านั้น
)
ในกลุ่มการจับแบบ "ลอย" เรามีสามตัวเลือก: ตัวเลขที่มีจุดทศนิยมอย่างน้อย 1 หลักทางด้านขวาของจุดทศนิยม ตัวเลขที่มีจุดทศนิยมด้านซ้ายอย่างน้อย 1 หลัก และตัวเลขที่ไม่มีจุดทศนิยม รายการใดรายการหนึ่งเป็นแบบ "ลอย" ตราบใดที่มีตัวอักษร "f" หรือ "F" ต่อท้าย ภายในกลุ่มการจับแบบ "ยาว" เรามีทางเลือกเดียวเท่านั้น - เราต้องมีตัวเลข 1 หลักขึ้นไปตามด้วยอักขระ "l" หรือ "L" เอ็นจิ้นนิพจน์ทั่วไปจะค้นหาสตริงย่อยเหล่านี้ในสตริงที่กำหนดและจัดทำดัชนีให้อยู่ในกลุ่มการจับที่เหมาะสม บันทึกว่าเราไม่จับคู่ตัวเลขใดๆ ที่ไม่มี "l", "L", "f" หรือ "F" เพิ่มเข้าไป ควรจำแนกตัวเลขเหล่านี้อย่างไร? ถ้ามีจุดทศนิยม ภาษา Java จะมีค่าเริ่มต้นเป็น "สองเท่า" ไม่เช่นนั้นจะต้องเป็น "int"

มารวบรวมสิ่งที่เราได้เรียนรู้ด้วยปริศนาสองสามข้อ:

เพิ่มกลุ่มการบันทึกอีกสองกลุ่มใน regex ด้านบนเพื่อแยกประเภทตัวเลขสองเท่าหรือ int ด้วย (นี่เป็นคำถามที่ยุ่งยากอีกข้อหนึ่ง อย่าเพิ่งท้อแท้หากต้องใช้เวลาสักครู่ เป็นวิธีสุดท้ายที่จะเห็นวิธีแก้ปัญหาของฉัน)
ลวดลาย:
string:   42L 12 x 3.4f 6l 3.3 0F LF .2F 0. 
ไม้ขีดไฟ: ^^^ ^^ ^^^^ ^^ ^^^ ^^ ^^^ ^^ 
กลุ่ม:    333 44 1111 33 222 11 111 22
( วิธีแก้ไข ) ปัญหาต่อไปจะง่ายขึ้นเล็กน้อย ใช้กลุ่มการจับในวงเล็บ()เครื่องหมาย 'หรือ' |และช่วงอักขระเพื่อจัดเรียงอายุต่อไปนี้: "ถูกกฎหมายสำหรับการดื่มในสหรัฐอเมริกา" (>= 21) และ "ไม่ได้รับอนุญาตให้ดื่มในประเทศสหรัฐอเมริกา" (<21):
ลวดลาย:
สาย:   7 10 17 18 19 20 21 22 23 24 30 40 100 120 
ตรงกัน: ^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^^ ^^^ 
กลุ่ม:    2 22 22 22 22 22 11 11 11 11 11 11 111 111 
( สารละลาย )

ขั้นตอนที่ 12: ระบุการจับคู่ที่เฉพาะเจาะจงมากขึ้นก่อน

20 ขั้นตอนสั้นๆ เพื่อเชี่ยวชาญนิพจน์ทั่วไป  ส่วนที่ 3 - 3คุณอาจประสบปัญหากับงานสุดท้าย หากคุณพยายามกำหนดให้ "นักดื่มที่ถูกกฎหมาย" เป็นกลุ่มจับกลุ่มแรกแทนที่จะเป็นกลุ่มที่สอง เพื่อให้เข้าใจว่าทำไม เรามาดูตัวอย่างอื่นกัน สมมติว่าเราต้องการบันทึกนามสกุลที่มีอักขระน้อยกว่า 4 ตัวและนามสกุลที่มีอักขระ 4 ตัวขึ้นไปแยกกัน มาตั้งชื่อให้สั้นลงให้กับกลุ่มจับภาพกลุ่มแรกและดูว่าเกิดอะไรขึ้น:
รูปแบบ: ([AZ][az]?[az]?)|([AZ][az][az][az]+) 
สตริง:   Kim Job s Xu Clo yd Moh r Ngo Roc k.
ตรงกัน: ^^^ ^^^ ^^ ^^^ ^^^ ^^^ ^^^ 
กลุ่ม:    111 111 11 111 111 111 111   
( ตัวอย่าง ) ตามค่าเริ่มต้น เอ็นจิ้นนิพจน์ทั่วไปส่วนใหญ่ใช้การจับคู่ที่ละโมบกับอักขระพื้นฐานที่เราเคยเห็นมา ซึ่งหมายความว่าเอ็นจิ้นนิพจน์ทั่วไปจะจับกลุ่มที่ยาวที่สุดที่กำหนดไว้โดยเร็วที่สุดในนิพจน์ทั่วไปที่ให้ไว้ ดังนั้นในขณะที่กลุ่มที่สองข้างต้นสามารถจับอักขระได้มากขึ้นในชื่อ เช่น "จ็อบส์" และ "คลาวด์" เป็นต้น แต่เนื่องจากอักขระสามตัวแรกของชื่อเหล่านั้นถูกจับโดยกลุ่มจับแรกแล้ว จึงไม่สามารถจับได้อีกในวินาที . ตอนนี้เรามาทำการแก้ไขเล็กๆ น้อยๆ - เพียงเปลี่ยนลำดับของกลุ่มการจับ โดยวางกลุ่มที่เฉพาะเจาะจงมากขึ้น (ยาวกว่า) ก่อน:
รูปแบบ: ([AZ][az][az][az]+)|([AZ][az]?[az]?)สตริง
:   Kim Jobs Xu Cloyd Mohr Ngo Rock
ตรงกัน: ^^^ ^^^^ ^^ ^^^^^ ^^^^ ^^^ ^^^^ 
กลุ่ม:    222 1111 22 11111 1111 222 1111    
( ตัวอย่าง )

ภารกิจ...ครั้งนี้มีเพียงหนึ่งเดียวเท่านั้น :)

รูปแบบที่ "เจาะจงมากขึ้น" มักจะหมายถึง "ยาวขึ้น" เสมอ สมมติว่าเราต้องการค้นหา "คำ" สองประเภท: ประเภทแรกคือคำที่ขึ้นต้นด้วยสระ (โดยเฉพาะ) จากนั้นอีกประเภทที่ไม่ขึ้นต้นด้วยสระ (คำอื่นใด) ลองเขียนนิพจน์ทั่วไปเพื่อบันทึกและระบุสตริงที่ตรงกับสองกลุ่มนี้ (กลุ่มด้านล่างนี้ใช้ตัวอักษรแทนตัวเลข คุณต้องพิจารณาว่ากลุ่มใดควรตรงกับกลุ่มแรกและกลุ่มใดตรงกับกลุ่มที่สอง)
ลวดลาย:
สตริง:   pds6f uub 24r2gp ewqrty l ui_op 
ตรงกัน: ^^^^^ ^^^ ^^^^^^ ^^^^^^ ^ ^^^^^ 
กลุ่ม:    NNNNN VVV NNNNNN VVVVVV N VVVVV
( วิธีแก้ไข ) โดยทั่วไป ยิ่งนิพจน์ทั่วไปของคุณแม่นยำมากเท่าใด นิพจน์ทั่วไปก็จะยิ่งใช้เวลานานขึ้นเท่านั้น และยิ่งแม่นยำมากเท่าไร โอกาสที่คุณจะจับภาพสิ่งที่คุณไม่ต้องการก็จะน้อยลงเท่านั้น ดังนั้นแม้ว่ามันอาจจะดูน่ากลัว แต่ regexes ที่ยาวขึ้น ~= regexes ที่ดีกว่า น่าเสียดาย .

ขั้นตอนที่ 13: วงเล็บปีกกา{}ตามจำนวนการทำซ้ำที่กำหนด

20 ขั้นตอนสั้นๆ เพื่อเชี่ยวชาญนิพจน์ทั่วไป  ส่วนที่ 3 - 4ในตัวอย่างที่มีนามสกุลจากขั้นตอนที่แล้ว เรามี 2 กลุ่มที่เกือบจะซ้ำกันในรูปแบบเดียว:
รูปแบบ: ([AZ][az][az][az]+)|([AZ][az]?[az]?)สตริง
:   Kim Jobs Xu Cloyd Mohr Ngo Rock
ตรงกัน: ^^^ ^^^^ ^^ ^^^^^ ^^^^ ^^^ ^^^^ 
กลุ่ม:    222 1111 22 11111 1111 222 1111    
สำหรับกลุ่มแรก เราต้องการนามสกุลที่มีตัวอักษรสี่ตัวขึ้นไป กลุ่มที่สองต้องจับนามสกุลที่มีตัวอักษรสามตัวหรือน้อยกว่านั้น มีวิธีที่ง่ายกว่าในการเขียนสิ่งนี้มากกว่าการทำซ้ำ[a-z]กลุ่มเหล่านี้ซ้ำแล้วซ้ำอีกหรือไม่? มีอยู่หากคุณใช้เครื่องหมายปีกกาสำหรับสิ่ง{}นี้ วงเล็บปีกกา{}ช่วยให้เราระบุจำนวนการจับคู่ขั้นต่ำและ (เป็นทางเลือก) สูงสุดของอักขระก่อนหน้าหรือกลุ่มแคปเจอร์ได้ มีกรณีการใช้งานสามกรณี{}:
{X} // ตรงกับ X ครั้งทุกประการ
{X,} // ตรงกัน >= X ครั้ง
{X,Y} // จับคู่ >= X และ <= Y ครั้ง
นี่คือตัวอย่างของไวยากรณ์ที่แตกต่างกันทั้งสามนี้:
รูปแบบ: [az]{11} 
สตริง:   humuhumunuk unukuapua'a
การแข่งขัน: ^^^^^^^^^^^   
( ตัวอย่าง )
รูปแบบ: [az]{18,} 
สตริง:   humuhumunukunukuapua 'a.
การแข่งขัน: ^^^^^^^^^^^^^^^^^^^^^    
( ตัวอย่าง )
รูปแบบ: [az]{11,18} 
สตริง:   humuhumunukunukuap ua'a
การแข่งขัน: ^^^^^^^^^^^^^^^^^^    
( ตัวอย่าง ) มีหลายประเด็นที่ควรทราบในตัวอย่างข้างต้นบันทึก:. ขั้นแรก เมื่อใช้สัญลักษณ์ {X} อักขระหรือกลุ่มก่อนหน้าจะจับคู่ตัวเลข (X) คูณทุกประการ หากมีอักขระใน "คำ" มากกว่า (มากกว่าตัวเลข X) ที่สามารถตรงกับรูปแบบได้ (ดังแสดงในตัวอย่างแรก) อักขระเหล่านั้นจะไม่ถูกรวมไว้ในการจับคู่ หากจำนวนอักขระน้อยกว่า X การจับคู่ทั้งหมดจะล้มเหลว (ลองเปลี่ยน 11 เป็น 99 ในตัวอย่างแรก) ประการที่สอง สัญกรณ์ {X,} และ {X,Y} เป็นสัญลักษณ์โลภ พวกเขาจะพยายามจับคู่อักขระให้ได้มากที่สุดเท่าที่จะเป็นไปได้ในขณะที่ยังคงพึงพอใจกับนิพจน์ทั่วไปที่กำหนด หากคุณระบุ {3,7} จะสามารถจับคู่อักขระได้ 3 ถึง 7 ตัว และหากอักขระ 7 ตัวถัดไปถูกต้อง ระบบจะจับคู่อักขระทั้ง 7 ตัว หากคุณระบุ {1,} และตรงกับอักขระ 14,000 ตัวถัดไปทั้งหมด อักขระเหล่านั้นทั้ง 14,000 ตัวจะรวมอยู่ในสตริงที่เกี่ยวข้อง เราจะใช้ความรู้นี้เขียนนิพจน์ข้างต้นใหม่ได้อย่างไร การปรับปรุงที่ง่ายที่สุดอาจเป็นการแทนที่กลุ่มใกล้เคียง[a-z]ด้วย[a-z]{N}โดยที่ N จะถูกเลือกตามลำดับ:
รูปแบบ: ([AZ][az]{2}[az]+)|([AZ][az]?[az]?)  
...แต่นั่นไม่ได้ทำให้อะไรดีขึ้นมากนัก ดูกลุ่มการจับกลุ่มแรก: เรามี[a-z]{2}(ซึ่งตรงกับตัวอักษรพิมพ์เล็ก 2 ตัวพอดี) ตามด้วย[a-z]+(ซึ่งตรงกับตัวอักษรพิมพ์เล็ก 1 ตัวขึ้นไป) เราสามารถทำให้สิ่งนี้ง่ายขึ้นโดยขอให้ใช้อักษรตัวพิมพ์เล็ก 3 ตัวขึ้นไปโดยใช้เครื่องหมายปีกกา:
รูปแบบ: ([AZ][az]{3,})|([AZ][az]?[az]?) 
กลุ่มการจับที่สองนั้นแตกต่างออกไป เราจำเป็นต้องมีอักขระไม่เกินสามตัวในนามสกุลเหล่านี้ ซึ่งหมายความว่าเรามีขีดจำกัดบน แต่ขีดจำกัดล่างของเราคือศูนย์:
รูปแบบ: ([AZ][az]{3,})|([AZ][az]{0,2}) 
ความเฉพาะเจาะจงจะดีกว่าเสมอเมื่อใช้นิพจน์ทั่วไป ดังนั้นจึงควรหยุดเพียงแค่นั้น แต่ฉันอดไม่ได้ที่จะสังเกตว่าช่วงอักขระทั้งสองนี้ ( [AZ]และ[az]) ที่อยู่ติดกันดูเหมือนคลาส "อักขระคำ" \w( [A-Za-z0-9_]) . หากเรามั่นใจว่าข้อมูลของเรามีเฉพาะนามสกุลที่มีรูปแบบที่ดี เราก็สามารถทำให้นิพจน์ทั่วไปของเราง่ายขึ้นและเขียนง่ายๆ:
รูปแบบ: (\w{4,})|(\w{1,3}) 
กลุ่มแรกจับลำดับใดๆ ที่มี "อักขระคำ" 4 ตัวขึ้นไป ( [A-Za-z0-9_]) และกลุ่มที่สองจับลำดับใดๆ ที่มี "อักขระคำ" ตั้งแต่ 1 ถึง 3 ตัว (รวม) มันจะได้ผลไหม?
รูปแบบ: (\w{4,})|(\w{1,3}) 
สตริง:   Kim Jobs Xu Cloyd Mohr Ngo Rock
ตรงกัน: ^^^ ^^^^ ^^ ^^^^^ ^^^^ ^^^ ^^^^ 
กลุ่ม:    222 1111 22 11111 1111 222 1111    
( ตัวอย่าง ) มันได้ผล! แนวทางนี้เป็นอย่างไร? และมันสะอาดกว่าตัวอย่างก่อนหน้านี้มาก เนื่องจากกลุ่มการจับกลุ่มแรกตรงกับนามสกุลทั้งหมดที่มีอักขระสี่ตัวขึ้นไป เราจึงสามารถเปลี่ยนกลุ่มการจับกลุ่มที่สองเป็นแบบง่ายๆ ได้\w+เนื่องจากจะทำให้เราสามารถจับนามสกุลที่เหลือทั้งหมด (ที่มีอักขระ 1, 2 หรือ 3 ตัว):
รูปแบบ: (\w{4,})|(\w+) 
สตริง:   Kim Jobs Xu Cloyd Mohr Ngo Rock
ตรงกัน: ^^^ ^^^^ ^^ ^^^^^ ^^^^ ^^^ ^^^^ 
กลุ่ม:    222 1111 22 11111 1111 222 1111    
( ตัวอย่าง )

มาช่วยให้สมองเรียนรู้สิ่งนี้และแก้ไขปัญหา 2 ข้อต่อไปนี้:

ใช้เครื่องหมายปีกกา{}เพื่อเขียนนิพจน์ทั่วไปการค้นหาหมายเลขประกันสังคมจากขั้นตอนที่ 7 ใหม่:
ลวดลาย:
สายอักขระ: 113-25=1902 182-82-0192 H23-_3-9982 1I1-O0-E38B
การแข่งขัน:              ^^^^^^^^^^^
( วิธีแก้ไข ) สมมติว่าตัวตรวจสอบความแข็งแกร่งของรหัสผ่านของเว็บไซต์กำหนดให้รหัสผ่านของผู้ใช้มีความยาวระหว่าง 6 ถึง 12 อักขระ เขียนนิพจน์ทั่วไปที่ทำเครื่องหมาย รหัสผ่าน ที่ไม่ถูกต้องในรายการด้านล่าง รหัสผ่านแต่ละอันอยู่ในวงเล็บ()เพื่อให้จับคู่ได้ง่าย ดังนั้น ตรวจสอบให้แน่ใจว่านิพจน์ทั่วไปขึ้นต้นและลงท้ายด้วยอักขระ ตามตัวอักษร (และ สัญลักษณ์ )คำแนะนำ: ตรวจสอบให้แน่ใจว่าคุณไม่อนุญาตให้ใช้วงเล็บตามตัวอักษรในรหัสผ่านที่มี[^()]หรือคล้ายกัน ไม่เช่นนั้น คุณจะจับคู่สตริงทั้งหมด!
ลวดลาย:
สตริง:   (12345) (รหัสผ่านของฉัน) (Xanadu.2112) (su_do) (ของพนักงานขาย!)
การแข่งขัน: ^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^  
( สารละลาย )

ขั้นตอนที่ 14: \bสัญลักษณ์เส้นขอบความกว้างเป็นศูนย์

20 ขั้นตอนสั้นๆ เพื่อเชี่ยวชาญนิพจน์ทั่วไป  ส่วนที่ 3 - 5งานสุดท้ายค่อนข้างยาก แต่ถ้าเราทำให้มันซับซ้อนขึ้นอีกหน่อยโดยการใส่รหัสผ่านในเครื่องหมายคำพูด""แทนวงเล็บล่ะ()? เราสามารถเขียนวิธีแก้ปัญหาที่คล้ายกันโดยแทนที่อักขระในวงเล็บทั้งหมดด้วยเครื่องหมายคำพูดได้หรือไม่
รูปแบบ: \"[^"]{0.5}\"|\"[^"]+\s[^"]*\" 
string:   "12345" "รหัสผ่านของฉัน" "Xanadu.2112 " " su_do" " OfSalesmen! "
การแข่งขัน: ^^^^^^^ ^^^^^^^^^^^^^ ^^^ ^^^  
( ตัวอย่าง ) มันไม่ได้ออกมาน่าประทับใจมากนัก คุณเคยเดาแล้วว่าทำไม? ปัญหาคือเรากำลังมองหารหัสผ่านที่ไม่ถูกต้องที่นี่ "Xanadu.2112" เป็นรหัสผ่านที่ดี ดังนั้นเมื่อ regex พบว่าลำดับนี้ไม่มีการเว้นวรรคหรืออักขระตามตัว"อักษร มันจะให้ไว้หน้าอักขระ"ที่รับรองรหัสผ่านทางด้านขวา (เนื่องจากเราระบุว่า"ไม่พบอักขระในรหัสผ่านโดยใช้[^"]) เมื่อเอ็นจิ้นนิพจน์ทั่วไปพอใจแล้วว่าอักขระเหล่านั้นไม่ตรงกับนิพจน์ทั่วไป ระบบจะทำงานอีกครั้งในตำแหน่งที่ค้างไว้ - ตำแหน่งที่อักขระอยู่"ซึ่งจำกัด " Xanadu.2112" ด้านขวา. จากนั้นเขาเห็นตัวละครอวกาศตัวหนึ่งและตัวละครอีกตัว"- สำหรับเขานี่คือรหัสผ่านที่ผิด! โดยพื้นฐานแล้ว เขาพบลำดับนี้" "และเดินหน้าต่อไป นี่ไม่ใช่สิ่งที่เราอยากได้เลย... คงจะดีถ้าเราระบุได้ว่าอักขระตัวแรกของรหัสผ่านไม่ ควร เว้นวรรค มีวิธีการทำเช่นนี้หรือไม่? (ถึงตอนนี้คุณคงรู้แล้วว่าคำตอบสำหรับคำถามเชิงวาทศิลป์ทั้งหมดของฉันคือ "ใช่") ใช่! มีวิธีดังกล่าว! \bเอ็นจินนิพจน์ทั่วไปจำนวนมาก มีลำดับการหลีก เช่น "ขอบเขตคำ" "ขอบเขตของคำ" \bคือลำดับการหลีกที่มีความกว้างเป็นศูนย์ ซึ่งค่อนข้างจะตรงกับขอบเขตของคำ โปรดจำไว้ว่าเมื่อเราพูด ว่า"คำ" เราหมายถึงลำดับของอักขระในชั้นเรียน\wหรือ [A-Za-z0-9_]การจับคู่ขอบเขตคำหมายความว่าอักขระที่อยู่ข้างหน้าหรือหลังลำดับ\bจะต้องเป็นнеอักขระคำ อย่างไรก็ตาม เมื่อจับคู่ เราจะไม่รวมอักขระนี้ในสตริงย่อยที่บันทึกไว้ นี่คือความกว้างเป็นศูนย์ หากต้องการดูวิธีการทำงาน โปรดดูตัวอย่างเล็กๆ น้อยๆ:
รูปแบบ: \b [ ^ ]+\b string 
:   ยังคงต้องการเงิน Lebowski
การแข่งขัน: ^^ ^^^^^ ^^^^ ^^ ^^^^^ ^^^^^^^^  
( ตัวอย่าง ) ลำดับ[^ ]จะต้องตรงกับอักขระใดๆ ที่ไม่ใช่อักขระช่องว่างตามตัวอักษร เหตุใดจึงไม่ตรงกับเครื่องหมายจุลภาค,หลังเงินหรือจุด " .ตามหลัง Lebowski เนื่องจากเครื่องหมายจุลภาค,และจุด.ไม่ใช่อักขระคำ จึงสร้างขอบเขตระหว่างอักขระคำและอักขระที่ไม่ใช่คำ โดยปรากฏระหว่างyส่วนท้ายของอักขระ คำว่า เงิน และลูกน้ำ,ที่ตามมา และระหว่าง " iคำว่า Lebowski กับจุด.(จุด/จุด) ที่ตามมา นิพจน์ทั่วไปจะจับคู่กับขอบเขตของคำเหล่านี้ (แต่ไม่ใช่กับอักขระที่ไม่ใช่คำที่ช่วยกำหนดเท่านั้น) แต่จะเกิดอะไรขึ้นหากเราไม่รวมความสอดคล้องไว้\bในเทมเพลตของเรา
รูปแบบ: [^ ]+ 
สตริง:   ฉันยังคงต้องการเงิน Lebowski 
การแข่งขัน: ^^ ^^^^^ ^^^^ ^^ ^^^^^^ ^^^^^^^^^  
( ตัวอย่าง ) ใช่แล้ว ตอนนี้เราพบเครื่องหมายวรรคตอนเหล่านี้แล้ว ตอนนี้เรามาใช้ขอบเขตของคำเพื่อแก้ไข regex สำหรับรหัสผ่านที่ยกมา:
รูปแบบ: \"\b[^"]{0.5}\b\"|\"\b[^"]+\s[^"]*\b\" 
string:   "12345" "mypassword" " Xanadu. 2112" "su_do" "ของนักขาย!"
การแข่งขัน: ^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^  
( ตัวอย่าง ) โดยการใส่ขอบเขตคำไว้ในเครื่องหมายคำพูด ("\b ... \b") เรากำลังบอกได้อย่างมีประสิทธิภาพว่าอักขระตัวแรกและตัวสุดท้ายของรหัสผ่านที่ตรงกันจะต้องเป็น "อักขระคำ" ดังนั้นวิธีนี้ใช้ได้ดีที่นี่ แต่จะใช้งานไม่ได้หากอักขระตัวแรกหรือตัวสุดท้ายของรหัสผ่านของผู้ใช้ไม่ใช่อักขระคำ:
รูปแบบ: \"\b[^"]{0.5}\b\"|\"\b[^"]+\s[^"]*\b\"
สตริง: "รหัสผ่านต่อไปนี้istooshort" "C ++"
การแข่งขัน:   
( ตัวอย่าง ) ดูว่ารหัสผ่านที่สองไม่ได้ทำเครื่องหมายว่า "ไม่ถูกต้อง" แม้ว่าจะสั้นเกินไปอย่างชัดเจนก็ตาม คุณจะต้องเป็นระมัดระวังด้วยลำดับ\bเนื่องจากจะจับคู่เฉพาะขอบเขตระหว่างอักขระเท่านั้น\wไม่ใช่ \wในตัวอย่างข้างต้น เนื่องจากเราอนุญาตให้ใช้อักขระที่ไม่ ในรหัสผ่าน\wขอบเขตระหว่างอักขระ ตัวแรก/ตัว สุดท้าย \ของรหัสผ่านจึงไม่รับประกันว่าเป็นขอบเขตของคำ\b

เพื่อให้ขั้นตอนนี้เสร็จสมบูรณ์ เราจะแก้ไขปัญหาง่ายๆ เพียงปัญหาเดียวเท่านั้น:

ขอบเขตของคำมีประโยชน์ในกลไกการเน้นไวยากรณ์เมื่อเราต้องการจับคู่ลำดับอักขระเฉพาะ แต่ต้องการให้แน่ใจว่าจะเกิดขึ้นที่จุดเริ่มต้นหรือจุดสิ้นสุดของคำเท่านั้น (หรือด้วยตัวมันเอง) สมมติว่าเรากำลังเขียนการเน้นไวยากรณ์และต้องการเน้นคำว่า var แต่เฉพาะเมื่อมันปรากฏด้วยตัวเองเท่านั้น (โดยไม่ต้องแตะอักขระอื่นในคำนั้น) คุณสามารถเขียนนิพจน์ทั่วไปสำหรับสิ่งนี้ได้หรือไม่? แน่นอนคุณทำได้ มันเป็นงานที่เรียบง่ายมาก ;)
ลวดลาย:
สตริง:   var varx _var ( var j) barvarcar * var var -> { var }
ตรงกัน: ^^^ ^^^ ^^^ ^^^ ^^^  
( สารละลาย )

ขั้นตอนที่ 15: "คาเร็ต" ^เป็น "จุดเริ่มต้นของบรรทัด" และเครื่องหมายดอลลาร์$เป็น "สิ้นสุดบรรทัด"

20 ขั้นตอนสั้นๆ เพื่อเชี่ยวชาญนิพจน์ทั่วไป  ตอนที่ 3 - 6ลำดับขอบเขตของคำ\b(จากขั้นตอนสุดท้ายของส่วนก่อนหน้าของบทความ) ไม่ใช่ลำดับความกว้างเป็นศูนย์พิเศษเพียงลำดับเดียวที่พร้อมใช้งานในนิพจน์ทั่วไป สองรายการที่ได้รับความนิยมมากที่สุดคือ "คาเร็ต" ^- "จุดเริ่มต้นของบรรทัด" และเครื่องหมายดอลลาร์$- "สิ้นสุดบรรทัด" การรวมหนึ่งในนิพจน์ทั่วไปของคุณหมายความว่ารายการที่ตรงกันจะต้องปรากฏที่จุดเริ่มต้นหรือจุดสิ้นสุดของสตริงต้นฉบับ:
รูปแบบ: ^start|end$ 
string:   start end start end start end start end 
ตรงกัน: ^^^^^ ^^^  
( ตัวอย่าง ) หากสตริงของคุณมีการขึ้นบรรทัดใหม่ มัน^startจะจับคู่ลำดับ "เริ่มต้น" ที่จุดเริ่มต้นของบรรทัดใดๆ และend$จะจับคู่ลำดับ "สิ้นสุด" ที่ท้ายบรรทัดใดๆ (แม้ว่าจะแสดงได้ยากที่นี่ก็ตาม) สัญลักษณ์เหล่านี้มีประโยชน์อย่างยิ่งเมื่อทำงานกับข้อมูลที่มีตัวคั่น กลับไปที่ปัญหา "ขนาดไฟล์" จากขั้นตอนที่ 9 โดยใช้^"จุดเริ่มต้นของบรรทัด" ในตัวอย่างนี้ ขนาดไฟล์ของเราจะถูกคั่นด้วยช่องว่าง " " ดังนั้นเราจึงต้องการให้แต่ละขนาดไฟล์เริ่มต้นด้วยตัวเลข นำหน้าด้วยอักขระเว้นวรรคหรือจุดเริ่มต้นของบรรทัด:
รูปแบบ: (^| )(\d+|\d+\.\d+)[KMGT]Bสตริง
:   6.6KB 1..3KB 12KB 5G 3.3MB KB .6.2TB 9MB
ตรงกัน: ^^^^^ ^^^^^ ^^^^^^ ^^^^ 
กลุ่ม:    222 122 1222 12    
( ตัวอย่าง ) เราใกล้ถึงเป้าหมายแล้ว! แต่คุณอาจสังเกตเห็นว่าเรายังคงมีปัญหาเล็กๆ น้อยๆ อยู่ประการหนึ่ง นั่นคือ เรากำลังจับคู่อักขระเว้นวรรคก่อนขนาดไฟล์ที่ถูกต้อง ตอนนี้เราสามารถเพิกเฉยต่อกลุ่มการจับภาพนี้ (1) เมื่อเอ็นจิ้นนิพจน์ทั่วไปของเราพบมัน หรือเราสามารถใช้กลุ่มที่ไม่ได้จับภาพ ซึ่งเราจะเห็นในขั้นตอนถัดไป

ในระหว่างนี้เรามาแก้ไขปัญหาโทนเสียงอีก 2 ข้อ:

ดำเนินการต่อด้วยตัวอย่างการเน้นไวยากรณ์ของเราจากขั้นตอนสุดท้าย การเน้นไวยากรณ์บางส่วนจะทำเครื่องหมายช่องว่างต่อท้าย ซึ่งก็คือช่องว่างใดๆ ที่อยู่ระหว่างอักขระที่ไม่ใช่ช่องว่างและจุดสิ้นสุดของบรรทัด คุณสามารถเขียน regex เพื่อเน้นเฉพาะช่องว่างต่อท้ายได้หรือไม่?
ลวดลาย:
สตริง: myvec <- c(1, 2, 3, 4, 5)  
ตรงกัน:                          ^^^^^^^  
( โซลูชัน ) ตัวแยกวิเคราะห์ค่าที่คั่นด้วยเครื่องหมายจุลภาค (CSV) แบบธรรมดาจะค้นหา "โทเค็น" ที่คั่นด้วยเครื่องหมายจุลภาค ""โดยทั่วไป ช่อง ว่างจะไม่มีความหมายเว้นแต่จะอยู่ในเครื่องหมายคำพูด เขียนนิพจน์ทั่วไปในการแยกวิเคราะห์ CSV อย่างง่ายที่จับคู่โทเค็นระหว่างเครื่องหมายจุลภาค แต่ละเว้น (ไม่ได้บันทึก) ช่องว่างที่ไม่ได้ อยู่ ระหว่างเครื่องหมายคำพูด
ลวดลาย:
สตริง:   a, "b", "c d",e,f, "g h", dfgi,, k, "", l 
ตรงกับ: ^^ ^^^^ ^^^^^^^^^^ ^^^ ^^^ ^^^^^^ ^^ ^^^ ^ 
กลุ่ม:    21 2221 2222212121 222221 222211 21 221 2    
( โซลูชัน ) RegEx: 20 ขั้นตอนสั้น ๆ เพื่อเชี่ยวชาญนิพจน์ทั่วไป ตอนที่ 4
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION