TL; ดร
ใช้[.]
แทน\.
และ[0-9]
แทนที่จะ\d
หลีกเลี่ยงปัญหาในบางภาษา (เช่น Java)
ขอบคุณบุคคลนิรนามที่รับรู้สิ่งนี้ตั้งแต่แรก
รูปแบบที่ค่อนข้างง่ายสำหรับการจับคู่เลขทศนิยมคือ
[+-]?([0-9]*[.])?[0-9]+
สิ่งนี้จะตรงกับ:
ดูตัวอย่างการทำงาน
หากคุณต้องการจับคู่123.
(จุดที่ไม่มีส่วนทศนิยม) คุณจะต้องมีนิพจน์ที่ยาวขึ้นเล็กน้อย:
[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)
ดูคำตอบของ pkellerสำหรับคำอธิบายที่ครบถ้วนยิ่งขึ้นเกี่ยวกับรูปแบบนี้
หากคุณต้องการรวมตัวเลขที่ไม่ใช่ทศนิยมเช่นฐานสิบหกและฐานแปดดูคำตอบของฉันฉันจะระบุได้อย่างไรว่าสตริงเป็นตัวเลข .
หากคุณต้องการตรวจสอบว่าอินพุตเป็นตัวเลข (แทนที่จะค้นหาตัวเลขภายในอินพุต) คุณควรล้อมรอบรูปแบบด้วย^
และ$
ดังนี้:
^[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)$
นิพจน์ทั่วไปที่ผิดปกติ
"การแสดงออกปกติ" ในขณะที่การดำเนินการในภาษาที่ทันสมัยที่สุด, APIs, กรอบ, ห้องสมุด, ฯลฯ อยู่บนพื้นฐานของแนวคิดการพัฒนาในทฤษฎีภาษาอย่างเป็นทางการ อย่างไรก็ตามวิศวกรซอฟต์แวร์ได้เพิ่มส่วนขยายจำนวนมากที่นำการใช้งานเหล่านี้ไปใช้เกินกว่าคำจำกัดความที่เป็นทางการ ดังนั้นในขณะที่เอ็นจิ้นนิพจน์ทั่วไปส่วนใหญ่มีลักษณะคล้ายกัน แต่ก็ไม่มีมาตรฐาน ด้วยเหตุนี้จำนวนมากจึงขึ้นอยู่กับภาษา API กรอบงานหรือไลบรารีที่คุณใช้
(อนึ่งเพื่อช่วยลดความสับสนหลายคนจึงใช้ " regex " หรือ " regexp " เพื่ออธิบายภาษาที่จับคู่ขั้นสูงเหล่านี้ดูRegex เหมือนกับนิพจน์ทั่วไปหรือไม่ที่ RexEgg.com สำหรับข้อมูลเพิ่มเติม)
ที่กล่าวว่าเครื่องมือ regex มากที่สุด (ที่จริงทั้งหมดของพวกเขาเท่าที่ฉันรู้) \.
จะยอมรับ เป็นไปได้มากว่ามีปัญหาในการหลบหนี
ปัญหาในการหลบหนี
บางภาษามีในตัวสนับสนุนสำหรับ regexes, เช่น JavaScript สำหรับภาษาที่ไม่มีการหลีกเลี่ยงอาจเป็นปัญหาได้
นี่เป็นเพราะโดยพื้นฐานแล้วคุณกำลังเข้ารหัสภาษาภายในภาษา ตัวอย่างเช่น Java ใช้\
เป็นอักขระหลีกภายในสตริงดังนั้นหากคุณต้องการวางอักขระแบ็กสแลชตามตัวอักษรภายในสตริงคุณต้องหลีกเลี่ยง:
// creates a single character string: "\"
String x = "\\";
อย่างไรก็ตาม regexes ยังใช้\
อักขระในการหลีกเลี่ยงดังนั้นหากคุณต้องการจับคู่\
อักขระตามตัวอักษรคุณต้องหลีกเลี่ยงสำหรับเอนจิน regexe จากนั้นจึงหลีกเลี่ยงอีกครั้งสำหรับ Java:
// Creates a two-character string: "\\"
// When used as a regex pattern, will match a single character: "\"
String regexPattern = "\\\\";
ในกรณีของคุณคุณอาจไม่ได้หลีกหนีอักขระแบ็กสแลชในภาษาที่คุณกำลังเขียนโปรแกรม:
// will most likely result in an "Illegal escape character" error
String wrongPattern = "\.";
// will result in the string "\."
String correctPattern = "\\.";
การหลบหนีทั้งหมดนี้อาจสร้างความสับสนได้มาก หากภาษาที่คุณใช้งานรองรับสตริงดิบคุณควรใช้ภาษาเหล่านี้เพื่อลดจำนวนแบ็กสแลช แต่ไม่ใช่ทุกภาษาที่ทำ (โดยเฉพาะอย่างยิ่ง: Java) โชคดีที่มีทางเลือกอื่นที่จะได้ผลในบางครั้ง:
String correctPattern = "[.]";
สำหรับเอนจิ้น regex \.
และ[.]
หมายความว่าเหมือนกันทุกประการ โปรดทราบว่าวิธีนี้ใช้ไม่ได้ในทุกกรณีเช่นขึ้นบรรทัดใหม่ ( \\n
) วงเล็บเหลี่ยมเปิด ( \\[
) และแบ็กสแลช ( \\\\
หรือ[\\]
)
หมายเหตุเกี่ยวกับการจับคู่หมายเลข
(คำใบ้: มันยากกว่าที่คุณคิด)
การจับคู่ตัวเลขเป็นหนึ่งในสิ่งที่คุณคิดว่าค่อนข้างง่ายสำหรับ regex แต่จริงๆแล้วมันค่อนข้างยุ่งยาก ลองมาดูแนวทางของคุณทีละชิ้น:
[-+]?
จับคู่ตัวเลือก-
หรือ+
[0-9]*
จับคู่ตัวเลขตามลำดับ 0 หรือมากกว่า
\.?
จับคู่ตัวเลือก .
[0-9]*
จับคู่ตัวเลขตามลำดับ 0 หรือมากกว่า
ขั้นแรกเราสามารถล้างนิพจน์นี้ได้เล็กน้อยโดยใช้ไฟล์ ชวเลขคลาสอักขระสำหรับตัวเลข (โปรดทราบว่าสิ่งนี้มีความอ่อนไหวต่อปัญหาการหลบหนีที่กล่าวถึงข้างต้นด้วย):
[0-9]
= \d
ฉันจะใช้\d
ด้านล่าง [0-9]
แต่เก็บไว้ในใจว่ามันหมายถึงสิ่งเดียวกันเช่น (จริงๆแล้วในเครื่องยนต์บางรุ่น\d
จะจับคู่ตัวเลขจากสคริปต์ทั้งหมดดังนั้นมันจะตรงมากกว่าที่[0-9]
จะต้องการ แต่นั่นอาจไม่สำคัญในกรณีของคุณ)
ตอนนี้ถ้าคุณดูที่นี้อย่างคุณจะรู้ว่าทุกส่วนหนึ่งของรูปแบบของคุณเป็นตัวเลือก รูปแบบนี้สามารถจับคู่สตริงความยาว 0 สตริงที่ประกอบด้วย+
หรือ-
; หรือสตริงที่ประกอบด้วย.
. นี่อาจไม่ใช่สิ่งที่คุณตั้งใจไว้
ในการแก้ไขปัญหานี้การเริ่มต้นด้วยการ "ยึด" regex ของคุณด้วยสตริงขั้นต่ำที่กำหนดจะเป็นประโยชน์ซึ่งอาจเป็นตัวเลขหลักเดียว:
\d+
ตอนนี้เราต้องการเพิ่มส่วนทศนิยม แต่มันไม่ไปในที่ที่คุณคิดว่ามันอาจ:
\d+\.?\d* /* This isn't quite correct. */
123.
นี้จะยังคงตรงกับค่าเช่น ที่แย่กว่านั้นคือมีความชั่วร้ายเกี่ยวกับเรื่องนี้ ช่วงเวลาเป็นทางเลือกซึ่งหมายความว่าคุณมีคลาสซ้ำสองคลาสแบบเคียงข้างกัน ( \d+
และ\d*
) สิ่งนี้อาจเป็นอันตรายได้หากใช้ในทางที่ผิดโดยจะเปิดระบบของคุณไปสู่การโจมตี DoS
ในการแก้ไขปัญหานี้แทนที่จะถือว่าช่วงเวลาเป็นทางเลือกเราจำเป็นต้องปฏิบัติตามที่กำหนด (เพื่อแยกคลาสอักขระที่ซ้ำกัน) และทำให้ส่วนทศนิยมทั้งหมดเป็นทางเลือก:
\d+(\.\d+)? /* Better. But... */
ตอนนี้ดูดีขึ้นแล้ว เราต้องการช่วงเวลาระหว่างตัวเลขลำดับแรกและลำดับที่สอง แต่มีข้อบกพร่องร้ายแรง: เราไม่สามารถจับคู่ได้.123
เนื่องจากต้องใช้เลขนำหน้า
นี่เป็นเรื่องง่ายที่จะแก้ไข แทนที่จะทำให้ส่วน "ทศนิยม" ของตัวเลขเป็นทางเลือกเราจำเป็นต้องพิจารณาให้เป็นลำดับของอักขระ: ตัวเลข 1 ตัวขึ้นไปที่อาจนำหน้าด้วยตัวเลข.
ที่อาจนำหน้าด้วย 0 หรือมากกว่า:
(\d*\.)?\d+
ตอนนี้เราเพิ่มเครื่องหมาย:
[+-]?(\d*\.)?\d+
แน่นอนว่าเครื่องหมายทับเหล่านี้ค่อนข้างน่ารำคาญใน Java ดังนั้นเราจึงสามารถแทนที่ในคลาสอักขระแบบยาวได้:
[+-]?([0-9]*[.])?[0-9]+
การจับคู่เทียบกับการตรวจสอบความถูกต้อง
สิ่งนี้เกิดขึ้นในความคิดเห็นสองสามครั้งดังนั้นฉันจึงเพิ่มภาคผนวกเกี่ยวกับการจับคู่กับการตรวจสอบความถูกต้อง
เป้าหมายของการจับคู่คือการค้นหาเนื้อหาบางส่วนในอินพุต ("เข็มในกองหญ้า") เป้าหมายของการตรวจสอบความถูกต้องคือเพื่อให้แน่ใจว่าอินพุตอยู่ในรูปแบบที่คาดหวัง
โดยธรรมชาติ Regexes จะจับคู่ข้อความเท่านั้น เมื่อป้อนข้อมูลบางส่วนพวกเขาจะพบข้อความที่ตรงกันหรือจะไม่พบ อย่างไรก็ตามด้วยการ "snapping" นิพจน์ไปยังจุดเริ่มต้นและจุดสิ้นสุดของอินพุตที่มีแท็ก anchor ( ^
และ$
) เราสามารถมั่นใจได้ว่าจะไม่พบรายการที่ตรงกันเว้นแต่ว่าอินพุตทั้งหมดจะตรงกับนิพจน์โดยใช้ regexes เพื่อตรวจสอบความถูกต้องตรวจสอบ
regex ที่อธิบายไว้ข้างต้น ( [+-]?([0-9]*[.])?[0-9]+
) จะจับคู่ตัวเลขอย่างน้อยหนึ่งตัวภายในสตริงเป้าหมาย ดังนั้นให้ป้อนข้อมูล:
apple 1.34 pear 7.98 version 1.2.3.4
regex จะตรง1.34
, 7.98
, 1.2
, .3
และ.4
และ
ในการตรวจสอบว่าอินพุตที่ระบุเป็นตัวเลขและไม่มีอะไรนอกจากตัวเลขให้ "สแนป" นิพจน์ไปที่จุดเริ่มต้นและจุดสิ้นสุดของอินพุตโดยการรวมไว้ในแท็กจุดยึด:
^[+-]?([0-9]*[.])?[0-9]+$
สิ่งนี้จะค้นหารายการที่ตรงกันก็ต่อเมื่ออินพุตทั้งหมดเป็นตัวเลขทศนิยมและจะไม่พบข้อมูลที่ตรงกันหากอินพุตมีอักขระเพิ่มเติม ดังนั้นเมื่อป้อนข้อมูล1.2
แล้วจะพบapple 1.2 pear
รายการที่ตรงกันแต่จะไม่พบรายการที่ตรงกัน
ทราบว่าบางส่วนเครื่องยนต์ regex มีvalidate
, isMatch
หรือฟังก์ชั่นที่คล้ายกันซึ่งเป็นหลักไม่สิ่งที่ฉันได้อธิบายไว้โดยอัตโนมัติกลับtrue
ถ้าการแข่งขันถูกพบและfalse
หากไม่มีการแข่งขันพบ นอกจากนี้โปรดทราบว่าเอ็นจิ้นบางตัวอนุญาตให้คุณตั้งค่าแฟล็กซึ่งเปลี่ยนนิยามของ^
และ$
จับคู่จุดเริ่มต้น / จุดสิ้นสุดของบรรทัดแทนที่จะเป็นจุดเริ่มต้น / จุดสิ้นสุดของอินพุตทั้งหมด โดยทั่วไปนี่ไม่ใช่ค่าเริ่มต้น แต่โปรดระวังแฟล็กเหล่านี้