φυβλαςのβλογ
phyblas的博客



เรียนรู้วิธีการใช้ regular expression (regex)
เขียนเมื่อ 2019/07/09 20:41
แก้ไขล่าสุด 2021/09/28 16:42
การวิเคราะห์ตัวหนังสือหรือข้อความเป็นงานอย่างหนึ่งที่สามารถทำได้โดยอาศัยการเขียนโปรแกรมเพื่อช่วยจัดการ

ในการเขียนโปรแกรมวิเคราะห์ข้อความนั้น มีเทคนิคหนึ่งที่ควรจะรู้เพราะเป็นประโยชน์เป็นอย่างมากนั่นคือสิ่งที่เรียกว่า เรกูลาร์ เอ็กซ์เพรชชัน (regular expression)

เนื่องจากชื่อยาวจึงมักจะถูกเรียกย่อๆว่า regex หรือบางทีก็สั้นๆเป็น re และในภาษาไทยบางครั้งก็อาจแปลว่า "นิพจน์ปกติ"

ต่อจากนี้ไปในบทความนี้จะเรียกสั้นๆว่า regex

ภาษาโปรแกรมต่างๆส่วนใหญ่ได้บรรจุคำสั่งสำหรับใช้งาน regex เอาไว้ แม้แต่ละภาษาจะมีไวยากรณ์ที่แตกต่างกัน แต่รูปแบบของ regex นั้นโดยรวมๆแล้วมีลักษณะเหมือนกัน แม้จะมีความแตกต่างในรายละเอียดปลีกย่อย

นอกจากเวลาเขียนโปรแกรมแล้ว ในพวกโปรแกรมแก้ไขข้อความ เช่น atom หรือ vscode พวกนี้ก็สามารถค้นหาโดยใช้ regex เช่นกัน จึงมีประโยชน์ที่จะเรียนรู้ ใช้งานได้กว้างขวาง

ในบทความนี้จะอธิบายการใช้ regex แบบทั่วไป โดยไม่พูดถึงวิธีการใช้ที่จำเพาะในภาษาต่างๆ เพื่อที่ว่าไม่ว่าใครใช้ภาษาอะไรอยู่ก็สามารถอ่านบทความนี้เพื่อเรียนรู้ regex ได้เหมือนกัน

เพียงแต่ว่าผลของ regex บางส่วนอาจมีการแสดงผลแตกต่างไปในแต่ละภาษาอย่างหลีกเลี่ยงไม่ได้ หากส่วนไหนสำคัญก็จะเน้นย้ำเป็นพิเศษให้รู้

โดยในที่นี้จะเน้นผลที่ปรากฏในภาษาจาวาสคริปต์ (javascript), ไพธอน (python), รูบี (ruby) และ php

หากตรงไหนไม่ได้เขียนเน้นว่าเป็นผลในภาษาไหนหมายความว่าใช้ได้เหมือนกันทุกภาษา (อย่างน้อยก็ใน ๔ ภาษาที่ยกมานี้ แต่ภาษาอื่นก็อาจมีอะไรต่างไปอีกได้) แต่ตรงไหนที่ต่างจะอธิบายแยกไว้

สารบัญ

จุดประสงค์ในการใช้ regex
ตัวทดลองใช้ regex
การค้นหาคัดกรองตัวเลขออกจากตัวหนังสืออื่นๆ (ใช้ \d และ \D)
สัญลักษณ์แทนตัวอักษรชนิดต่างๆ (เช่น \w, \W, \s, \S, ., ฯลฯ)
การกำหนดชุดตัวอักษรที่เข้าข่ายตามที่ต้องการ (ใช้วงเล็บเหลี่ยม [ ])
การกำหนดให้อักษรมีกี่ตัวก็ได้ (ใช้ +)
การกำหนดให้อักษรมีหรือไม่มีก็ได้ (ใช้ ? และ *)
การกำหนดให้อักษรมีจำนวนตามที่กำหนด (ใช้วงเล็บปีกกา { })
โลภมากหรือไม่โลภมาก (ใช้ ?)
การคัดกรองให้เข้าข่ายเฉพาะเริ่มต้นและปิดท้ายสายอักขระ (ใช้ ^ หรือ $)
※ ตัวเลือกเสริมให้การเริ่มบรรทัดใหม่ถือเป็นจุดตั้งต้นคำ
※ ตัวเลือกเสริมให้ไม่แยกแยะตัวพิมพ์เล็กพิมพ์ใหญ่
การคัดกรองให้เข้าข่ายเฉพาะเมื่ออยู่ต้นหรือท้ายคำ (ใช้ \b)
การกำหนดกลุ่มคำที่ตามด้วยคำที่ต้องการ (ใช้ (?= ) และ (?! ))
การกำหนดกลุ่มคำที่มาหลังคำที่ต้องการ (ใช้ (?<= ) และ (?<! ))
การเลือกเอาแค่บางส่วน (ใช้วงเล็บโค้ง ( ))
การนำกลุ่มก้อนมาอ้างอิงเพื่อหาส่วนที่ปรากฏซ้ำ (ใช้ \หมายเลข)
※ การใช้ regex เพื่อแทนที่
※ การใช้ regex เพื่อแยกส่วนคำ
การจับกลุ่มก้อนเฉยๆ (ใช้ (?: ))
การตั้งให้รูปแบบเปลี่ยนไปตามเงื่อนไข (ใช้ (?( ) | ) )
การกำหนดทางเลือกให้เข้าข่ายหลายตัว (ใช้ |)


จุดประสงค์ในการใช้ regex

ก่อนอื่นต้องมาทำความเข้าใจกันให้ดีก่อนว่า regex คืออะไร

ขอยกตัวอย่างง่ายๆว่า สมมุติเรามีข้อความ (สายอักขระ) ยาวๆอันนึงแบบนี้ที่มีทั้งตัวเลขและตัวอักษรปนกัน
d7fjk38fbk84 s84kkc88slsy4ub5cjo 1293a8a1lp9

หากต้องการค้นเอาเฉพาะส่วนที่เป็นตัวเลขอย่างเดียว หรือตัวหนังสืออย่างเดียวจะทำอย่างไร?

เวลาใช้พวกโปรแกรมแก้ไขข้อความ หรือแม้แต่ในเว็บเราสามารถกดค้นคำได้ แต่ปกติจะแค่พิมพ์อักษรที่ต้องการค้น เช่นต้องการหาเลข 1 ก็พิมพ์ 1 ต้องการหาเลข 9 ก็พิมพ์เลข 9 แต่ถ้าหากต้องการหาเลขตั้งแต่ 0 ถึง 9 พร้อมกันทีเดียวจะทำอย่างไร?

