Regular Expression (Regex)

Regular Expression (Regex)

Regular Expression หรือเรียกย่อๆว่า Regex หมายถึง รูปแบบของลำดับ หรือกลุ่มของสัญลักษณ์ ที่ใช้แทนลำดับ หรือกลุ่มของอักขระตามที่ต้องการ

เราใช้สัญลักษณ์ [ ] (square brackets) เพื่อกำหนดขอบเขตของกลุ่มตัวอักขระหลายตัวที่ใช้เป็นตัวเลือก เช่น สมมุติว่า เราต้องการจะเขียนรูปแบบที่ใช้แทนตัวอักขระหนึ่งตัว อะไรก็ได้จาก {a,e,i,o,u} เราก็จะเขียนว่า [aeiou] โดยจะเรียงลำดับก่อนหลังอย่างไรก็ได้ เช่น [eioua] ให้ผลเหมือนกับ [aeoui] หรือ ถ้าเราต้องการเขียน รูปแบบเพื่อใช้แทนตัวขระหนึ่งตัวที่เป็นตัวเลขตัวใดตัวหนึ่งจาก 0 ถึง 9 เราก็เขียนว่า [0123456789] หรือจะเขียนแบบสั้นๆใหม่ได้เป็น [0-9] หรืออีกตัวอย่างหนึ่ง ถ้าเราต้องการจะเขียนนิพจน์แบบregex ขึ้นมา เพื่อใช้แทนอักขระตัวใดตัวหนึ่งที่เป็นได้ทั้งตัวพิมพ์ใหญ่หรือเล็กในภาษาอังกฤษหรือตัวเลขระหว่าง 0 ถึง 9 เราก็เขียนว่า [A-Za-z0-9]

[aeiou] ตัวอักขระตัวหนึ่งจาก {a,e,i,o,u} ตัวไหนก็ได้
[0-9] ตัวอักขระตัวหนึ่งจาก {0,1,…,9} ตัวไหนก็ได้
[A-Za-z0-9] ตัวอักขระตัวหนึ่งจาก {A,B,..,Z, a, b, … , z, 0, 1, … 9} ตัวไหนก็ได้

ถ้าเรามีข้อความแล้วเราต้องการจะค้นหาอักขระหรือลำดับของอักขระ (หรือ pattern) ในข้อความเรานั้น เราเรียกขั้นตอนในการค้นหาตามรูปแบบนี้ว่า pattern matching ในภาษา PHP จะมีฟังก์ชันที่เราใช้ในการค้นหาลำดับของตัวอักขระตามแบบที่ต้องการคือereg() และ eregi()
และต่างกันตรงที่ว่า ฟังก์ชัน eregi() จะเปรียบเทียบโดยไม่คำนึงถึงเรื่องตัวพิมพ์เล็กหรือใหญ่

ตัวอย่างเช่น สมมุติว่า เรามีข้อความอยู่ในอาร์เรย์เป็นข้อความที่มีแค่ตัวอักขระตัวเดียว แล้วเราต้องการจะหาว่า ตัวไหนบ้างที่เป็นตัวเลข 0 ถึง 9 บ้างและตัวไหนบ้างที่เป็นตัวพิมพ์ภาษาอังกฤษ a, b, หรือ c เราก็เขียนสคริปต์โดยใช้ฟังก์ชัน ereg() ได้ดังนี้

<?
$a=array(“0″,”1″,”2″,”3″,”5″,”7″,”a”,”b”,”c”);

for ($i=0; $i < count($a); $i++) { // print only digit
if ( ereg(“[0-9]”,$a[$i]) ) {
print (“$a[$i] <BR>\n”);
}
}
?>
<HR>
<?
for ($i=0; $i < count($a); $i++) { // print only a, b or c
if ( ereg(“[a-c]”,$a[$i]) ) {
print (“$a[$i] <BR>\n”);
}
}
?>

ข้อความที่เราใช้ในตัวอย่างข้างบนมีแค่ตัวอักขระเพียงหนึ่งตัว แต่อันที่จริงแล้วฟังก์ชัน ereg() จะให้ค่าเป็นจริง ถ้าพบว่า ข้อความที่มีความยาวมากกว่าหนึ่งตัวอักขระและมีตัวอักขระอย่างน้อยหนึ่งตัวที่ตรงตามรูปแบบที่กำหนด ฟังก์ชันก็จะให้ค่าเป็นจริงด้วย ตัวอย่างเช่น

<?
$a=array(“f”,”mn”, “eU”,”+5″,”Y”,”17″,”a4″,”%m”,”cdef”);
for($i=0; $i < count($a); $i++) {
if ( ereg(“[a-z]”,$a[$i]) ) {
print (“$a[$i] <BR>\n”);
}
}
?>

