[python] Regular Expressions ตอนที่ 2

minatorak
2 min readJan 7, 2018

--

7/1/2018

บทความชุดนี่ มีทั้งหมด 3 ตอนนะครับ

[python] ลองเล่น Regular Expression ตอนที่ 1

[Python] Regular Expressions ตอนที่ 3 by Examples

รอบนี้จะมาดูในส่วนของตัวpatternกัน ว่าเราปรับเพิ่มเติมอะไรได้

Grouping Patterns

Number Group

import re

text = 'kalyan roll number is cs1004'

pattern = '(cs)(\d\d\d\d)'
regex = re.compile(pattern)

result = regex.search(text)
print(result.group()) #cs1004 default -> .group(0)
print(result.group(0)) #cs1004
print(result.group(1)) #cs
print(result.group(2)) #1004
print(result.groups())
#('cs', '1004') output is <class 'tuple'>

จากจากตัวอย่างข้างบนจะเห็นว่ามีpattern = ‘(cs)(\d\d\d\d)’ ซึ่งตรงนี้มันเป็นการแบ่งกลุ่มในการค้นหาอีกที (cs)เป็นกลุ่มที่1 (\d\d\d\d)เป็นกลุ่มที่2 แต่ในการค้นหาต้องการข้อความที่ทั้ง2กลุ่มนี้อยู่ติดกัน จากตัวอย่างนี้ลองเอาไปใช้กับโจทย์ที่ต้องการหาตัวเลขที่มีตัวอักษร “cs”ประกบอยู่ด้านหน้า แต่หากเราไม่ต้องการตัวอักษร “cs” ก็สามารถเลือกเอาเฉพาะgroup 2 ที่เป็นตัวเลขได้

Name Group

import re

text = 'kalyan roll number is cs1004'

pattern = r'(?P<branch>cs)(?P<roll>\d\d\d\d)'
regex = re.compile(pattern)

result = regex.search(text)
print(result.group()) #cs1004
print(result.group(0)) #cs1004
print(result.group(1)) #cs
print(result.group("branch")) #cs
print(result.group(2)) #1004
print(result.group("roll")) #1004
print(type(result.groups())) #('cs', '1004') output is <class 'tuple'>

concept เดียวกับอันก่อนหน้า แต่เราสามารถตั้งชื่อให้กับgroupได ้ดูแล้วสื่อความหมายได้มากกว่า โดยเพิ่ม “?P<ชื่อของกลุ่ม>” เข้าไปในวงเล็บของกลุ่มนั้น
จากตัวอย่าง สามารถเพิ่มความหยืดหยุ่นได้เป็น

branch = 'branch'
roll = 'roll'
pattern = r'(?P<'+branch+'>cs)(?P<'+roll+'>\d\d\d\d)'
regex = re.compile(pattern)
print(result.group(branch)) #cs
print(result.group(roll)) #1004

Non-Capturing Group

import re

text = 'My personal number is 032-456789 and my office number is 090-142536'
pattern1 = '\d\d\d-\d\d\d\d\d\d'
regex = re.compile(pattern1)
number = regex.findall(text)
print(number) #['032-456789', '090-142536']

regex = re.compile(pattern='(\d\d\d)-(\d\d\d\d\d\d)')
number = regex.findall(text)
print(number) #[('032', '456789'), ('090', '142536')]

regex = re.compile(pattern='(?:\d\d\d)-(?:\d\d\d\d\d\d)')
number = regex.findall(text)
print(number) #['032-456789', '090-142536']

มาดูตัว pattern=’(?:\d\d\d)-(?:\d\d\d\d\d\d)’ จะเห็นว่ามีเครื่องหมาย “ ?: ” อยู่ในgroupด้วย จะเห็นความแตกต่างคือ ตัวที่เป็นgroupธรรมดาจะreturn tupleกลับมาแต่ตัวนี้ return list หรือก็คือมันนำเอาผลลัพท์ของgroup(1)+group(2)แล้วจึง return กลับมา และผลลัพท์นี้เอาไปทำการแก้ไขและนำไปใช้งานได้สะดวกกว่าแบบที่ 2

Multi-character Patterns

Meta character