ตรงนี้เองคือสิ่งที่สามารถใช้ประโยชน์จาก regex ได้

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

ต่อไปมาดูวิธีการ



ตัวทดลองใช้ regex

เพื่อให้สะดวกที่จะเรียนรู้ เราได้เตรียมที่สำหรับทดสอบ regex เอาไว้ในบล็อกนี้ สามารถไปลองทดสอบใช้กันได้

>> https://phyblas.hinaboshi.com/regex

ในการใช้งานให้ใส่คำค้น regex ที่ช่องด้านบน แล้วใส่ข้อความที่ช่องด้านล่าง จากนั้นผลลัพธ์การค้นจะแสดงขึ้นทางฝั่งขวา

ส่วนโหมดและตัวเลือกเสริมนั้นเดี๋ยวจะอธิบายทีหลัง ตอนนี้ถ้าไม่ได้กล่าวถึงเป็นพิเศษให้ถือว่าไม่ได้ติ๊กตัวเลือกเสริมอะไร ส่วนโหมดเป็นค้นหาธรรมดา

โค้ดนี้ถูกเขียนด้วยจาวาสคริปต์ ดังนั้นหมายความว่าผลการค้นหาจะเป็นของจาวาสคริปต์เท่านั้น แต่ที่ยกตัวอย่างต่อไปนี้จะมีบางส่วนที่ใช้ในจาวาคริปต์ไม่ได้อยู่ด้วย (ซึ่งจะมีบอกไว้ชัด) จึงไม่สามารถทดสอบตามในนี้ได้ แต่ถ้าใครสามารถรัน regex ในไพธอนหรือรูบีได้ก็ลองทดสอบดู จะเห็นผลตามนั้นได้



การค้นหาคัดกรองตัวเลขออกจากตัวหนังสืออื่นๆ (ใช้ \d และ \D)

เริ่มแรกเปิดหน้าตัวทดสอบ regex ขึ้นมา ลองใส่ตัวหนังสือยุ่งๆเมื่อครู่นี้ลงในช่องข้อความ แล้วลองใส่อักษร \d ในช่องคำค้น

ผลที่ได้ก็จะออกมาเป็นแบบนี้


จะเห็นว่าเลข 8 ถูกไฮไลต์ทั้งหมด และแต่ละตัวก็แสดงให้เห็นตรงด้านล่างด้วย

ข้อความค้นก็คือตัวที่เราต้องการค้นนั่นเอง แต่ว่าใส่แค่เลข 8 ไปง่ายๆแบบนี้มันก็จะค้นเจอแต่เลข 8 ดูเหมือนพิมพ์ข้อความค้นธรรมดา ไม่มีอะไรเป็นพิเศษ ยังไม่ได้ใช้ประโยชน์จาก regex

ทีนี้ลองเปลี่ยนเป็นพิมพ์ \d ลงไปดู


จะพบว่าที่เป็นตัวเลขทั้งหมดถูกเลือกและแสดงไว้ที่ด้านล่างทั้งหมด

ในการใช้ regex นั้น \d เป็นโค้ดที่เอาไว้แทนตัวเลขใดๆก็ได้ตั้งแต่ 0 ถึง 9 ดังนั้นแค่พิมพ์ \d ลงในช่องค้นจึงเป็นการหาตัวเลขใดๆก็ได้ทั้งหมดในทีเดียว

และถ้าพิมพ์ \d ไป ๒ ตัวจะเป็นการค้นตัวเลขที่อยู่ติดกัน ๒ ตัว


ในทางตรงกันข้าม มี \D ซึ่งมีความหมายตรงกันข้ามกับ \d คือแทนอะไรก็ตามที่ไม่ใช่ตัวเลข

ลองใช้ \D ค้นดู


คราวนี้จะได้แต่เฉพาะที่ไม่ใช่ตัวเลขแทน

หรือถ้าต้องการหาที่เป็นตัวเลขแล้วตามด้วยที่ไม่ใช่ตัวเลขก็ใส่ \d\D แบบนี้





สัญลักษณ์แทนตัวอักษรชนิดต่างๆ (เช่น \w, \W, \s, \S, ., ฯลฯ)

นอกจาก \d ซึ่งแทนตัวเลข และ \D ที่แทนสิ่งอื่นนอกจากตัวเลขแล้วยังมีรูปแบบอื่นๆอีกมากมายซึ่งก็มักเขียนแทนด้วยแบ็กสแลช (\) แบบนี้ มีดังนี้

\d ตัวเลข 0-9
\D ตัวอื่นนอกจากตัวเลข 0-9
\t แท็บ
\n ขึ้นบรรทัดใหม่
\s สเปซบาร์หรือแท็บหรือขึ้นบรรทัดใหม่
\S ตัวอื่นนอกจากสเปซบาร์หรือแท็บหรือขึ้นบรรทัดใหม่
\w ตัวเลขและอักษร รวมถึงขีดล่าง (_)
\W ตัวอื่นที่ไม่ใช่ \w
. (จุด) แทนตัวอักษรอะไรก็ได้

จะเห็นว่ามีการแยกตัวพิมพ์ใหญ่และตัวพิมพ์เล็ก โดยที่ตัวพิมพ์ใหญ่เช่น \D \S \W จะมีความหมายตรงกันข้ามกับตัวพิมพ์เล็ก \d \s \w ต้องแยกใช้ให้ดี

สำหรับ \w นั้นมีข้อควรระวัง คือผลที่ได้จะต่างกันไปโดยในจาวาสคริปต์และรูบี \w จะแทนแค่ตัวอักษรภาษาอังกฤษและตัวเลขและ _

แต่ในไพธอนจะรวมทั้งตัวอักษรภาษาอื่นๆด้วย

ตัวอย่างเช่นลองค้นด้วย \w ในจาวาสคริปต์, รูบี และ php จะเป็นแบบนี้


แต่ถ้าค้นในไพธอนจะได้ผลแบบนี้


การที่ผลลัพธ์ต่างกันแบบนี้ ทำให้การใช้ \w ในการค้นอาจต้องระวังเป็นพิเศษ