ผลจากการทำงานของสคริปตในตัวอย่างนี้จะได้ว่า ข้อความที่ตรงตามรูปแบบ [a-z] โดยใช้ฟังก์ชัน ereg() ในการเปรียบเทียบ คือ “f”, “mn”, “eU”, “a4”, “%m” และ “cdef” เพราะว่าข้อความเหล่านี้ มีตัวอักขระอย่างน้อยหนึ่งตัวที่อยู่ระหว่าง a ถึง z ดังนั้นจึงจะเห็นได้ว่า ข้อความที่เหลือคือ “+5”, “Y” และ “17” ไม่ตรงตามรูปแบบ

จากตัวอย่างข้างบน สมมุติว่า เราต้องการจะหาข้อความที่มีสองตัวอักขระและมีแค่สองตัวเท่านั้น เป็นตัวใดก็ได้ระหว่าง a ถึง z เราก็จะต้องกำหนดรูปแบบให้การค้นหาให้เป็น ^[a-z]{2}$ โดยที่สัญลักษณ์ ^ หมายถึงการเริ่มต้น และ $ หมายถึงการลงท้าย และเราจะอ่านว่า ให้ค้นหาข้อความที่เริ่มต้นด้วยตัวภาษาอังกฤษพิมพ์เล็ก และจบด้วยตัวภาษาอังกฤษพิมพ์เล็ก และมีอยู่สองตัวเท่านั้น จากตัวอย่างข้างบน ถ้าเราใช้รูปแบบใหม่นี้ในการค้นหา เราจะได้ “en” เป็นข้อความเดียวเท่านั้นที่ตรงตามรูปแบบ

สมมุติว่า เราต้องการค้นหาคำที่ขึ้นต้นด้วยตัวพิมพ์ใหญ่ภาษาอังกฤษและตามด้วยสระ {a,e,i,o,u} ถ้าข้อความมีมากกว่าสองตัวอักขระแล้วตัวที่ตามมาจะเป็นอะไรก็ได้ เราก็เขียนรูปแบบในการค้นหาได้ เป็น ^[A-Z][aeiou] ลองตัวดูอย่างต่อไปนี้

<?
$a=array(“f”,”Monday”, “eU”,”755″,”Pu”,”English”,”Tuesday”,”for”, “Bee”);

for($i=0; $i < count($a); $i++) {
if ( ereg(“^[A-Z][aeiou]”,$a[$i]) ) {
print (“$a[$i] <BR>\n”);
}
}
?>

ตัวอย่างนี้จะได้ว่า “Monday”, “Pu” ,”Tuesday” และ “Bee” คือข้อความที่ตรงตามรูปแบบที่กำหนด

ถ้าต้องการจะหาข้อความที่ขึ้นต้นด้วยตัวพิมพ์ใหญ่แล้วตามด้วยสระเป็นตัวพิมพ์เล็กอย่างน้อยหนึ่งตัว เราก็เขียนว่า ^[A-Z][aeiou]+$ ซึ่งเครื่องหมายบวกที่อยู่ข้างหลัง [aeiou] หมายความว่า มีได้อย่างน้อยที่สุดหนึ่งตัว จากตัวอย่างข้างบน ถ้าเราใช้รูปแบบใหม่นี้ในการค้นหา ก็จะได้ข้อความ “Pu” และ “Bee” เท่านั้นที่ตรงตามรูปแบบ

ถ้าจะหาข้อความที่ขึ้นต้นด้วยตัวพิมพ์ใหญ่แต่ตามด้วยตัวพิมพ์เล็กใดๆก็ได้ที่ไม่ใช่สระ {a,e,i,o,u} เราก็เขียนใหม่ได้เป็น ^[A-Z][^aeiou] โปรดสังเกตว่า เราใส่สัญลักษณ์ ^ ไว้ระหว่าง [ ] และอยู่ข้างหน้าสุด เพื่อใช้กำหนดกรณีตรงข้าม และจากตัวอย่างข้างบน เราจะได้ข้อความ “English” เท่านั้นที่ตรงตามรูปแบบใหม่นี้

อีกตัวอย่างหนึ่งที่น่าสนใจ คือ สมมุติว่า เรามีรายชื่อไฟล์อยู่หลายๆชื่อ ต้องการจะตรวจดูว่า ชื่อไหนลงท้ายด้วย .gif .jpeg .jpg หรือ .png บ้าง โดยใช้ eregi() ในการค้นหา เราจะต้องกำหนดรูปแบบของ regex ขึ้นมาก่อน ซึ่งทำได้ดังนี้(\.gif|\.jpg|\.jpeg|\.png)$ ในกรณีนี้เราไม่ได้ใช้ [ ] แต่ใช้วงเล็บคู่ ( ) แทน และสัญลักษณ์ \. เป็น escape sequence ที่เราใช้แทนจุด (.) ถ้าเขียนอยู่ในระหว่างวงเล็บคู่ เนื่องจากว่าถ้าใช้จุดโดยไม่มี backslash (\) นำหน้าก็จะหมายถึง ตัวอักขระใดๆก็ได้ ถ้าเราใช้จุดระหว่าง [ ] ก็จะหมายถึงจุด โดยไม่ต้องใส่ \ ไว้เพื่อกำหนดให้เป็น escape sequence

