အခန်း ၁၁ ။ Regular Expression

Programming မှာ အခြေခံအား ဖြင့် regular expression ကို မဖြစ်မနေ သိထားသင့်ပါတယ်။ Regular Expression ကို Regex လို့ အတိုခေါက် လည်း ခေါ်ကြပါတယ်။ Regular expression ဟာ validation စစ်သည့် နေရာတွေမှာ အသုံးပြုနိုင်သလို search , replace နေရာတွေမှာလည်း အသုံးပြုနိုင်ပါတယ်။ Regular expression ဟာ search pattern ကို အသုံးပြုပြီး စစ်ဆေးခြင်း နှင့် ရှာဖွေ ခြင်း တို့ကို လုပ်ဆောင်နိုင်ပါတယ်။

Regular Expression အယူအဆကို ၁၉၅၀ ဝန်းကျင်မှာ အမေရိကန် သင်္ချာပညာရှင် Stephen Cole Kleene က regular language ဆိုပြီး စတင်ခဲ့ပါတယ်။ unix ရဲ့ text-processing utilities မှာ အများအားဖြင့် အသုံးပြုခဲ့ကြပါတယ်။ 1980 ဝန်းကျင်မှာတော့ မတူညီသည့် syntax တွေ နဲ့ regular expressions ရှိခဲ့ပြီး POSIX standard နှင့် လူသုံးများသည့် Perl syntax တွေ ရှိလာခဲ့ပါတယ်။ 1997 မှာတော့ Philip Hazel က PCRE (Perl Compatible Regular Expressions) ကို developd လုပ်ခဲ့ပါတယ်။ PCRE ကို အခုခေတ် နေရာ အတော်များများ မှာ အသုံးပြုကြပါတယ်။

Patterns

Regular Expression ကို ရေးသားဖို့ အတွက် အရင်ဆုံး Patterns သဘောတရားကို သိဖို့ လိုပါတယ်။ Pattern ဆိုဟာ အထပ်ထပ်အခါအခါ ဖြစ်ပေါ်နေသည့် အကြောင်းအရာ တွေကို အကျဉ်းချုပ် ပုံစံ ဖန်တီးတယ်လို့ ဆိုရပါမယ်။

ဥပမာ။ အင်္ဂလိပ်စာမှာ နာမည်ဆိုရင် A ကနေ Z စာလုံးကြီးနဲ့ စတယ်။ ကြားမှာကတော့ a ကနေ z အထိ စာလုံးများစွာ ဖြစ်နိုင်တယ်။ space သို့မဟုတ် . နဲ့ ဆုံးတယ်။ အဲလို အတွက် pattern ကို [A-Z][a-z]*(\s|\.) ဆိုပြီး ရေးနိုင်ပါတယ်။

အကြိမ်အရေအတွက် zero or more ဖြစ်သည့် အတွက် ကြောင့် * ကို သုံးပါတယ်။ space သို့မဟုတ် . ဆိုသည့် အတွက်ကြောင့် (\s|\.) ကို အသုံးပြုထားပါတယ်။

Boolean or

apple သို့မဟုတ် orange ဆိုသည့် ကိစ္စများအတွက် vertical bar ကို အသုံးပြုပါတယ်။ ဥပမာ apple|orange ဟု ရေးပါသည်။

Grouping

Group ဖွဲချင်ရင် လက်သည်းကွင်း နှင့် အသုံးပြုပါတယ်။ gray | grey ဟု ရေးမည့် အစား gr(a|e)y ဟု group ဖွဲ့ပြီး ရေးသားနိုင်ပါသည်။