ส่วน \W นั้นก็จะเป็นตัวที่ตรงกันข้ามกับ \w ดังนั้นจึงมีความหมายเปลี่ยนไปด้วยเช่นกัน



ส่วน \s จะแทนตัวอักษรเว้นวรรคหนึ่งตัว นั่นคือสเปซบาร์ (" ", คือช่องว่าง) และแท็บ (\t, คือการเคาะเว้นย่อหน้า) รวมถึงการขึ้นบรรทัดใหม่ (\n)

ตัวอย่าง เช่น หาส่วนประกอบที่เป็นตัวหนังสือที่ไม่ใช่ตัวเลขสองตัวซึ่งคั่นด้วยช่องว่าง


ที่จริงตัวอย่างนี้จะใช้ช่องว่าง " " แทน \s นั่นคือ '\D \D' ก็ได้ผลเช่นเดียวกัน (ช่องว่างก็นับเป็นอักษรตัวหนึ่ง)

แต่ \s นั้นจะรวมแท็บและการขึ้นบรรทัดใหม่ (\n) ด้วย ตัวอย่างเช่น


และหากต้องการให้อักษรตัวหนึ่งแทนอะไรก็ได้ ก็ให้ใช้ . (จุด) เช่น


เพียงแต่ว่ากรณีที่ต้องการจะค้นหาอักษรจุดจริงๆจะต้องใช้ \ นำหน้ากลายเป็น \. แบบนี้ ไม่เช่นนั้นจะกลายเป็นการหาตัวอะไรก็ได้ไป เช่น


แต่พอลองใส่ \ นำหน้าจึงจะกลายเป็นการหาแค่ a.


ไม่ใช่แแค่จุดเท่านั้น มีเครื่องหมายอีกหลายตัวที่ทำหน้าที่พิเศษ เช่น ()[]{}|+*?\^$ ซึ่งจะกล่าวถึงต่อไป หากต้องการค้นอักษรพวกนี้จริงๆจำเป็นต้องใส่ \ นำหน้า แม้แต่ตัว \ เองก็ด้วย

หากใส่ \\ จะหมายถึงการหาตัว \ ตัวเดียว เช่นถ้าหากใส่ \\. แบบนี้จะหมายถึงค้นหา \ แต่จุดที่อยู่หลัง . จะหมายถึงการหาตัวอักษรใดๆ ตามความสามารถเดิมของมัน




การกำหนดชุดตัวอักษรที่เข้าข่ายตามที่ต้องการ (ใช้วงเล็บเหลี่ยม [ ])

กลุ่มตัวอักษรชนิดที่กำหนดด้วยแบ็กสแลชนั้นแค่เป็นการแบ่งกลุ่มแบบคร่าวๆ แต่ก็อาจมีบ่อยครั้งที่เราต้องการเจาะจงอักษรบางกลุ่มที่กำหนดขึ้นเอง

กรณีแบบนี้สามารถกำหนดกลุ่มตัวอักษรตามที่ต้องการเอาเองได้โดยใช้วงเล็บเหลี่ยมล้อม [ ] แล้วใส่อักษรที่ต้องการทั้งหมดในนั้น

เช่น ลองหาคำที่เป็นอักษรกลางตามด้วยอะไรก็ได้อีกตัวนึง ก็เอา [ ] คร่อมอักษรที่ต้องการทั้งหมดไว้


หากมีอักษรหลายตัวที่ต้องการให้เข้าข่ายก็จะต้องเขียนไล่ไปทั้งหมด เช่นต้องการ a ถึง m ก็ต้องพิมพ์ [abcdefghijklm] แบบนี้ดูแล้วค่อนข้างยาว

แต่หากอักษรที่ต้องการนั้นเป็นตัวที่ลำดับติดกันแบบนี้ มีวิธีการเขียนที่ง่ายขึ้นเพื่อความสะดวก นั่นคือใช้เครื่องหมาย - เพื่อแสดงช่วง

เช่น ถ้าใส่ [a-y] หมายถึงอักษรตั้งแต่ a ถึง y [ก-ช] หมายถึงอักษรตั้งแต่ ก ถึง ช เป็นต้น

ลำดับอักษรในที่นี้เป็นลำดับที่เรียงตามเลขในยูนิโค้ด สำหรับภาษาไทยแล้วอักษรตัวแรกสุดคือ "ก" และตัวสุดท้ายคือเลข "๙" ดังนั้นหากต้องการแสดงถึงอักษรไทยทั้งหมดก็เขียนเป็น [ก-๙]

หากเขียน [ก-ฮ] จะเท่ากับ [กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮ] นั่นคือพยัญชนะทั้ง ๔๔ ตัว รวม "ฤ" กับ "ฦ" ด้วย

เพราะตามหลักเวลาเปิดพจนานุกรม "ฤ" กับ "ฦ" จะแทรกอยู่ลำดับถัดจาก "ร" และ "ล" ดังนั้นในยูนิโค้ดก็เรียงโดยยึดตามนี้ด้วย

สำหรับอักษรจีนจะใช้ [一-龥] เพื่อครอบคลุมทุกตัวอักษร ส่วนอักษรญี่ปุ่นฮิรางานะใช้ [ぁ-ゞ] และคาตาคานะใช้ [ァ-ヶ]

ตัวอย่างเช่น



ในทางตรงข้าม ถ้าต้องการเอาอักษรทุกตัวยกเว้นบางตัวก็จะใช้ ^ มาใส่เป็นตัวขึ้นต้นในวงเล็บเหลี่ยม เช่น



ผลที่ได้ตรงข้ามกับตัวอย่างที่แล้ว

อักษร - ที่ใช้คั่นระหว่าง ๒ อักษรที่ต้องการนี้ถือเป็นอักษรพิเศษในกรณีนี้ ไม่ได้ถูกตีความเปํนอักษรตัวหนึ่ง เช่น [ค-ญ] ไม่ได้หมายถึงอักษร ค หรือ - หรือ ญ แต่หมายถึงอักษรตั้งแต่ ค ถึง ญ

แต่ว่าหากต้องการให้ - รวมอยู่ในกลุ่มที่เข้าข่ายด้วยก็ให้วางไว้เป็นตัวแรก เพราะหากวางไว้ต่อจากตัวอักษรอื่นจะกลายเป็นการกำหนดช่วงตั้งแต่อักษรตัวนั้นไปจนถึงอักษรที่อยู่ทางขวา

เช่นตัวอย่างนี้หมายถึงค้นตัวที่อยู่ระหว่าง + ถึง ÷


