Sunday, November 22, 2009

Regular Expression มหัศจรรย์แห่งการจัดการ Text (ตอนที่ 2 จบ)

ข้อเขียนนี้ช่วยฉัน: 
ข้อเขียนที่แล้วได้แนะนำการใช้งานและประโยชน์ของ Regular Expression สำหรับตอนนี้เราจะมาพูดถึงไวยกรณ์ของ Regular Expression บน Oracle กันนะครับ


source_string คือ Text ที่เราต้องการจัดการ ส่วนมากจะเป็นชื่อคอลัมน์ในตาราง และเป็นคอลัมน์ที่มีประเภทข้อมูลเป็น Char, Varchar2, Nchar, Nvarchar2, Clob, NClob

pattern คือ รูปแบบทีเป็นไวยกรณ์ของ Regular Expression ที่เอาไว้จัดการ Text ใน source_string

match_parameter คือตัวที่กำหนดพฤติกรรมในการจับคู่ตาม pattern ที่กำหนด ตัวอย่างเช่น
'i' หมายถึงการจับคู่ตาม pattern ไม่สนใจเรื่องตัวอักษรใหญ่หรือเล็ก (Case Insensitive)
'c' หมายถึงการจับคู่ตาม pattern ที่จะต้องตรงกันตามอักษรตัวเล็ก-ใหญ่ (Case Sensitive)
'n' ทำให้สัญญลักษณ์จุด (Period)ใน Regular Expression ซึ่งโดยปกติหมายถึงตัวอักษรใด ๆ ก็ได้หนึ่งตัว กลายเป็นหมายถึงตัวขึ้นบรรทัดใหม่ (New Line Character)
'm' ทำให้เครื่องหมาย ^ และ $ เป็นจุดเริ่มต้นและจุดสิ้นสุดบรรทัดใน source_string ซึ่งหมายถึงทำให้มอง source_string เป็นหลาย ๆ บรรทัดได้ ถ้าไม่ได้กำหนดตัวนี้ source_string จะถูกมองเห็นเป็นบรรทัดเดียว และ ^ กับ $ จะหมายถึงเป็นจุดเริ่มต้นกับสิ้นสุดของ Text ทั้งหมดใน source_string

ถ้าเราระบุ match_parameter ที่ขัดแย้งกันเช่น 'ic' จะใช้ค่าตัวหลังสุดคือ 'c' ซึ่งหมายถึง Case Sensitive ตัวอักษรจะต้องเป็นตัวเล็ก-ใหญ่ตรงกัน หรือถ้าเราไม่ระบุ match_parameter เลย ค่าดีฟอลต์จะมีผลดังนี้
- การจับคู่จะเป็น Case Sensitive หรือไม่ขึ้นอยู่กับตัวแปร NLS_SORT
- จุด (Period) จะหมายถึงตัวใด ๆ หนึ่งตัว ไม่ได้แทนตัวขึ้นบรรทัดใหม่
- source_string จะถูกมองเป็นบรรทัดเดียว และเครื่องหมาย ^ และ $ จะหมายถึงจุดเริ่มต้นและสิ้นสุด Text ทั้งหมดใน source_string

ตัวอย่างที่ 1
คิวรีข้างล่างนี้จะแสดงชื่อและนามสกุลของพนักงาน ซึ่งเราต้องการคนที่มีชื่อประมาณว่า "สตีเฟ่น" ซึ่งภาษาอังกฤษอาจจะเขียนเป็น Steven หรือ Stephen ก็ได้(มีชื่อขึ้นต้นด้วย Ste และลงท้ายด้วย en และตรงกลางเป็น v หรือไม่ก็ ph)

SQL select first_name, last_name
from employees
where REGEXP_LIKE (first_name, '^Ste(v|ph)en$');

FIRST_NAME LAST_NAME
---------------------- ------------------------------------------
Steven King
Steven Markle
Stephen Stiles