A ကနေ Z အထိ ကဲ့သို့ range လိုမျိုးကို [A-Z] ဟုရေးနိုင်တယ်။ A ကနေ Z , a ကနေ z , 0 ကနေ 9 ကဲ့သို့ range မျိုးအတွက် အကုန်ပေါင်းရေးနိုင်သည်။ [A-Za-z0-9] ။ စာလုံးသည် [A-Za-z0-9] ထဲတွင် ရှိသည် ဟု ဆိုလိုခြင်း ဖြစ်သည်။ ထို့အပြင် အခြား စာလုံးများလည်း ပေါင်းရေးနိုင်သည်။ [A-Za-z0-9#%] ဟု ဆိုလျှင် A-Z , a-z , 0-9 အပြင် #% စာလုံးများလည်း ဖြစ်နိုင်သည် ဟု ဆိုလိုပါသည်။

Quantification

Quantifier တွေ ကို အသုံးပြုရင် တစ်ခုသည်း အသုံးပြုလို့ မရပါဘူး။ character , group စသည့် တစ်ခုခု နဲ့ တွဲပြီး အသုံးပြုရပါတယ်။ အသုံးပြုလို့ ရသည့် quantifier တွေကတော့ အောက်ပါ အတိုင်းဖြစ်ပါသည်။

ExpressionDescription
?question mark ကတော့ zero or one အခြေအနေ ကို ရေးသားခြင်းဖြစ်ပါသည််။
*zero or more အခြေအနေ ကို ရေးသားရာတွင် အသုံးပြုသည်။
+one or more အခြေအနေကို ရေးသားရာတွင် အသုံးပြုသည်။
^**`စာကြောင်း၏ အစ စာလုံး ကို သတ်မှတ် ရာတွင် အသုံးပြုသည်။
$စာကြောင်း၏ အဆုံး စာလုံးကို သတ်မှတ်ရာတွင် အသုံးပြုသည်။
[^ ]range ထဲက စာလုံးများ မဟုတ်သည့် စာလုံးများ အတွက် အသုံးပြုသည်။
{n}စာလုံး ပါဝင်မည့် အရေအတွက် ကို စစ်ဆေးရန် အတွက် အသုံးပြုသည်။
{min,}အနည်းဆုံး ပါဝင်ရမည့် စာလုံး အရေအတွက် အသုံးပြုသည်။
{min,max}အနည်းဆုံး နှင့် အများဆုံး ပါဝင်ရမည့် စာလုံး အရေအတွက် စစ်ရာတွင် အသုံးပြုသည်။

Wildcard

ExpressionDescription
.မည်သည့် character အတွက် မဆို အသုံးပြုလိုလျှင် dot (.) လေးကို အသုံးပြုနိုင်သည်။
\dကိန်းဂဏန်း အားလုံးအတွက် \d ကိုအသုံးပြုနိုင်သည်။ [0-9] နှင့် အတူတူ ဖြစ်ပါသည်။
\Dဂဏန်း မဟုတ်သော် စာလုံးများကို စစ်ဆေးရာတွင် အသုံးပြုသည်။ [^0-9] နှင့် အတူတူဖြစ်သည်။
\wword ထဲတွင် ပါသော character များအတွက်အသုံးပြုသည်။ [A-Za-z0-9_] နှင့် တူညီသည်။
\Wword ထဲတွင် မပါသော character များ အတွက် ဖြစ်သည်။ [^A-Za-z0-9_] နှင့် တူညီသည်။
\swhite space character နှင့် တူညီ မူ အတွက် အသုံးပြုသည်။ [\r\n\t\f\v ] နှင့် တူညီ သည်။
\Swhite space character မဟုတ်ခြင်း အတွက် အသုံးပြုသည်။ [^\r\n\t\f\v ] နှင့် တူညီသည်။

အသုံးပြုပုံ

ကျွန်တော်တို့ ဖုန်းနံပတ်စစ်သည့် regular expression လေးရေးကြည့်ရအောင်။ Telenor ဖုန်းဟုတ်မဟုတ် စစ်ဖို့အတွက် rule က +959 သို့မဟုတ် 959 သို့မဟုတ် 09 သို့မဟုတ် 9 နဲ့ စမယ်။ နောက်မှာ 79 သို့မဟုတ် 78 သို့မဟုတ် 77 ဖြစ်ပါမယ်။ အနောက်မှာ 0 ကနေ 9 ထိ ဂဏန်း ၇ လုံး ပါမယ်။ အဲဒီ အတွက် ကျွန်တော်တို့တွေ အောက်မှာ ဖော်ပြထားသည့် အတိုင်း ရေးပါမယ်။

(\+?95|0?9)7(9|8|7)\d{7}$

က one ore more အခြေအနေ ကို စစ်ခြင်း နှင့် ရောနေသည့် အတွက်ကြောင့် backslash (\) ကို အသုံးပြုထားပါတယ်။ \+ ဆိုသည်မှာ + သည် စာလုံးဖြစ်ခြင်း ဟု ဖော်ပြထားခြင်း ဖြစ်သည်။ + နှင့် 0 သည် zero or one ဖြစ်နိုင်သည့် အခြေအနေ အတွက် ? နှင့် သုံးထားသည် ကို တွေ့နိုင်ပါသည်။ $ က တော့ စာလုံး အဆုံးကို ဆိုလိုပါတယ်။ ဂဏန်း နဲ့ မဆုံး ပဲ အခြား character နှင့် ဆုံးခဲ့ရင် မဟုတ်တော့ပါဘူး။

  • \d သည် digit ကို ဆိုလိုခြင်း ဖြစ်သည် ။ digit သည် 0 မှ 9 အထိ စာလုံးများ ပါဝင်သည်။
  • \d{7} သည် digital ၇ လုံး ဖြစ်ရမည် ဟု ဆိုလိုခြင်း ဖြစ်သည်။
  • \d{1,7} ဟု ရေးလျှင် အနည်းဆုံး digit ၁ လုံးမှ ၇ လုံး ထိ ပါရမည် ဆိုလိုခြင်းဖြစ်သည်။
  • \d{1,} ဟု ရေးလျှင် အနည်းဆုံး digit ၁ လုံးပါရမည် ဟု ဆိုလိုသည်။

Match

အထက်ပါ regular expression ကို python နှင့် တွဲပြီး အသုံးပြုကြည့်ပါမယ်။

import re

telenor = re.compile("(\+?95|0?9)7(9|8|7)\d{7}$")
if telenor.match("09791234567") == None :
    print("Not telenor")
else:
    print("telenor")

if telenor.match("09971234567") == None :
    print("Not telenor")
else:
    print("telenor")

regular expression အသုံးပြုမယ် ဆိုရင် import re ကို ထည့်ဖို့ လိုပါတယ်။ re.compile မှာတော့ ကျွန်တော်တို့ pattern ကို ထည့်ဖို့ လိုပါတယ်။ match လုပ်သည့်အခါမှာတော့ pattern နှင့် မကိုက်လျှင် None return ပြန်ပြီး pattern နှင့် ကိုက်ညီလျှင် match object return ပြန်ပါတယ်။ match object ကတော့ စာကြောင်းရဲ့ ဘယ်နေရာမှာ match ဖြစ်နေလဲ ဆိုတာကို ပြောပြပေးပါတယ်။

အခု နောက်ထပ် တစ်ခု စမ်းကြည့်ရအောင်။ ကျွန်တော်တို့ ထည့်ပေးထားသည့် စာလုံးထဲမှာ HTML စာလုံးဖြစ်သည့် bold ပါမပါ စစ်ကြည့်ပါမယ်။

import re

boldregex = re.compile(".*<b>(.*)</b>.*")
text = "Hello <b>World</b>"
match = boldregex.match(text)
print(match)

pattern ပုံစံကို လေ့လာကြည့်ရအောင်။

.*<b>(.*)</b>.*

လို့ရေးသားထားပါတယ်။ စာသားအစက ကြိုက်တာ ဖြစ်နိုင်ပြီးတော့ အဆုံးကလည်း ကြိုက်တာ ဖြစ်နိင်ပါတယ်။ <b> နှင့် </b> ကြားမှာတော့ စာလုံးကို group ဖွဲ့ထားပါတယ်။ match ကို print ထုတ်ကြည့်သည့် အခါမှာတော့ match object ကို ထုတ်ပြတာ တွေ့နိုင်ပါတယ်။

<_sre.SRE_Match object; span=(0, 18), match='Hello <b>World</b>'>

ကျွန်တော်တို့ အခု match object ကနေ ပြီး group ဖွဲ့ထားသည့် စာလုံးကို ဆွဲထုတ်ပါမယ်။ Group ဖွဲ့ထားသည့် ပုံအရ ဆိုရင်တော့ World ဆိုသည့် စာလုံး ဖော်ပြပေးဖို့ လိုပါတယ်။ ပထမဆုံး match ဖြစ်သည့် group ကို ထုတ်ကြည့်ရအောင်။

print(match.group(0))
print(match.group(1))
Hello <b>World</b>
World

ဆိုပြီး ထွက်လာပါမယ်။

findall

match က ကျွန်တော်တို့တွေ အပြည့်အစုံ အတိအကျ မှန်မှသာ အသုံးပြုပါတယ်။ စာလုံးကို ရှာချင်ရင်တော့ ကျွန်တော်တို့တွေ findall ကို အသုံးပြုနိုင်ပါတယ်။

import re

boldregex = re.compile("<b>(.*)</b>")
text = "Hello <b>World</b>"
match = boldregex.findall(text)
print(match)

အထက်ပါ code မှာ ဆိုရင် result က World ဆိုပြီး array return ပြန်ပါမယ်။ ကျွန်တော်တို့ text ကို နည်းနည်း ထပ်ပြင်ပြီး စမ်းကြည့်ပါမယ်။

import re

boldregex = re.compile("<b>(.*)</b>")
text = "Hello <b>World</b>! This is <b>bold</b>"
match = boldregex.findall(text)
print(match)

ဆိုရင်တော့ result က

['World</b>! This is <b>bold']

ဆိုပြီး ဖြစ်နေတယ်။ <b> နဲ့ စပြီး </b> ဆိုရင် ဖြစ်သည့် အတွက်ကြောင့် အခုလို ပြန်နေပါတယ်။ regular expression pattern ကို ပြန်ပြင်ပါမယ်။

import re

boldregex = re.compile("<b>(.*?)</b>")
text = "Hello <b>World</b>! This is <b>bold</b>"
match = boldregex.findall(text)
print(match)

ဆိုရင် result က

['World', 'bold']

ကျွန်တော်တို့ လိုချင်သည့် အဖြေရပါပြီ။ code မှာ .* အစား .*? လို့ ပြင်လိုက်ပါတယ်။ *? က အကုန်လုံးကို match လုပ်မယ်။ ဘယ်အထိလည်း ဆိုတော့ အနောက်က < /b> မပါလာသေးသည့် အထိ လို့ ဆိုလိုပါတယ်။ .* ဆိုရင် နောက်ဆုံး </b> ဖြစ်သည့် အထိပါ။ ဒါကြောင့် ကြားမှာ </b> ပါဝင် နေပါတယ်။

အခုဆိုရင် match နှင့် findall ကို နားလည်မယ် ထင်ပါတယ်။ match ကတော့ အတိကျမှန်သည့် စာကြောင်း အတွက် အသုံးပြုပါတယ်။ findall ကတော့ ပေးလိုက်သည့် pattern နှင့် တူညီသည့် စာများကို ရှာပေးပါတယ်။

အခု search နှင့် replace ကို လေ့လာပါမယ်။ ကျွန်တော်တို့တွေ Markdown ကနေ HTML ပြောင်းသည့် code လေးရေးပါမယ်။ အရင်ဆုံး Markdown code ကို အနည်းငယ် ရှင်းပြပါမယ်။

Markdown Parser

Markdown ဆိုတာကတော့ developer တွေ HTML အစား အသုံးများသည့် format တစ်ခုပါ။ Github မှာ README.md ဆိုပြီး README file ကို markdown နှင့် ရေးသားပါတယ်။ HTML ထက် ရှင်းလင်းပြီး ရေးသားရာမှာ မြန်ဆန် ပါတယ်။ HTML ကို လွယ်လင့် တကူ ပြောင်းပေးနိုင်ပါတယ်။

Header အတွက် ဆိုရင် # နဲ့ စပါတယ်။

# H1
## H2
### H3
#### H4

အထက်ပါ စာ တွေက အောက်ပါ HTML နှင့် ညီပါတယ်။

<h1>H1</h1>
<h2>H2</h2>
<h3>H3</h3>
<h4>H4</h4>

bold အတွက်ဆိုရင်တော့

**bold**

က

<b>bold</b>

နှင့် ညီပါတယ်။ Italic အတွက်

_italic_

က

<i>italic</i>

နှင့် ညီပါတယ်။

code အတွက်ဆိုရင်တော့

```js var k = 10; ```

က

<pre>
<code class="js">
var k = 10;
</code>
</pre>

အခု code တွေက Markdown မှာ အသုံးပြုသည့် code အချို့ပါ။ ကျွန်တော်တို့တွေ ဒီစာအုပ်မှာတော့ ဥပမာ အနေနဲ့ အကုန်လုံးကို မရေးပြပဲ အထက်ပါ markdown code အချို့ကို html ပြောင်းသည့် parser လေး ရေးပါမယ်။

ပထမဆုံး Header ကို ရေးကြည့်ရအောင်။

# H1 ဖြစ်သည့် အတွက်ကြောင့် # နှင့် စမယ် space လာမယ် ပြီးရင် ကြိုက်သည့်စာဖြစ်နိုင်တယ်။ ဒါဆိုရင် <h1>H1</h1> အနေဖြင့် ပြောင်းပေးဖို့ လိုပါတယ်။ သတိထားသည့်တာကတော့ စာကြောင်း အစ က # ဖြစ်ဖို့ လိုပါတယ်။ ဒါကြောင့် ^ ကို သုံးဖို့ လိုအပ်ပါတယ်။ အဲဒီအတွက် pattern က

^#\s(.*)

ပြောင်းမယ့် ပုံစံကတော့

<h1>\1<\h1>

\1 ဆိုတာကတော့ group 1 ကို ရည်ညွှန်းပါတယ်။ group တွေကတော့ လက်သည်းကွင်း နဲ့ သတ်မှတ်ပါတယ်။

Python code နဲ့ ပြောင်းရေးကြည့်ပါမယ်။

import re

parser = re.compile("^#\s(.*)")
text = "# Header 1"
replacement = parser.sub("<h1>\\1</h1>",text)
print(replacement)

python မှာ \ ကို ဖော်ပြဖို့ အတွက် \\ ဆိုပြီး အသုံးပြုဖို့ လိုပါတယ်။ ဒီထက် ပိုပြီး ရိုးရှင်းသည့် ပုံစံ ပြောင်းရေးပြပါမယ်။

import re

text = "# Header 1"
replacement = re.sub("^#\s(.*)","<h1>\\1</h1>",text)
print(replacement)

re.sub ဖြင့် တိုက်ရိုက်လည်း အသုံးပြုနိုင်ပါတယ်။ ဒါဆိုရင်တော့ h2 ကနေ h4 ထိ ရေးနိုင်မယ်လို့ ယုံကြည်ပါတယ်။

အခု bold နှင့် italic ကို ရေးကြည့်ရအောင်။ bold နှင့် italic က ပုံစံ အတူတူပါပဲ။ ကွာခြားချက်ကတော့ ** နှင့် _ ပဲ ကွာပါတယ်။ pattern က လည်း ဆင်ပါတယ်။ ** လာမယ်။ ပြီးရင် ကြိုက်သည့် string ဖြစ်နိုင်တယ်။ ပြီးလျှင် ** နှင့် ပြီးမယ်။ သတိထားသင့်တာကတော့ ** လာသည် နှင့် ပြီးဖို့ လိုပါတယ်။

Bold အတွက်

\*\*(.*?)\*\*

Italic အတွက်

_(.*?)_

Bold အတွက် code က

import re

text = "This is **bold**. This is another **bold again**."
replacement = re.sub("\*\*(.*?)\*\*","<b>\\1</b>",text)
print(replacement)

Italic အတွက် code က

import re

text = "This is _italic_. This is another _italic again_."
replacement = re.sub("_(.*?)_","<i>\\1</i>",text)
print(replacement)

အခု အခါမှာတော့ regex က နားလည် သည့် အခါမှာ လွယ်လင့် တကူ ရေးနိုင်တယ် ဆိုတာကို သတိပြုမိပါလိမ့်မယ်။

အခုနောက်ဆုံး code အတွက် ရေးကြည့်ရအောင်။ code က ``` နှင့်စတယ်။ ပြီးလျှင် language တစ်ခု လိုက်တယ်။ တစ်ကြောင်းဆင်းတယ်။ ပြီးလျှင် code တွေ လာတယ်။ ပြီးလျှင် တစ်ကြောင်းဆင်းတယ်။ နောက်ပြီး ``` လာတယ်။ အဲဒီ အတွက် pattern လေးရေးကြည့်ရအောင်။

```(.)(\n)+(.)(\n)+```

Replace အတွက် pattern ကတော့

<pre><code class="\1">\2\3\4</code></pre>

အခု python code နဲ့ တွဲပြီး စမ်းရေးကြည့်ရအောင်။

import re
text = """```js
var k = 10;
​```"""
replacement = re.sub("```(.*)(\n)+(.*)(\n)+```","<pre><code class=\"\\1\">\\2\\3\\4</code></pre>",text)
print(replacement)

code လေးက ရိုးရိုးရှင်းရှင်းပါပဲ။ ကျွန်တော်တို့ အပေါ်မှာ ပြောထားသည့် အတိုင်း regular expresison နှင့် search and repalce လုပ်သွားတာပါ။

အခုဆိုရင်တော့ regular expression ကို အနည်းငယ် သိပြီလို့ ထင်ပါတယ်။ reguler expression ဟာ email တွေစစ်ဆေးခြင်း အခြား number validation , string validation စတာတွေ အတွက် လည်း အသုံးဝင်ပါတယ်။

အခု လေ့ကျင့်ခန်း အချို့ကို ရေးသားကြည့်ရအောင်။

လေ့ကျင့်ခန်း ၁၁-၁

၁။ ကားလိုင်စင် စစ်ဆေးသည့် regular expression တစ်ခု ရေးသားပါ။ A-Z နှင့် စမယ်။ / လာမယ်။ ဂဏန်း ၅ လုံးပါမယ်။ / ပါမယ်။ ဂဏန်း ၂ လုံးပါမယ်။ ဥပမာ B/11111/16 ပုံစံပါ။

၂။ mention ခေါ်သည့် ပုံစံကို regular expression မှာ ရေးကြည့်ရအောင်။ @mgmg ဆိုပြီး ရေးထားသည့် စာတွေကို html အနေနဲ့ ပြန်ပြောင်းပါမယ်။ <a href="/username/mgmg">@mgmg</a> ဆိုပြီး ဖော်ပြပေးပါမည်။

၃။ markdown က code တစ်ကြောင်းအတွက် `code here` ဆိုရင် <code>code here</code> ဆိုပြီးပြောင်းပါတယ်။