จะเห็นว่าที × ด้วย ทั้งๆที่ไม่ได้ใส่ × มาด้วย

ส่วนถ้าเขียน -+÷ แบบนี้จะกลายเป็นการค้นแค่ -+÷ ๓ ​ตัวนี้


ถ้าหากอักษรที่ใส่ไปตัวหน้า - อยู่ในลำดับยูนิโค้ดหลัง - ก็จะเกิดข้อผิดพลาดขึ้น เช่น


ตัวอย่างนี้ผิดพลาดเพราะ + มาทีหลัง * ตามลำดับยูนิโค้ด

ทั้งอักษร + และ * นี้ที่จริงก็เป็นอักษรที่มีความหมายพิเศษเช่นกัน (ซึ่งจะกล่าวถึงในหัวข้อถัดไป) เพียงแต่หากอยู่ภายในวงเล็บเหลี่ยม [ ] แล้วความหมายของอักษรบางตัวจะเปลี่ยนไป อักษรบางตัวที่เดิมทีมีความหมายพิเศษก็จะไม่มีความหมายพิเศษอะไรเมื่ออยู่ใน [ ]



การกำหนดให้อักษรมีกี่ตัวก็ได้ (ใช้ +)

หากมีอักษรที่ต้องการให้มีแต่ไม่ได้เจาะจงว่าจะต้องมีกี่ตัว จะมีหนึ่งตัวหรือกี่ตัวก็ได้ กรณีแบบนี้ให้เติม + ตามหลังตัวอักษรที่ต้องการ

เช่น ต้องการตัวที่ "ก" ตามหลังตัวเลขอะไรก็ได้กี่ตัวก็ได้


กรณีที่เป็นชุดอักษรที่กำหนดด้วยกรอบ [] ก็ใส่ + ได้เช่นกัน เช่น ลองหาอักษรที่เป็นพยัญชนะ (ก-ฮ) ที่อยู่ติดกัน


แบบนี้จะเห็นว่าพยัญชนะอะไรก็ได้ที่อยู่ติดกันก็จะรวมอยู่ในกลุ่มเดียวกัน





การกำหนดให้อักษรมีหรือไม่มีก็ได้ (ใช้ ? และ *)

หากเติมเครื่องหมายคำถาม ? ลงข้างหลังตัวอักษรจะหมายความว่าอักษรตัวนั้นจะมีหรือไม่มีอยู่ก็ได้

ตัวอย่างเช่น


และถ้าใช้ดอกจัน * จะหมายถึงว่าจะมีหรือไม่มีก็ได้และถ้ามีจะมีกี่ตัวก็ได้


ข้อแตกต่างระหว่าง * กับ + ก็คือ + จะต้องมีอย่างน้อย ๑ ตัว แต่ * จะไม่มีเลยก็ได้



การกำหนดให้อักษรมีจำนวนตามที่กำหนด (ใช้วงเล็บปีกกา { })

ปกติแล้วต้องการให้มีอักษรกี่ตัวก็ต้องเขียนอักษรไปเป็นจำนวนเท่านั้น เช่นตัวเลข ๔ ตัวก็เขียน \d\d\d\d แต่ว่าหากมีจำนวนมากการเขียนแบบนี้จะยาว

การใช้วงเล็บปีกกาจะช่วยให้การเขียนสั้นลงได้ โดยเขียนตัวที่ต้องการซ้ำแล้วตามด้วยวงเล็บปีกกาที่คร่อมด้วยจำนวนที่ต้องการ

ตัวอย่างเช่น ต้องการตัวเลขยาวต่อกัน ๓ ตัว อาจเขียนว่า \d{3} แทนที่จะเขียนเป็น \d\d\d



แต่บางครั้งจำนวนก็อาจไม่ได้รู้แน่ชัดแต่มีขอบเขตที่แน่นอนอยู่ค่าหนึ่ง แบบนั้นก็สามารถใส่จุลภาค , ไว้ใน {} โดยจำนวนที่อยู่ทางซ้าย , คือขอบเขตล่าง ถ้าไม่ใส่คือ 0 ทางขวา , คือขอบเขตบน ถ้าไม่ใส่คือไม่จำกัดขอบเขต

\d{2,3} เท่ากับ \d\d\d? คือตัวเลข ๒ ถึง ๓ ตัว

\d{,3} เท่ากับ \d{0,3} หรือ \d?\d?\d? คือตัวเลขไม่เกิน ๓ ตัว

\d{2,} เท่ากับ \d\d+ หรือ \d\d\d* คือตัวเลข ๒ ตัวขึ้นไป

ตัวอย่าง หาเลขโทรศัพท์ ซึ่งควรจะขึ้นต้นด้วย 0 แล้วมีความยาว ๙ ถึง ๑๐ ตัว





โลภมากหรือไม่โลภมาก (ใช้ ?)

ข้อควรระวังที่สำคัญอย่างหนึ่งในการใช้ + หรือ * ก็คือ ปกติแล้วเวลาที่ใช้ + หรือ * นั้นจะมีอักษรถูกรวมอยู่ในกลุ่มกี่ตัวก็เป็นไปได้

ซึ่งในบางครั้งก็ทำให้เกิดความคลุมเครือว่าต้องการกี่ตัวกันแน่ และทำให้เกิดผลที่ไม่ได้คาดหวังขึ้นมาได้

ยกตัวอย่างเช่นถ้าเราต้องการค้นหาคำอะไรก็ได้ยาวแค่ไหนก็ได้ที่ลงท้ายด้วยสระ "า" ก็อาจเขียนเป็น '\S+า' เช่น


จะเห็นว่าพอเขียนแบบนี้แล้ว \S+ จะแทนตัวอักษรตั้งแต่ตัวแรกจนถึงก่อน "า" ตัวสุดท้าย

แต่ในความเป็นจริงก็มี "า" นำหน้าอยู่ก่อนตั้ง ๒ ตัวซึ่งถ้าจะนับรวมถึงแค่ "า" ตัวแรกก็ยังถือว่าเข้าข่ายรูปแบบที่ต้องการอยู่ดี

แต่ว่าโดยทั่วไปแล้วโปรแกรมจะพยายามหากลุ่มอักษรที่เข้าข่ายให้ยาวที่สุดดังนั้นต่อให้เจออักษรที่เข้าข่าวตามที่ต้องการแล้วหากไล่ตัวต่อไปดูแล้วยังเข้าข่ายอยู่มันก็จะนับต่อไปจนสุด

