กลุ่มที่ไม่ได้รับการจับภาพเช่น(?:)
ใช้ในนิพจน์ทั่วไปและกลุ่มใดที่ดีสำหรับ
กลุ่มที่ไม่ได้รับการจับภาพเช่น(?:)
ใช้ในนิพจน์ทั่วไปและกลุ่มใดที่ดีสำหรับ
คำตอบ:
ให้ฉันพยายามอธิบายสิ่งนี้ด้วยตัวอย่าง
พิจารณาข้อความต่อไปนี้:
http://stackoverflow.com/
/programming/tagged/regex
ตอนนี้ถ้าฉันใช้ regex ด้านล่างมัน ...
(https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?
... ฉันจะได้ผลลัพธ์ต่อไปนี้:
Match "http://stackoverflow.com/"
Group 1: "http"
Group 2: "stackoverflow.com"
Group 3: "/"
Match "/programming/tagged/regex"
Group 1: "https"
Group 2: "stackoverflow.com"
Group 3: "/questions/tagged/regex"
แต่ฉันไม่สนใจเกี่ยวกับโปรโตคอล - ฉันแค่ต้องการโฮสต์และเส้นทางของ URL ดังนั้นฉันจึงเปลี่ยน regex (?:)
ที่จะรวมถึงกลุ่มที่ไม่ได้จับ
(?:https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?
ตอนนี้ผลของฉันมีลักษณะเช่นนี้:
Match "http://stackoverflow.com/"
Group 1: "stackoverflow.com"
Group 2: "/"
Match "/programming/tagged/regex"
Group 1: "stackoverflow.com"
Group 2: "/questions/tagged/regex"
ดู? กลุ่มแรกยังไม่ถูกจับ โปรแกรมแยกวิเคราะห์ใช้เพื่อจับคู่ข้อความ แต่จะละเว้นภายหลังในผลลัพธ์สุดท้าย
ขอให้ฉันพยายามอธิบายกลุ่มด้วย
กลุ่มมีจุดประสงค์มากมาย พวกเขาสามารถช่วยคุณดึงข้อมูลที่แน่นอนจากการจับคู่ที่ใหญ่กว่า (ซึ่งสามารถตั้งชื่อได้) พวกเขาช่วยให้คุณสามารถจับคู่กลุ่มที่ตรงกันก่อนหน้านี้และสามารถใช้สำหรับการทดแทน ลองตัวอย่างบางส่วนได้ไหม
ลองนึกภาพคุณมี XML หรือ HTML บางประเภท (โปรดทราบว่าregex อาจไม่ใช่เครื่องมือที่ดีที่สุดสำหรับงานแต่มันก็เป็นตัวอย่างที่ดี) คุณต้องการแยกแท็กดังนั้นคุณสามารถทำสิ่งนี้ (ฉันได้เพิ่มช่องว่างเพื่อให้ง่ายต่อการเข้าใจ):
\<(?<TAG>.+?)\> [^<]*? \</\k<TAG>\>
or
\<(.+?)\> [^<]*? \</\1\>
regex แรกมีกลุ่มชื่อ (TAG) ในขณะที่สองใช้กลุ่มทั่วไป regexes ทั้งสองทำสิ่งเดียวกัน: พวกเขาใช้ค่าจากกลุ่มแรก (ชื่อของแท็ก) เพื่อจับคู่แท็กปิด ความแตกต่างคือชื่อแรกใช้ชื่อเพื่อให้ตรงกับค่าและอีกรายการหนึ่งใช้ดัชนีกลุ่ม (ซึ่งเริ่มต้นที่ 1)
ลองเปลี่ยนบางสิ่งแทนตอนนี้ พิจารณาข้อความต่อไปนี้:
Lorem ipsum dolor sit amet consectetuer feugiat fames malesuada pretium egestas.
ทีนี้ลองใช้ regex ใบ้ตัวนี้ดู:
\b(\S)(\S)(\S)(\S*)\b
regex นี้จับคู่คำที่มีอักขระอย่างน้อย 3 ตัวและใช้กลุ่มเพื่อแยกตัวอักษรสามตัวแรก ผลลัพธ์คือ:
Match "Lorem"
Group 1: "L"
Group 2: "o"
Group 3: "r"
Group 4: "em"
Match "ipsum"
Group 1: "i"
Group 2: "p"
Group 3: "s"
Group 4: "um"
...
Match "consectetuer"
Group 1: "c"
Group 2: "o"
Group 3: "n"
Group 4: "sectetuer"
...
ดังนั้นหากเราใช้สตริงการแทนที่:
$1_$3$2_$4
... เหนือนั้นเราพยายามใช้กลุ่มแรกเพิ่มขีดล่างใช้กลุ่มที่สามจากนั้นกลุ่มที่สองเพิ่มขีดล่างอีกกลุ่มจากนั้นกลุ่มที่สี่ สตริงที่เกิดจะเป็นเช่นเดียวกับด้านล่าง
L_ro_em i_sp_um d_lo_or s_ti_ a_em_t c_no_sectetuer f_ue_giat f_ma_es m_la_esuada p_er_tium e_eg_stas.
${name}
คุณสามารถใช้กลุ่มตั้งชื่อตามชื่อแทนเกินไปใช้
หากต้องการเล่นกับ regexes ฉันขอแนะนำhttp://regex101.com/ซึ่งให้รายละเอียดจำนวนมากเกี่ยวกับการทำงานของ regex มันยังมีเอ็นจิ้น regex ให้เลือกอีกด้วย
คุณสามารถใช้กลุ่มการจับภาพเพื่อจัดระเบียบและแยกการแสดงออก กลุ่มที่ไม่ได้รับการบันทึกมีผลประโยชน์แรก แต่ไม่มีค่าใช้จ่ายในส่วนที่สอง คุณยังสามารถพูดได้ว่ากลุ่มที่ไม่มีการจับภาพเป็นตัวเลือกตัวอย่างเช่น
สมมติว่าคุณต้องการจับคู่ข้อความที่เป็นตัวเลข แต่บางตัวเลขสามารถเขียนเป็น 1, 2, 3, 4, ... หากคุณต้องการจับภาพส่วนที่เป็นตัวเลข แต่ไม่ใช่ส่วนต่อท้าย (เป็นทางเลือก) คุณสามารถใช้กลุ่มที่ไม่จับภาพได้ .
([0-9]+)(?:st|nd|rd|th)?
ที่จะจับคู่ตัวเลขในรูปแบบ 1, 2, 3 ... หรือในรูปแบบที่ 1, 2, 3, ... แต่จะจับเฉพาะส่วนที่เป็นตัวเลขเท่านั้น
?:
จะใช้เมื่อคุณต้องการจัดกลุ่มนิพจน์ แต่คุณไม่ต้องการบันทึกเป็นส่วนที่จับคู่ / จับได้ของสตริง
ตัวอย่างจะเป็นสิ่งที่ตรงกับที่อยู่ IP:
/(?:\d{1,3}\.){3}\d{1,3}/
โปรดทราบว่าฉันไม่สนใจเกี่ยวกับการบันทึก 3 octets แรก แต่การ(?:...)
จัดกลุ่มช่วยให้ฉันสามารถทำให้ regex สั้นลงโดยไม่เกิดค่าใช้จ่ายในการจับและจัดเก็บการแข่งขัน
มันทำให้กลุ่มไม่จับภาพซึ่งหมายความว่าสตริงย่อยที่จับคู่โดยกลุ่มนั้นจะไม่รวมอยู่ในรายการจับ ตัวอย่างในทับทิมเพื่อแสดงความแตกต่าง:
"abc".match(/(.)(.)./).captures #=> ["a","b"]
"abc".match(/(?:.)(.)./).captures #=> ["b"]
(?:)
มีประโยชน์เมื่อคุณต้องการจัดกลุ่มนิพจน์ย่อย (พูดเมื่อคุณต้องการใช้ปริมาณกับนิพจน์ย่อยที่ไม่ใช่อะตอมมิกหรือถ้าคุณต้องการ จำกัด ขอบเขตของ a ) แต่คุณไม่ต้องการจับอะไรเลย (?:)
(?:)
|
แรงจูงใจทางประวัติศาสตร์:
การมีอยู่ของกลุ่มที่ไม่ได้ดักจับสามารถอธิบายได้ด้วยการใช้วงเล็บ
พิจารณาสำนวน(a|b)c
และa|bc
เนื่องจากความสำคัญของการต่อกันมากกว่า|
การแสดงออกเหล่านี้เป็นตัวแทนของสองภาษาที่แตกต่างกัน ( {ac, bc}
และ{a, bc}
ตามลำดับ)
อย่างไรก็ตามวงเล็บยังใช้เป็นกลุ่มจับคู่ (ตามที่อธิบายโดยคำตอบอื่น ๆ ... )
เมื่อคุณต้องการให้มีวงเล็บ แต่ไม่จับนิพจน์ย่อยคุณใช้กลุ่มที่ไม่ใช่การจับภาพ ในตัวอย่าง(?:a|b)c
ให้ฉันลองทำสิ่งนี้ด้วยตัวอย่าง:
รหัส Regex: (?:animal)(?:=)(\w+)(,)\1\2
สตริงการค้นหา:
บรรทัด 1 - animal=cat,dog,cat,tiger,dog
บรรทัด 2 - animal=cat,cat,dog,dog,tiger
บรรทัด 3 - animal=dog,dog,cat,cat,tiger
(?:animal)
-> กลุ่มที่ไม่ถูกจับ 1
(?:=)
-> กลุ่มที่ไม่ได้จับภาพ 2
(\w+)
-> กลุ่มที่ถูกจับ 1
(,)
-> กลุ่มที่ถูกจับ 2
\1
-> ผลลัพธ์ของกลุ่มที่ถูกจับ 1 คือในบรรทัดที่ 1 คือแมวในบรรทัดที่ 2 คือแมวในบรรทัดที่ 3 คือสุนัข
\2
-> ผลการจับภาพกลุ่มที่ 2 เช่นจุลภาค (,)
ดังนั้นในรหัสนี้โดยการให้\1
และ\2
เราจำหรือทำซ้ำผลลัพธ์ของการจับกลุ่ม 1 และ 2 ตามลำดับในรหัส
ตามคำสั่งของรหัส(?:animal)
ควรเป็นกลุ่ม 1 และ(?:=)
ควรเป็นกลุ่ม 2 และดำเนินการต่อ ..
แต่โดยการให้?:
เราสร้างกลุ่มการจับคู่ที่ไม่ถูกจับ (ซึ่งไม่นับรวมในกลุ่มที่จับคู่ดังนั้นหมายเลขการจัดกลุ่มจะเริ่มจากกลุ่มที่ถูกจับครั้งแรกและไม่ใช่กลุ่มที่ไม่ถูกจับ) เพื่อให้เกิดการซ้ำของผลลัพธ์ของกลุ่ม(?:animal)
ไม่สามารถเรียกรหัสในภายหลังได้
หวังว่านี่จะอธิบายการใช้กลุ่มที่ไม่ได้รับการดักจับ
กลุ่มที่จับคุณสามารถใช้ในภายหลังใน regex เพื่อจับคู่หรือคุณสามารถใช้พวกเขาในส่วนทดแทนของ regex การทำให้กลุ่มที่ไม่ถูกดักจับนั้นยกเว้นกลุ่มนั้นไม่ให้ถูกใช้ด้วยเหตุผลเหล่านี้
กลุ่มที่ไม่ได้จับภาพนั้นยอดเยี่ยมหากคุณพยายามจับภาพสิ่งต่าง ๆ มากมายและมีบางกลุ่มที่คุณไม่ต้องการจับภาพ
นั่นเป็นเหตุผลที่พวกเขามีอยู่จริง ในขณะที่คุณเรียนรู้เกี่ยวกับกลุ่มเรียนรู้เกี่ยวกับกลุ่มอะตอมพวกเขาทำอะไรมากมาย นอกจากนี้ยังมีกลุ่ม lookaround แต่มีความซับซ้อนน้อยกว่าและไม่ได้ใช้มากนัก
ตัวอย่างการใช้ในภายหลังใน regex (backreference):
<([A-Z][A-Z0-9]*)\b[^>]*>.*?</\1>
[ค้นหาแท็ก xml (ไม่สนับสนุน ns)]
([A-Z][A-Z0-9]*)
เป็นกลุ่มที่จับภาพ (ในกรณีนี้คือ tagname)
ในภายหลังใน regex \1
ซึ่งหมายความว่าจะตรงกับข้อความเดียวกับที่อยู่ในกลุ่มแรก ( ([A-Z][A-Z0-9]*)
กลุ่ม) (ในกรณีนี้มันตรงกับแท็กสิ้นสุด)
ฉันเป็นนักพัฒนา JavaScript และจะพยายามอธิบายความสำคัญของ JavaScript ที่เกี่ยวข้อง
พิจารณาสถานการณ์ที่คุณต้องการจับคู่cat is animal
เมื่อคุณต้องการจับคู่แมวและสัตว์และทั้งคู่ควรis
อยู่ระหว่าง
// this will ignore "is" as that's is what we want
"cat is animal".match(/(cat)(?: is )(animal)/) ;
result ["cat is animal", "cat", "animal"]
// using lookahead pattern it will match only "cat" we can
// use lookahead but the problem is we can not give anything
// at the back of lookahead pattern
"cat is animal".match(/cat(?= is animal)/) ;
result ["cat"]
//so I gave another grouping parenthesis for animal
// in lookahead pattern to match animal as well
"cat is animal".match(/(cat)(?= is (animal))/) ;
result ["cat", "cat", "animal"]
// we got extra cat in above example so removing another grouping
"cat is animal".match(/cat(?= is (animal))/) ;
result ["cat", "animal"]
ในนิพจน์ทั่วไปที่ซับซ้อนคุณอาจมีสถานการณ์เกิดขึ้นเมื่อคุณต้องการใช้กลุ่มจำนวนมากบางกลุ่มมีการจับคู่ซ้ำและบางกลุ่มใช้เพื่อให้มีการอ้างอิงกลับ โดยค่าเริ่มต้นการจับคู่ข้อความแต่ละกลุ่มจะถูกโหลดลงในอาร์เรย์ย้อนกลับ ในกรณีที่เรามีกลุ่มจำนวนมากและต้องสามารถอ้างอิงบางกลุ่มจากแถวลำดับ backreference เราสามารถแทนที่พฤติกรรมเริ่มต้นนี้เพื่อบอกการแสดงออกปกติว่ากลุ่มบางกลุ่มมีเฉพาะสำหรับการจัดการการทำซ้ำและไม่จำเป็นต้องบันทึกและจัดเก็บ ในอาร์เรย์ backreference
ฉันไม่สามารถแสดงความคิดเห็นกับคำตอบยอดนิยมในการพูดแบบนี้: ฉันต้องการเพิ่มจุดที่ชัดเจนซึ่งมีการบอกเป็นนัยในคำตอบยอดนิยมเท่านั้น:
กลุ่มที่ไม่จับภาพ(?...)
ไม่ได้ลบอักขระใด ๆออกจากการแข่งขันเต็มรูปแบบเดิมเท่านั้นจัดระเบียบ regex ใหม่ให้กับโปรแกรมเมอร์
ในการเข้าถึงส่วนเฉพาะของ regex โดยไม่มีการกำหนดอักขระภายนอกคุณจะต้องใช้เสมอ .group(<index>)
tl; drกลุ่มที่ไม่ได้ดักจับตามชื่อที่แนะนำคือส่วนของ regex ที่คุณไม่ต้องการรวมไว้ในการแข่งขันและ?:
เป็นวิธีในการกำหนดกลุ่มให้เป็นกลุ่มที่ไม่ได้จับภาพ
example@example.com
สมมติว่าคุณมีที่อยู่อีเมล regex ต่อไปนี้จะสร้างสองกลุ่มคือส่วน id และ @ example.com ส่วน (\p{Alpha}*[a-z])(@example.com)
. เพื่อความเรียบง่ายเรากำลังแยกชื่อโดเมนทั้งหมดรวมถึง@
ตัวละคร
ตอนนี้สมมติว่าคุณต้องการเพียงส่วน id ของที่อยู่ สิ่งที่คุณต้องการจะทำคือการคว้ากลุ่มแรกของผลการแข่งขันที่ล้อมรอบด้วย()
ใน regex ?:
และวิธีการทำเช่นนี้คือการใช้ไวยากรณ์ไม่ใช่กลุ่มจับคือ ดังนั้น regex (\p{Alpha}*[a-z])(?:@example.com)
จะส่งกลับเฉพาะส่วน id ของอีเมล
สิ่งหนึ่งที่น่าสนใจที่ฉันเจอคือความจริงที่ว่าคุณสามารถมีกลุ่มจับในกลุ่มที่ไม่ได้ดักจับได้ ดู regex ด้านล่างเพื่อจับคู่ URL ของเว็บ:
var parse_url_regex = /^(?:([A-Za-z]+):)(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;
ป้อนสตริง URL:
var url = "http://www.ora.com:80/goodparts?q#fragment";
กลุ่มแรกใน regex ของฉัน(?:([A-Za-z]+):)
เป็นกลุ่มที่ไม่ได้จับภาพซึ่งตรงกับรูปแบบโปรโตคอลและ:
ตัวละครลำไส้ใหญ่เช่นhttp:
แต่เมื่อฉันทำงานด้านล่างรหัสฉันเห็นดัชนีที่ 1 ของอาเรย์ที่ส่งคืนนั้นประกอบด้วยสตริงhttp
เมื่อฉันคิดว่าhttp
และลำไส้ใหญ่:
ทั้งสองจะไม่ได้รับการรายงานเนื่องจากอยู่ในกลุ่มที่ไม่ได้รับการบันทึก
console.debug(parse_url_regex.exec(url));
ฉันคิดว่าถ้ากลุ่มแรกเป็นกลุ่ม(?:([A-Za-z]+):)
ที่ไม่ได้จับภาพแล้วทำไมมันถึงส่งคืนhttp
สตริงในอาร์เรย์ผลลัพธ์
ดังนั้นหากคุณสังเกตเห็นว่ามีกลุ่มซ้อน([A-Za-z]+)
อยู่ภายในกลุ่มที่ไม่ได้จับภาพ ว่ากลุ่มที่ซ้อนกัน([A-Za-z]+)
เป็นกลุ่มจับ (ไม่ได้มี?:
ที่จุดเริ่มต้น) (?:([A-Za-z]+):)
ในตัวเองภายในกลุ่มที่ไม่ได้จับ นั่นเป็นสาเหตุที่ข้อความhttp
ยังคงถูกดักจับ แต่:
อักขระโคลอนซึ่งอยู่ในกลุ่มที่ไม่ถูกดักจับ แต่ภายนอกกลุ่มการดักจับไม่ได้รับรายงานในอาร์เรย์เอาต์พุต
เปิด Google Chrome devTools ของคุณแล้วไปที่แท็บคอนโซล: และพิมพ์สิ่งนี้:
"Peace".match(/(\w)(\w)(\w)/)
เรียกใช้และคุณจะเห็น:
["Pea", "P", "e", "a", index: 0, input: "Peace", groups: undefined]
เอ็นJavaScript
จิ้น RegExp จับสามกลุ่มรายการที่มีดัชนี 1,2,3 ตอนนี้ใช้เครื่องหมายที่ไม่จับภาพเพื่อดูผลลัพธ์
"Peace".match(/(?:\w)(\w)(\w)/)
ผลลัพธ์คือ:
["Pea", "e", "a", index: 0, input: "Peace", groups: undefined]
นี่คือสิ่งที่ชัดเจนว่าไม่ใช่กลุ่มจับภาพ
ฉันคิดว่าฉันจะให้คำตอบคุณ อย่าใช้ตัวแปรจับภาพโดยไม่ตรวจสอบว่าการจับคู่สำเร็จ
ตัวแปรการจับภาพ$1
ฯลฯ ไม่ถูกต้องยกเว้นว่าการจับคู่สำเร็จและไม่ถูกล้างเช่นกัน
#!/usr/bin/perl
use warnings;
use strict;
$_ = "bronto saurus burger";
if (/(?:bronto)? saurus (steak|burger)/)
{
print "Fred wants a $1";
}
else
{
print "Fred dont wants a $1 $2";
}
ในตัวอย่างข้างต้นเพื่อหลีกเลี่ยงการจับภาพบรอนโนใน$1
ให้(?:)
ใช้
หากจับคู่รูปแบบจะ$1
ถูกจับเป็นรูปแบบที่จัดกลุ่มถัดไป
ดังนั้นผลลัพธ์จะเป็นดังนี้:
Fred wants a burger
มันมีประโยชน์ถ้าคุณไม่ต้องการให้บันทึกการแข่งขัน
มันง่ายมากเราสามารถเข้าใจได้ด้วยตัวอย่างวันที่ที่ง่ายสมมติว่าวันที่ดังกล่าวเป็นวันที่ 1 มกราคม 2019 หรือ 2 พฤษภาคม 2019 หรือวันอื่น ๆ และเราเพียงต้องการแปลงเป็นdd / mm / yyyyรูปแบบเราไม่ต้องการเดือน ชื่อที่เป็นเดือนมกราคมหรือกุมภาพันธ์สำหรับเรื่องนั้นดังนั้นในการจับภาพส่วนที่เป็นตัวเลข แต่ไม่ใช่ส่วนต่อท้าย (เป็นทางเลือก) คุณสามารถใช้กลุ่มที่ไม่ได้รับการบันทึกได้
ดังนั้นการแสดงออกปกติจะเป็น
([0-9]+)(?:January|February)?
มันง่ายเหมือนที่