เครื่องหมาย ^ คื่อให้เริ่มตรวจสอบ(ด้วย pattern)ตั้งแต่อักขระตัวแรกของ source_string โดยที่อักษรตัวแรกจะต้องเป็นตัว S, ตัวที่สองสามเป็น te ตัวถัดมาอาจจะเป็น v หรือ ph ก็ได้ และจบท้ายด้วย en เครื่องหมาย $ แสดงจุดสิ้นสุดของ Text ที่เราต้องการตรวจสอบ เมื่อใช้ en$ หมายถึงว่า Text ที่จะเข้าเงื่อนไขจะต้องลงท้ายด้วย en แต่ถ้าเราใส่แค่ en เฉย ๆ ก็หมาย ความว่าตัวอักษรตัวที่ห้า-หก หรือตัวที่หก-เจ็ด จะเป็น en (ขึ้นอยู่กับว่าจะเป็น Stev หรือ Steph)ส่วนหลังจากนั้นจะเป็นอะไรก็ได้

ตัวอย่างที่ 2
คิวรีข้างล่างนี้แสดงนามสกุลของพนักงาน ที่มีสระเหมือนกันติดกันสองตัว (คอลัมน์ last_name มีตัวอักษรที่เป็นสระ (a, e, i, o หรือ u) ซ้ำกันสองตัวที่อยู่ติดกันด้วย โดยไม่สนใจว่าตัวเล็ก หรือตัวใหญ่

SQL> select last_name
from employees
where REGEXP_LIKE (last_name,'([aeiou])\1','i');

LAST_NAME
-------------------------
Bloom
De Haan
Feeney
Gee
Greenberg
Greene
Khoo
Lee

ตัว Bracket "[]" ครอบตัวอักษรใด ๆ ที่เป็นไปได้ ในกรณีนี้คือตัวใด ๆ ระหว่าง a,e,i,o หรือ u ส่วนเครื่องหมาย Backslash "\" หมายถึงซ้ำตัวที่อยู่ในวงเล็บ "()" ข้างหน้า และตัว i ข้างหลังสุดคือการจับคู่ไม่สนใจว่าจะเป็นตัวใหญ่หรือเล็ก เราจะพบว่า LAST_NAME ที่ปรากฎทุก row จะมีสระซ้ำกันสองตัว

สรุปสัญญลักษณ์ต่าง ๆ ที่ใช้ใน Regular Expression (ส่วนที่เป็น Pattern)
\ (Backslash) เป็นเครื่องหมาย Backslash ตรงตัวหรือทำให้ตัวอักษรซึ่งเป็น Operator (เช่นเครื่องหมายคูณ, หาร, บวก และลบ) กลายเป็นตัวอักษรธรรมดาตัวหนึ่ง เช่น \* จะหมายถึง ตัวดอกจันหนึ่งตัว ไม่ใช่เครื่องหมายคูณเป็นต้น

* แทนการเกิดขึ้น 0 ครั้งขึ้นไป

+ แทนการเกิดขึ้น1 ครั้งเป็นต้นไป

? แทนการเกิดขึ้น 0 หรือ 1 ครั้ง

| ใช้เป็นตัวคั่นเพื่อแสดงตัวเลือก

^ แสดงว่าเป็นจุดเริ่มของข้อความ ถ้าใช้ร่วมกับ match_parameter 'm' จะใช้เป็นจุดเริ่มต้นของบรรทัดใด ๆ ใน source_string

$ แสดงว่าเป็นจุดสิ้นสุดของข้อความ ถ้าใช้ร่วมกับ match_parameter 'm' จะใช้เป็นจุดสิ้นสุดของบรรทัดใด ๆ ใน source_string

. แทนตัวอักษรใด ๆ หนึ่งตัวยกเว้น NULL และแทนตัวขึ้นบรรทัดใหม่ (New Line) ถ้า match_parameter เป็น 'n'

[ ] ใน Bracket นี้จะเป็นลิสต์ของตัวอักษร ซึ่งตัวใดตัวหนึ่งในลิสต์สามารถจับคู่ได้กับใน source_string ถ้าต้องการให้เป็นตรงกันข้ามคือต้องการ source_string ที่ไม่มีในลิสต์จะต้องใส่ ^ เข้าไปเช่นจากคิวรีข้างบนถ้าเปลี่ยน [aeiou] เป็น [^aeiou] จะได้ข้อมูล last_name ที่เป็นตรงกันข้ามคือแสดงทุกเรคคอร์ดใน employees ยกเว้น 8 เรคคอร์ดนี้

( ) เครื่องหมายกลุ่ม, สัญญลักษณ์ทั้งหลายในนี้จะถือเป็นหนึ่งตัว

{m} แทนการเกิดขึ้น m ครั้งเท่านั้น

{m,} แทนการเกิดขึ้น m ครั้งขึ้นไป

{m,n} แทนการเกิดขึ้น m ถึง n ครั้ง

\n แทนการเกิดขึ้นซ้ำครั้งที่ n ของตัว ( ) ที่อยู่ข้างหน้าตัวมัน เช่น ([aeiou])\1 หมายถึงการเกิดขึ้นซ้ำของตัว a, e,
i, o หรือ u ครั้งที่หนึ่ง เช่น aa, ee, ii, oo หรือ uu ตัว n สามารถเป็นได้จาก 1 ถึง 9

[. .] แสดงตัวอักษรหนึ่งตัว ซึ่งอาจจะประกอบด้วยอักขระมากกว่าหนึ่งตัวก็ได้ (เช่น [.ch.] ในภาษาสเปน) ใช้ในกรณีที่เราต้องการแทนตัวอักษรตัวใดๆ ในลำดับ เช่นในภาษาอังกฤษ จาก a ไปจนถึง c เราจะใช้ [a-c] แต่ในบางภาษาตัวอักษรแต่ละตัวอาจจะไม่ได้ประกอบด้วยอักขระเพียงตัวเดียวดังเช่น ch ในภาษาสเปน เราก็เลยต้องใช้ [..] ช่วยเช่นจาก a ถึง ch เราก็สามารถใช้ [a-[.ch.]] แทน

[: :] แสดงคลาสของตัวอักษร (เช่น [:alpha:]) โดยจับคู่กับตัวอักษรใด ๆ ในคลาสของตัวอักษรที่ระบุ
[:alnum:] ตัวอักษรและตัวเลขทุกตัว (alphanumeric)
[:alpha:] ตัวอักษรทุกตัว
[:digit:] ตัวเลขทุกตัว
[:blank:] ตัวช่องว่าง (Space) ทุกตัว
[:space:] ตัวช่องว่างที่ไม่สามารถพิมพ์ได้ (Nonprinting)
[:cntrl:] แสดงตัวอักษร Control ทุกตัว (ตัวอักษรที่พิมพ์ไม่ได้ - Nonprinting)
[:punct:] ตัวอักขระพิเศษทุกตัว
[:lower:] ตัวอักษรที่เป็นตัวเล็กทุกตัว
[:upper:] ตัวอักษรที่เป็นตัวใหญ่ทุกตัว
[:graph:] ตัวอักษรที่อยู่ในคลาส [:punct:], [:upper:], [:lower:] และ [:digit:] ทุกตัว
[:print:] ตัวอักษรทุกตัวที่พิมพ์ได้
[:xdigit:] ตัวอักษรที่เป็นเลขฐานสิบหกทุกตัว
คลาสเหล่านี้จะต้องใช้ภายใน Bracket เท่านั้นเช่น [[:alnum:]] หรือ [[:alpha:][:digit:]] เป็นต้น

[==] จับคู่กับตัวอักษรที่มีตัวอักษรฐาน (Base Character) ตรงกัน เช่น [=a=] จะจับคู่กับตัวอักษรที่มีตัวอักษรฐานเป็น a (เช่น Á และ Ä เป็นต้น) จะต้องใช้ภายใน Bracket เท่านั้น

ลองเล่นดูนะครับ เล่นไป ๆ จะพบว่ามันไม่ยากอย่างที่คิดเลย ขอให้สนุกกับ Regular Expression นะครับ!

บทความที่เกี่ยวเนื่องกัน
Regular Expression มหัศจรรย์แห่งการจัดการ Text (ตอนที่ 1)

3 comments:

JoeIMIsu said...

เป็นกำลังใจให้ครับ สำหรับความรู้ดีดี ครับ

kookie said...

ขอบคุณค่ะ
อาจารย์ให้การบ้านเรื่องนี้พอดีเลยค่ะ ^___^

Raspit said...

ผมชอบบทความของพี่ ทุกบทความเลย ขอบคุณครับ

Post a Comment