ลักษณะแบบนี้เรียกว่าเป็นการจับกลุ่มแบบ "โลภมาก" แต่เราสามารถทำให้โปรแกรมหยุดหาต่อทันทีที่เจอกลุ่มตัวอักษรที่เข้าข่ายครบในขั้นต่ำ ซึ่งทำได้โดยการเติม ? ลงไปต่อท้าย + หรือ * เช่น


พอทำแบบนี้แล้วเมื่อเจอ "า" ก็จะแยกเป็นก้อนหนึ่งทันที

ลักษณะแบบนี้เรียกว่าเป็นการจับกลุ่มแบบ "ไม่โลภมาก" คือพอได้ตามเงื่อนไขแล้วก็พอเลย ไม่เอาต่อแล้ว

จะเป็นแบบโลภมากหรือไม่นั้นต่างกันแค่เติม ? ต่อท้ายหรือไม่เท่านั้น

? ในที่นี้มีความหมายต่างจาก ? ที่ตามหลังตัวอักษรทั่วไปเพื่อแทนการมีหรือไม่มีก็ได้ของตัวนั้น และมันสามารถตามหลัง + หรือตามหลัง * หรือ {} หรือแม้แต่ ? เองก็ได้

ตัวอย่างอื่นๆ เช่น


แบบนี้คือโลภมาก ถ้าเอา ๕ อันได้ก็เอา ถ้ามีไม่ครบ ๕ ก็ค่อยพอใจอยู่แค่นั้น

ส่วนแบบนี้คือไม่โลภมาก เจอแค่ ๒ ซึ่งเป็นขั้นต่ำก็หยุดแล้ว


อีกตัวอย่าง คราวนี้ใช้กับ *

โลภมาก


ไม่โลภมาก


กรณีที่ regex ทั้งหมดมีแต่ส่วนที่ใช้ * หรือ ? นั้นจำนวนอักษรขั้นต่ำคือ 0 หมายความว่าถึงไม่มีเลยก็นับ ซึ่งอาจชวนให้เป็นห่วงว่าเวลาค้นแบบไม่โลภมาก (เติม ?) จะได้สายอักขระว่าง "" เป็นจำนวนไม่สิ้นสุด แต่ในความเป็นจริงแม้จะไม่นับเลยก็ยังไล่ไปยังอักษรตัวถัดไป ดังนั้นจึงไม่มีปัญหา

โลภมาก


ไม่โลภมาก


โลภมาก


ไม่โลภมาก





การคัดกรองให้เข้าข่ายเฉพาะเริ่มต้นและปิดท้ายสายอักขระ (ใช้ ^ หรือ $)

หากต้องการให้เข้าข่ายแค่เฉพาะเมื่อคำนั้นอยู่ที่ตำแหน่งแรกของสายอักขระเท่านั้นให้ใส่ ^ นำหน้า

และหากต้องการให้เข้าข่ายแค่เฉพาะเมื่อคำนั้นอยู่ที่ตำแหน่งท้ายสุดก็ใส่ $ ต่อท้าย

ตัวอย่างเช่น กรณีเอาเฉพาะเมื่อขึ้นต้นคำ


กรณีเอาเฉพาะเมื่อลงท้ายคำ





ตัวเลือกเสริมให้การเริ่มบรรทัดใหม่ถือเป็นจุดตั้งต้นคำ

เมื่อใช้ ^ ปกติแล้วผลที่ได้จะออกมาแค่คำเดียวเท่านั้น คือดูแค่คำที่อยู่ต้น เพราะคำอื่นนอกจากที่อยู่อักษรตัวแรกไม่มีทางเข้าข่าย

$ ก็เช่นเดียวกัน คำที่เข้าข่ายจะต้องอยู่ตรงท้ายสุดเท่านั้น

อย่างไรก็ตาม ปกติเวลาใช้ regex มักจะมีตัวเลือกเสริม หรือที่เรียกว่าแฟล็ก (flag) ให้เลือก

โดยมีตัวเลือกหนึ่งคือ m หรือชื่อเต็มคือ MULTILINE ตัวเลือกนี้ถ้าหากเลือกแล้วจะทำแต่ละบรรทัดคิดแยกกัน ต้นบรรทัดก็ถือเป็นจุดเริ่มต้นคำ จึงเข้าข่ายเมื่อใช้ ^ และท้ายบรรทัดก็ถือเป็นท้ายคำ จึงเข้าข่ายเมื่อใช้ $

ลองใส่ตัวเลือกนี้แล้วค้นด้วย $ ดู จะพบว่าขอแค่อยู่ท้ายบรรทัดแล้วตรงตามเงื่อนไขก็จะเข้าข่าย


แต่ถ้าลองไม่ใส่ตัวเลือกนี้ จะมีเฉพาะตัวสุดท้ายของบรรทัดสุดท้ายเท่านั้นที่เข้าข่าย


ถ้าเขียนโปรแกรม ในแต่ละภาษาก็มีวิธีการใส่ตัวเลือกเสริมแบบนี้ต่างกันออกไป จาวาสคริปต์หรือรูบีก็แค่เติม m ต่อท้าย เช่น /\s+า$/m ส่วนในไพธอนต้องเติม re.M หรือ re.MULTILINE



ตัวเลือกเสริมให้ไม่แยกแยะตัวพิมพ์เล็กพิมพ์ใหญ่

ตัวเลือกเสริมอีกอย่างที่สำคัญก็คือ การพิจารณาว่าจะแยกแยะตัวพิมพ์ใหญ่พิมพ์เล็กหรือเปล่า

ปกติแล้วเวลาใช้ regex ตัวอักษรโรมันเช่นภาษาอังกฤษนั้น พิมพ์เล็กกับพิมพ์ใหญ่จะถือเป็นคนละตัวกัน ดังนั้นถ้าค้นแค่ตัวพิมพ์เล็กในการค้นหา ตัวพิมพ์ใหญ่จะไม่ได้ถูกค้นเจอด้วย


แต่ก็สามารถใส่ตัวเลือกเสริมให้ไม่มีการแยกแยะตัวพิมพ์เล็กพิมพ์ใหญ่ได้ ซึ่งมีผลทั้งอักษรโรมันปกติ และอักษรพิเศษอย่าง æ ç é รวมถึงอักษรกรีกอย่าง δ/Δ (เดลตา) ด้วย แต่ไม่มีผลใดๆกับอักษรญี่ปุ่น