<?
$files=array(“bird.gif”,”Linux.JPG”, “unknown.xbm”,”icon.Png”);

for($i=0; $i < count($files); $i++) {
if ( eregi(“(\.gif|\.jpg|\.jpeg|\.png)$”,$files[$i]) ) {
print (“$files[$i] <BR>\n”);
}
}
?>

ความแตกต่างของการใช้ ( ) และ [ ] อยู่ที่ว่า ถ้าเขียน (abc) หมายถึงข้อความที่เริ่มด้วย a ตามด้วย b และ c ตามลำดับ ในขณะที่ [abc] หมายถึง a หรือ b หรือ c ซึ่ง [abc] จะให้ผลเหมือนกับ (a|b|c) และเครื่องหมาย | ที่ใช้ภายในวงเล็บคู่ ( ) จะหมายถึง “หรือ”ดังนั้นจากตัวอย่างข้างบน จากรูปแบบเดิม (\.gif|\.jpg|\.jpeg|\.png)$ เราอาจจะเขียนใหม่ได้เป็น [(\.gif|\.png)(\.jpg|\.jpeg)]$ ก็จะให้ผลเหมือนกัน

จากที่ได้อธิบายมาก็เป็นเพียงการใช้งานส่วนหนึ่งของ Regular Expression ซึ่งยังมีลักษณะและการใช้งานที่นอกเหนือจากที่กล่าวไป

สัญลักษณ์พื้นฐานที่ใช้ในการเขียน Regular Expression

. ตัวอักขระใดๆก็ได้
[ … ] ตัวอักขระตัวใดๆก็ได้ที่อยู่ในกลุ่มของตัวอักขระที่เป็นตัวเลือก
[^… ] ตัวอักขระตัวใดๆก็ได้ที่ไม่ได้อยู่ในกลุ่มของตัวอักขระที่เป็นตัวเลือก
^ ตำแหน่งเริ่มต้นของข้อความ
$ ตำแหน่งท้ายสุดของข้อความ
\< ตำแหน่งเริ่มต้นของคำ
\> ตำแหน่งท้ายสุดของคำ
| สัญลักษณ์ที่ใช้แยกตัวเลือกระหว่าง ( … )
(…) ใช้แทนกลุ่มของตัวเลือก

Repetition Quantifiers

? ถ้ามีก็ไม่เกินหนึ่งตัวหรือไม่มีก็ได้
* มีหนึ่งตัวหรือมากกว่าหนึ่งหรือไม่มีเลยก็ได้
+ มีหนึ่งตัวหรือมากกว่าหนึ่งก็ได้ แต่ต้องมีอย่างน้อยหนึ่งตัว
{min,max} ใช้กำหนดจำนวนน้อยที่สุดและมากที่สุดที่จะมีได้

ตัวอย่างของการใช้ regular expressions

^be ข้อความที่ขึ้นต้นด้วย b และถัดจาก b ตามด้วย e
^[be] ข้อความที่ขึ้นต้นด้วย b หรือ e
[^be] ข้อความที่ไม่ได้ขึ้นต้นด้วย b และ e
ion$ ข้อความที่ลงท้ายด้วยหรือจบท้ายด้วย ion
[ion]$ ข้อความที่ลงท้ายหรือจบท้ายด้วย i หรือ o หรือ n
\n$ ข้อความที่มีสัญลักษณ์ \n (new line) อยู่ท้าย
<H[1-6]> ข้อความที่มี <H1> <H2> <H3> <H4> <H5> หรือ <H6> อยู่ด้วย
<H[^4-6]> ข้อความที่ไม่มี <H4> <H5> และ <H6> อยู่ด้วย
[0-9.] ข้อความที่มีตัวเลขใดๆระหว่าง 0 ถึง 9 หรือ จุด อยู่ด้วย
^Subject$ ข้อความที่มีคำว่า Subject เท่านั้น
^(From|Subject|Date): ข้อความที่มีคำว่า From: หรือ Subject: หรือ Date: ขึ้นต้น
^[0-9]+$ ข้อความที่มีตัวเลขเท่านั้นและอย่างน้อยหนึ่งตัว
([0-9]+(\.[0-9]+){3}) ข้อความที่ตรงตามรูปแบบของหมายเลข IP เช่น 127.0.0.1
^[-+]?[0-9]+(\.[0-9]*)?$ ข้อความที่ตรงตามรูปแบบของตัวเลขทศนิยม เช่น -12.345

การใช้งาน regular expression อย่างจริงจังนั้นไม่ใช่เป็นเรื่องง่าย ต้องทำความเข้าใจอย่างจริงจัง และในเอกสารนี้ก็ทำได้แค่นำเสนอเนื้อหาพื้นฐานสำหรับการใช้งานเท่านั้น ถ้าใครสนใจการใช้งานอย่างจริงจัง ในเรื่องของ regular expression ก็สามารถหาหนังสือ สำหรับเรื่องนี้โดยเฉพาะมาอ่านได้