| (pipe) ใช้กำกับความหมาย“หรือ”

? (question mark) ศูนย์หรือหนึ่งตัว คือจะมีก็ได้ไม่มีก็ได้ แต่ถ้ามีจะมีแค่ตัวเดียว

* (Asterisk)ศูนย์หรือมากว่า

+ ( Plus)หนึ่งหรือมากว่า

. (dot) ตัวอักษรอะไรก็ได้ ยกเว้น\n

มาดูตัวอย่างกันดีกว่า

ใช้กับตัวอย่างด้านล่างทั้งหมด ถ้าหากไม่ได้กำหนดค่าตัวแปรtext

pipe

ถ้าข้อมูลเป็นแบบด้านบน แล้วเราต้องการตัวเลข แต่ว่าตัวเลขนั้นมีทั้ง2หลักแล3หลัก.

pattern = r'\b(\d{2}|\d{3})\b'
regex = re.compile(pattern)
numbers = regex.findall(text)
print(numbers) #['44', '550', '404', '70']

\b หมายถึงการเว้นวรรค \d หมายถึงตัวเลข \d{2} มีค่าเท่ากับ \d\d

pattern = r'\b(\d{2}|\d{3})\b'

pattern = r'\b(\d{2,3})\b'

จากตัวอย่างถ้าตีเป็นคำพูดความหมายคือ เลือกตัวเลข 2 ตัว หรือ 3ตัวที่เขียนติดกัน โดยมีการเว้นวรรคด้านหน้าและเว้นวรรคด้านหลัง

แบบประยุกต์ใช้ร่วมกับ Non-Capturing Group

text = " a cat and rat.bat"
regex = re.compile(pattern=r'(?:c|r|b)at')
numbers = regex.findall(text)
print(numbers) #['cat', 'rat', 'bat']

question mark

pattern = r'\d{2}d?'
regex = re.compile(pattern)
numbers = regex.findall(text)
print(numbers) #['44', '550', '404', '70']

จะเห็นว่าให้ผลลัพท์เหมือนตัวอย่างก่อนหน้าเลย'\d{2}d?' คือเจ้าตัว ‘?’ มันมีความหมายประมาณว่า เอาตัวอักษรที่กำหนด1 ตัว แต่จะมีหรือไม่มีตัวอักษรนั้นก็ได้
จากตัวอย่าง จะกำหนด เอาตัวเลข2ตัวจาก \d{2} และกำหนดตัวเลขอีก1ตัว ซึ่งจะมีตัวหรือไม่มีก็ได้ เลยให้ผลลัพท์เท่ากันกับการกำหนด\d{2}|\d{3}

Asterisk

text1 = 'word are abbbc abbbbbc abc ac ca'
regex = re.compile(pattern=r'ab*c')
print(regex.findall(text1)) #['abbbc', 'abbbbbc', 'abc', 'ac']

คล้ายกับquestion mark แต่เป็น0 หรือมากกว่า จากตัวอย่างคือจะมี b กี่ตัวก็ได้ หรือจะไม่มีเลยก็ได้

Plus

text1 = 'word are abbbc abbbbbc abc ac ca'
regex = re.compile(pattern=r'ab+c')
print(regex.findall(text1)) #['abbbc', 'abbbbbc', 'abc']

จะเหมือนAsterisk ต่างกันตรงที่ต้องมีอย่างน้อย1ตัว แต่จะมีมากเท่าไหร่ก็ได้

dot

text1 = 'word are \n abc'
regex = re.compile(pattern=r'a..')
print(regex.findall(text1)) #['are', 'abc']

ใช้กำหนดแทนตัวอักษรตัวไหนก็ได้1ตัว ยกเว้น ขึ้นบรรทัดใหม่ ‘\n’

ถ้าหาก

regex = re.compile(pattern=r'.')
['w', 'o', 'r', 'd', ' ', 'a', 'r', 'e', ' ', 'a', 'b', 'c']

ก็สำหรับเนื้อหาในส่วนของGrouping Patterns และcharacter Patterns ก็หมดเท่านี้ละครับ ถ้าติดปัญหาหรือมีข้อสงสัย ถามมาได้เลยครับ ขอบคุณครับ

--

--