เพียงแต่ว่าใน php จะมีผลเฉพาะตัวอักษร a-z ธรรมดาเท่านั้น พวก æ ç é หรืออักษรกรีก จะยังคงแยกพิมพ์เล็กพิมพ์ใหญ่

เวลาเขียนโปรแกรม ในจาวาสคริปต์หรือรูบีให้เติม i ต่อท้าย เช่น /[æçéΔあ]/i ส่วนในไพธอนต้องเติม re.I หรือ re.IGNORECASE



การคัดกรองให้เข้าข่ายเฉพาะเมื่ออยู่ต้นหรือท้ายคำ (ใช้ \b)

^ กับ $ นั้นจะใช้กรณีที่คัดคำที่อยู่ต้นหรือท้ายของทั้งบรรทัดหรือทั้งสายอักขระเท่านั้น (แล้วแต่ตัวเลือกเสริม) กรณีที่ต้องการคัดเอาเฉพาะคำที่แค่อยู่ต้นหรือท้ายคำแต่ไม่ใช่ต้นหรือท้ายของทั้งสายอักขระ แบบนี้จะใช้ \b

ความหมายของต้นคำและท้ายคำนี้ขึ้นอยู่กับความหมาย \w และ \W กล่าวคือ \b จะเข้าข่ายเมื่อเจอตัวที่เข้าข่าย \w แล้วต่อด้วยตัวที่เป็น \W (คือไม่ใช่ \w) หรือเจอ \w ที่นำหน้าด้วย \W

ดังนั้นความหมายของ \b นี้จะต่างกันไปขึ้นอยู่กับความหมายของ \w ด้วย ในไพธอน \w จะมีความหมายรวมถึงอักษรสำหรับสร้างคำในแต่ละภาษา ดังนั้นตัวที่ถือเป็นตัวกั้นคำก็คือพวกสัญลักษณ์ต่างๆ เช่น ,./?!\<>[]{}()*&^%$#@~ ยกเว้นแค่ _ เท่านั้น

แต่ในจาวาสคริปต์, รูบี และ php นั้น \w จะรวมแค่อักษรภาษาอังกฤษและตัวเลขเท่านั้น ทำให้อักษรภาษาอื่นก็กลายเป็นตัวกั้นไปด้วย

ตัวอย่าง
จะเห็นว่าคำที่เข้าข่ายนั้นคือพวกที่ขึ้นต้นและลงท้ายด้วยตัวอักษรภาษาอังกฤษ นอกจากนี้ *pl ก็เข้าข่ายมาด้วยแบบน่าประหลาด ที่เป็นแบบนี้ก็เพราะว่า * อยู่ในกลุ่ม \W แต่ทางซ้ายของ * เป็น l ซึ่งอยู่ในกลุ่ม \w แต่ทางขวาลงท้ายด้วย l ซึ่งอยู่ในกลุ่ม \w แต่ตัวถัดมาเป็น ส ซึ่งอยู่ในกลุ่ม \W

แต่ในไพธอน การตีความ \w จะต่างไป ผลจะเป็นดังนี้


ส่วน \B จะตรงกันข้ามกับ \b คือจะทำให้เข้าข่ายในกรณีที่ไม่อยู่ริม






การกำหนดกลุ่มคำที่ตามด้วยคำที่ต้องการ (ใช้ (?= ) และ (?! ))

บางครั้งเราอาจต้องการหากลุ่มอักษรที่เข้าข่ายที่ต้องการกลุ่มหนึ่ง แต่ไม่ได้ต้องการจะเอามันมาใช้ทั้งหมด เช่นว่า "อยากได้อักษรนี้แค่เฉพาะเมื่ออยู่หน้าอักษรนี้" เป็นต้น ในกรณีแบบนี้สามารถใช้ ?=

เช่น ถ้าต้องการตัวอักษรอะไรก็ได้ที่นำหน้า a แต่ไม่เอา a ด้วย ให้เขียนแบบนี้


เพราะถ้าเราตัด (?= ) ออกไป เหลือแค่ \Sa เฉยๆ จะหมายความว่าเราเอา a ด้วย


ดังนั้นการที่ใส่ (?= ) จึงเป็นการตัด a ออกจากกลุ่มที่จะเอา เอาแค่ตัวข้างหน้า

ในทางตรงกันข้าม เราอาจจะ "อยากได้ตัวนี้เฉพาะเมื่อไม่นำหน้าตัวนี้" กรณีแบบนี้ให้ใช้ ?!

เช่นต้องการหาพยัญชนะสักตัวที่ไม่ตามด้วย a





การกำหนดกลุ่มคำที่มาหลังคำที่ต้องการ (ใช้ (?<= ) และ (?<! ))

ในทำนองเดียวกันกับข้อที่แล้ว บางครั้งเราอาจจะมีเงื่อนไขว่า "อยากได้อักษรนี้แค่เฉพาะเมื่ออยู่หลังอักษรนี้" กรณีแบบนี้อาจใช้ (?<= )

เพียงแต่ว่ารูปแบบการเขียนนี้ ไม่สามารถใช้ในจาวาสคริปต์ได้ ต้องใช้ในไพธอนหรือรูบีเท่านั้น ถ้าใช้ในจาวาสคริปต์ก็จะเกิดข้อผิดพลาดขึ้น

ดังนั้นตัวอย่างต่อไปนี้ต้องทดลองในไพธอน, รูบี หรือ php เอา ถ้าหากลองในโปรแกรมที่เตรียมไว้ซึ่งเขียนด้วยจาวาสคริปต์ดูจะเกิดข้อผิดพลาดขึ้น

ตัวอย่างเช่น หาอักษรที่ตามด้วย "เ" แต่ไม่เอา "เ" ด้วย


และในทางตรงกันข้าม ถ้า "อยากได้อักษรนี้แค่เฉพาะเมื่อไม่อยู่หลังอักษรนี้" ก็ใช้ (?<! ) อันนี้ก็ใช้ในจาวาสคริปต์ไม่ได้เช่นเดียวกัน



การเลือกเอาแค่บางส่วน (ใช้วงเล็บโค้ง ( ))

การใช้ (?= ) หรือ (?<= ) ดังตัวอย่างที่ผ่านมานั้นอาจเข้าใจยาก อีกทั้งยังใช้ในจาวาสคริปต์ไม่ได้ด้วย แต่ก็มีวิธีที่สามารถใช้แทนได้อยู่ นั่นคืออาจใช้วงเล็บโค้งล้อมรอบเฉพาะส่วนที่ต้องการ

ตัวอย่างเช่น ต้องการเอาพยัญชนะที่อยู่หลัง "เ" แต่ไม่เอา "เ" ด้วย ก็ให้วงเล็บที่อักษรหลัง "เ" ไว้ ทำแบบนี้จะได้ผลลัพธ์ออกมา ๒ ส่วน โดยกลุ่มคำทั้งหมดจะเรียกว่า \0 ส่วนเฉพาะที่อยู่ในวงเล็บจะเรียกว่า \1 เราสามารถเลือกหยิบมาเฉพาะส่วน \1 ได้


แบบนี้ก็จะได้ผลเหมือนตัวอย่างที่แล้ว

ตัวอย่างเช่น เอาพยัญชนะที่อยู่หน้า "า" แต่ไม่เอา "า" ด้วย ก็ให้วงเล็บที่อักษรหน้า "า" ไว้ ทำแบบเดียวกัน


ถ้าใส่วงเล็บหลายตัว ก็จะได้กลุ่มเพิ่มมา กลุ่มที่ ๒ เรียกว่า \2 ส่วนกลุ่มที่ ๓​ นั้นจะเรียกว่า \3 ตามลำดับ


พอจัดกลุ่มก้อนใส่หมายเลขแบบนี้แล้วสามารถเลือกว่าจะหยิบส่วนไหนมาทำอะไรได้ ซึ่งจะกล่าวถึงในหัวข้อต่อๆไป



การนำกลุ่มก้อนมาอ้างอิงเพื่อหาส่วนที่ปรากฏซ้ำ (ใช้ \หมายเลข)

กลุ่มก้อนข้อความที่ล้อมด้วยวงเล็บสามารถนำมาใช้อ้างอิงซ้ำได้ โดยเขียนแทนด้วย \1 และ \2 ลงใน regex ไปเลย

เช่น ต้องการอักษรที่ขนาบกันคั่นด้วยอักษรตัวนึง เราก็สามารถใช้วงเล็บล้อมที่อักษรตัวแรก แล้วใช้ \1 เพื่อแทนตัวขวา


หรือทำอะไรที่ซับซ้อนขึ้นแบบนี้ก็ได้





การใช้ regex เพื่อแทนที่

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

การแทนที่ข้อความนั้นถ้าเป็นในจาวาสคริปต์ใช้ชื่อเมธอดว่า replace ในไพธอนจะใช้ชื่อว่า sub ส่วนในรูบีจะใช่ชื่อว่า gsub ใน php จะเป็น preg_replace

เช่น ถ้าต้องการหาสระ "ะ" หรือ "า" แล้วแทนด้วย a


แต่บางครั้งเราอาจต้องการแทนที่โดยใช้กลุ่มที่เราค้นเจอเป็นตัวอ้างอิงอีกที แบบนี้ก็ให้แทนด้วย \0 หรือ \1

เช่นแทนที่ไม้ยมก "ๆ" ด้วยคำที่นำหน้ามัน


นอกจากนี้เมธอดที่ใช้ในการแทนที่อักษรนั้นมักจะมีความสามารถในการกำหนดฟังก์ชันในการแทน ทำให้สามารถกำหนดรูปแบบการแทนได้อย่างอิสระ อันนี้จะค่อนข้างซับซ้อนและต่างกันไปตามภาษาต่างๆ ดังนั้นจึงไม่ขอลงรายละเอียด



การใช้ regex เพื่อแยกส่วนคำ

อีกงานที่ regex มักถูกใช้คือเวลาที่ต้องการแยกคำออกเป็นส่วนๆ โดยใช้ค้นหาอักษรที่จะใช้เป็นตัวแยก

เมธอดสำหรับแยกในภาษาต่างๆเช่นจาวาสคริปต์, ไพธอน, รูบี จะชื่อว่า split แต่ใน php จะใช้ชื่อว่า preg_split

ตัวอย่าง





การจับกลุ่มก้อนเฉยๆ (ใช้ (?: ))

บางครั้งเราอาจเพียงต้องการใส่วงเล็บเพื่อต้องการจะใช้ให้พิจารณารวมเป็นกลุ่มก้อนเท่านั้น ไม่ได้ต้องการที่จะเน้นหรือว่าให้หมายเลขกับก้อนนั้นเพื่อจะเอามาทำอะไร กรณีแบบนี้ให้เติม ?: ลงไปขึ้นต้นในวงเล็บ

ปกติถ้าใส่วงเล็บไปแบบธรรมดาจะกลายเป็นกลุ่มที่มีหมายเลขกำกับแบบนี้


แต่ถ้าวงเล็บไหนเติม ?: เข้าไปจะไม่นับเป็นกลุ่มที่ติดหมายเลข





การตั้งให้รูปแบบเปลี่ยนไปตามเงื่อนไข (ใช้ (?( ) | ) )

หากมีกลุ่มก้อนอักษรที่ใช้ ? หรือ * อยู่ อักษรกลุ่มนั้นอาจมีตัวตนอยู่หรือไม่ก็ได้ และในบางครั้งเราอาจต้องการให้รูปแบบที่จะตามมานั้นเปลี่ยนไปโดยขึ้นกับว่า กลุ่มนี้มีอยู่หรือเปล่า

สามารถทำได้โดยใส่ส่วนที่ต้องการให้เปลี่ยนตามเงื่อนไขในรูป (?(เลขกลุ่ม)รูปแบบเมื่อมี|รูปแบบเมื่อไม่มี)

ที่อยู่ในวงเล็บด้านในก็คือตัวเลขที่แทนกลุ่มที่ต้องการให้เป็นตัวกำหนดเงื่อนไข หรือถ้าตั้งชื่อไว้ก็ใส่เป็นชื่อได้ แล้วหลังวงเล็บก็เป็นรูปแบบที่ต้องการให้ทำเมื่อมีตัวนั้นอยู่ จากนั้นคั่นด้วย | แล้วก็ตามด้วยรูปแบบเมื่อไม่มี

เพียงแต่ว่ากรณีที่ไม่ใส่รูปแบบเมื่อไม่มี จะละ | ไปเลยก็ได้ ใส่แค่รูปแบบเมื่อมีอย่างเดียวเฉยๆต่อจากวงเล็บ

อย่างไรก็ตาม การเขียนแบบนี้ ใช้ในจาวาสคริปต์ไม่ได้ แต่ใช้ได้ในไพธอน, รูบี และ php

ตัวอย่าง เช่น หากต้องการหาคำที่เป็นพยัญชนะตัวเดียวประกอบกับสระ "เ" หรือ "า" หรือ "ะ" ในจำนวนนี้มีสระ "เ" ที่ต้องวางอยู่ด้านหน้า ที่เหลืออยู่หลัง และบางครั้งก็อาจมีทั้งคู่ เช่น "เอะ" หมายความว่าด้านหน้าพยัญชนะอาจมีสระ "เ" หรือไม่ก็ได้ และด้านหลังจะมี "า" หรือ "ะ" อยู่หรือไม่ก็ได้ ดังนั้นอาจเขียนได้เป็น


แต่พอเขียนแบบนี้จะเห็นว่าแค่ "บ" ตัวเดียวซึ่งไม่มีทั้ง "เ" ตามหน้าและ "าะ" ตามหลังเลยก็เข้าข่ายด้วย ถ้าหากเราไม่ต้องการก็ต้องตั้งเงื่อนไขว่าถ้าไม่มี "เ" นำหน้าจะต้องมี "า" หรือ "ะ" ตัวใดตัวหนึ่งตามหลัง แต่ถ้ามี "เ" นำหน้าแล้ว จะมี "า" และ "ะ" ตามหลังหรือไม่ก็ได้ ซึ่งจะเขียนได้ดังนี้


ในที่นี้ (1) แทนกลุ่ม 1 ซึ่งอาจมีสระ เ หรือไม่มีอะไรอยู่ก็ได้ และการพิจารณาส่วนด้านขวาก็จะต่างกันไป

โครงสร้างการเขียนค่อนข้างซับซ้อนดูแล้วเข้าใจยาก งงได้ง่าย จึงต้องค่อยๆพยายามทำความเข้าใจให้ดี



การกำหนดทางเลือกให้เข้าข่ายหลายตัว (ใช้ |)

บางครั้งเรามีรูปแบบที่ต้องการให้เข้าข่ายอยู่พร้อมกันหลายๆชุด ซึ่งอาจจะมีลักษณะบางอย่างร่วมกันบ้างหรือไม่มีเลย กรณีแบบนี้สามารถเขียนรูปแบบทั้งหมดที่ต้องการแล้วคั่นด้วย |

ตัวอย่าง เมื่อต้องการให้รูปแบบเป็น bcd หรือ efg


หรือเช่นต้องการอักษร ๓ ตัวที่เป็นตัวเลขสลับกับตัวหนังสือ


ปกติถ้าไม่มีวงเล็บโค้ง () อยู่ | จะเป็นการแบ่งกั้นรูปแบบออกเป็น ๒ ส่วน แต่ผลของ | นั้นสามารถแบ่งคั่นได้ด้วยวงเล็บ () หากต้องการของเขตของการแบ่งแค่ตรงไหนก็ใช้ () ล้อมส่วนนั้น

ตัวอย่าง


พื้นฐานการใช้ regex โดยคร่าวๆก็มีอยู่ราวๆนี้ ที่เหลือก็เป็นรายละเอียดปลีกย่อยที่อาจต่างกันไปในแต่ละภาษา ซึ่งมีอะไรให้ต้องเรียนรู้กันต่อไป

ที่สำคัญคือแค่อ่านจบตรงนี้ไปคงยังไม่อาจเข้าใจและจำได้ทั้งหมด ต้องหาโอกาสใช้บ่อยๆจึงจะเร่ิมค่อยๆจำได้ไปเอง

สำหรับคนที่ต้องการใช้ในภาษาไพธอนให้อ่านเพิ่มเติมได้ใน https://phyblas.hinaboshi.com/20160922



อ้างอิง


-----------------------------------------

囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧

ดูสถิติของหน้านี้

หมวดหมู่

-- คอมพิวเตอร์ >> เขียนโปรแกรม >> regex

ไม่อนุญาตให้นำเนื้อหาของบทความไปลงที่อื่นโดยไม่ได้ขออนุญาตโดยเด็ดขาด หากต้องการนำบางส่วนไปลงสามารถทำได้โดยต้องไม่ใช่การก๊อปแปะแต่ให้เปลี่ยนคำพูดเป็นของตัวเอง หรือไม่ก็เขียนในลักษณะการยกข้อความอ้างอิง และไม่ว่ากรณีไหนก็ตาม ต้องให้เครดิตพร้อมใส่ลิงก์ของทุกบทความที่มีการใช้เนื้อหาเสมอ

目录

从日本来的名言
模块
-- numpy
-- matplotlib

-- pandas
-- manim
-- opencv
-- pyqt
-- pytorch
机器学习
-- 神经网络
javascript
蒙古语
语言学
maya
概率论
与日本相关的日记
与中国相关的日记
-- 与北京相关的日记
-- 与香港相关的日记
-- 与澳门相关的日记
与台湾相关的日记
与北欧相关的日记
与其他国家相关的日记
qiita
其他日志

按类别分日志



ติดตามอัปเดตของบล็อกได้ที่แฟนเพจ

  查看日志

  推荐日志

ตัวอักษรกรีกและเปรียบเทียบการใช้งานในภาษากรีกโบราณและกรีกสมัยใหม่
ที่มาของอักษรไทยและความเกี่ยวพันกับอักษรอื่นๆในตระกูลอักษรพราหมี
การสร้างแบบจำลองสามมิติเป็นไฟล์ .obj วิธีการอย่างง่ายที่ไม่ว่าใครก็ลองทำได้ทันที
รวมรายชื่อนักร้องเพลงกวางตุ้ง
ภาษาจีนแบ่งเป็นสำเนียงอะไรบ้าง มีความแตกต่างกันมากแค่ไหน
ทำความเข้าใจระบอบประชาธิปไตยจากประวัติศาสตร์ความเป็นมา
เรียนรู้วิธีการใช้ regular expression (regex)
การใช้ unix shell เบื้องต้น ใน linux และ mac
g ในภาษาญี่ปุ่นออกเสียง "ก" หรือ "ง" กันแน่
ทำความรู้จักกับปัญญาประดิษฐ์และการเรียนรู้ของเครื่อง
ค้นพบระบบดาวเคราะห์ ๘ ดวง เบื้องหลังความสำเร็จคือปัญญาประดิษฐ์ (AI)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