การจัดการข้อความและข้อมูลที่มีโครงสร้าง
โค้ด Ruby ใช้ร่วมจากโฟลเดอร์ en/ ของต้นฉบับ เพื่อให้สองภาษาผูกกับชุดทดสอบเดียวกัน
th/topic_04_strings_symbols_ranges_regex
ภาพรวม
ทำไมหัวข้อนี้จึงสำคัญ
งานซอฟต์แวร์จริงจำนวนมากคือการทำงานกับข้อความ เช่น ตรวจข้อมูลนำเข้า ปรับรูปแบบ string เปลี่ยน label ให้เป็น URL และจัดการ key ที่มีโครงสร้าง Ruby เด่นในงานแบบนี้ เพราะ strings และ regular expressions เป็นเครื่องมือที่ทั้งใช้งานจริงและเข้าถึงได้
สิ่งที่ผมอยากให้คุณทำได้เมื่อจบหัวข้อนี้
เมื่อจบหัวข้อนี้ คุณควรจะ:
- อธิบายบทบาทของ symbols ในฐานะตัวระบุที่เบาและชัดได้
- ใช้ ranges เพื่อแทนลำดับที่รวมปลายได้อย่างชัดเจน
- ใช้ regular expressions กับงานตรวจสอบที่ตรงไปตรงมาได้
- ปรับข้อความให้อยู่ในรูปที่ปลอดภัยสำหรับ URL แบบง่าย ๆ ได้
- อธิบายเส้นแบ่งระหว่าง regex ที่มีประโยชน์กับ regex ที่ซับซ้อนเกินไปได้
จุดที่ผมใช้ดูความเข้าใจ
ผมอยากให้คุณแก้ปัญหาเรื่องข้อความทั่วไปได้อย่างชัดเจน โดยไม่ทำให้โค้ดกลายเป็นเวทมนตร์ ของ pattern ที่อ่านไม่ออก
โน้ตสั้น
Ruby มักให้ความรู้สึกดีเวลาโค้ดต้องจัดรูป ปรับแต่ง และตรวจสอบ strings แต่นั่นไม่ได้ หมายความว่าปัญหาทุกอย่างเกี่ยวกับข้อความควรถูกทำให้ฉลาดเกินจำเป็น เป้าหมายของหัวข้อ นี้คือการจัดการ string แบบใช้งานได้จริง และยังบอกเจตนาได้ชัด
สิ่งที่ผมอยากให้คุณสังเกต:
- ranges สื่อช่วงที่รวมปลายได้เป็นธรรมชาติ
- regex ช่วยตรวจสอบรูปแบบที่พบได้บ่อยอย่างกระชับ
- การ normalize ข้อความเพียงเล็กน้อยก็ช่วยงานสายเว็บได้มาก
- symbols มักทำให้ hashes ดูสะอาดขึ้น เมื่อ key เป็นตัวระบุที่คงที่
จุดที่ Ruby ทำได้ดีในหัวข้อนี้:
- การจัดการข้อความทีละขั้นแบบง่าย ๆ มักอ่านได้เป็นธรรมชาติ
- Ruby มี regex อยู่ในตัวภาษาอยู่แล้ว
- transformation สั้น ๆ ก็ยังสื่อจุดประสงค์ทางธุรกิจได้ชัด
จุดที่ต้องระวังในหัวข้อนี้:
- regex จะกลายเป็นโค้ดที่เขียนเสร็จแล้วอ่านไม่ออก ถ้าใช้เกินพอดี
- กฎสำหรับจัดระเบียบข้อความต้องมีขอบเขตชัด ไม่อย่างนั้นจะเริ่มไม่สม่ำเสมอ
คำถามชวนคิด:
- เมื่อไร regex เป็นคำตอบที่ชัดเจน และเมื่อไรกลายเป็นภาระในการดูแลรักษา
ตัวอย่างแบบลงมือดู
Example 1: ตรวจข้อมูลนำเข้าที่พบได้บ่อย
การตรวจรูปแบบอีเมลเป็นตัวอย่างสอนที่ดี เพราะทีมแทบทุกทีมต้องเจองานแบบนี้อยู่แล้ว แต่ ในแบบฝึกหัดของคอร์ส เราจะตั้งใจกำหนดกฎให้พอดี ไม่พยายามไล่ความสมบูรณ์ทุกกรณี
บทเรียนของตัวอย่างนี้ไม่ใช่ "สร้างระบบตรวจอีเมลที่สมบูรณ์แบบ" แต่คือ "ใช้ regex กับ กฎเฉพาะจุดที่สมเหตุสมผล และรู้ขอบเขตของมัน"
Example 2: สร้าง URL slug
การทำ slug เป็นตัวอย่างที่พบได้จริงใน blogs, CMS systems และ admin tools
slugifier.slugify("Ruby for Java Developers")
# => "ruby-for-java-developers"ruby
เหตุผลที่ตัวอย่างนี้มีประโยชน์:
- คุณมองเห็นการแปลงข้อมูลได้ทีละขั้น
- ผลลัพธ์มีความหมายจริงใน web applications
- โค้ดนี้แสดงจุดแข็งของ Ruby ในการจัดการข้อความแบบต่อเป็นขั้นสั้น ๆ
โพยสั้น
Range
(1..5).to_aruby
regex สำหรับรูปแบบอีเมล
EMAIL_REGEX = /\A[^\s@]+@[^\s@]+\.[^\s@]+\z/ruby
ลำดับแปลงข้อความสำหรับทำ slug
text.downcase.strip
.gsub(/\s+/, "-")
.gsub(/[^a-z0-9-]/, "")ruby
จุดที่ควรจำ
\Aและ\zใช้ยึดทั้ง string..คือ range แบบรวมปลาย- ถ้าเป็น validation ทั่วไป ให้เลือก regex ที่อ่านได้ก่อน
คู่มือการเรียน
จุดประสงค์ของหัวข้อนี้
หัวข้อนี้อยากให้คุณเห็นว่า Ruby จัดการงานที่เกี่ยวกับข้อความได้สะอาดและใช้งานจริงได้ โดยไม่ต้องโชว์ความซับซ้อนของ regex เกินจำเป็น
ลำดับที่ผมแนะนำ
- อ่าน
overview.md - อ่าน
shortnote.mdแล้วสังเกตว่าตรงไหน regex ควรใช้แค่พอดี - อ่าน
worked_examples.md - เปิด
cheatsheet.mdไว้ตอนอ่านexample.rb - ทำแบบฝึกหัดเรื่อง range
- ทำแบบฝึกหัดเรื่อง slug แล้วอธิบายแต่ละขั้นของการแปลงข้อมูล
สิ่งที่ผมอยากให้คุณสังเกต
- การเขียน range กระชับแต่ยังอ่านได้
- regex เหมาะกับกฎตรวจสอบสั้น ๆ ที่อยู่เฉพาะจุด
- การแปลงข้อมูลทีละขั้นที่ดีควรอธิบายได้ทีละบรรทัด
คำถามที่อยากให้คุณพกไว้
- ทำไมตัวอย่าง slug ถึงสอนได้ดีกว่าแบบฝึกหัด string ที่สมมติขึ้นลอย ๆ
- อะไรจะทำให้วิธีใช้ regex นี้เริ่มเปราะเกินไปสำหรับงาน production
- เมื่อไร key แบบ symbol เหมาะกว่าการใช้ string อิสระ
Source Files and Tests
โค้ด Ruby ใช้ร่วมจากโฟลเดอร์ en/ ของต้นฉบับ เพื่อให้สองภาษาผูกกับชุดทดสอบเดียวกัน
# EXAMPLE CODE
# Topic: topic_04_strings_symbols_ranges_regex
#
# Purpose:
# - This file demonstrates reference implementation for the concept.
# - It should pass tests from the beginning.
# - Read and understand it before solving exercises.
class Validator
EMAIL_REGEX = /\A[^\s@]+@[^\s@]+\.[^\s@]+\z/
def valid_email?(value)
!!(value =~ EMAIL_REGEX)
end
end
Ruby course source
# STUDENT TASK (BASIC)
# Topic: topic_04_strings_symbols_ranges_regex
#
# What to do:
# - Implement or improve the class/methods in this file.
# - Read tests in tests/topic_04_strings_symbols_ranges_regex_spec.rb under the "basic exercise" examples.
# - Make tests pass without breaking the example/advanced sections.
#
# Expected outcome:
# - You can run this topic tests and see all examples green after implementation.
class RangeBuilder
def inclusive(a, b)
(a..b).to_a
end
end
Ruby course source
# STUDENT TASK (ADVANCED)
# Topic: topic_04_strings_symbols_ranges_regex
#
# Academic purpose:
# - Practice a realistic text-normalization task rather than isolated string syntax.
# - Learn how Ruby can express a multi-step text transformation compactly and readably.
#
# Real-world use case:
# - Slugs appear in blog platforms, CMS tools, documentation systems, and admin panels.
# - Teams use them to convert human-readable titles into URL-friendly identifiers.
# - This is exactly the sort of small but common feature where Ruby feels elegant.
#
# Why Ruby is beautiful here:
# - String methods chain naturally.
# - Each transformation step reflects a concrete formatting decision.
# - The code is short enough to scan but still close to the product need.
#
# What to do:
# - Complete the challenge behavior requested by the guide.
# - Be able to explain what each `gsub` removes or reshapes.
# - Use tests in tests/topic_04_strings_symbols_ranges_regex_spec.rb under the "advanced exercise" examples.
#
# Expected outcome:
# - Advanced tests pass and you can connect the implementation to a web-facing use case.
class Slugifier
def slugify(text)
text.downcase.strip.gsub(/\s+/, "-").gsub(/[^a-z0-9-]/, "")
end
end
Ruby course source
# ANSWER KEY (BASIC)
# Topic: topic_04_strings_symbols_ranges_regex
#
# Solution idea:
# - Ruby ranges are inclusive with `..`.
# - Convert the range to an array because the tests want the concrete sequence.
class RangeBuilder
def inclusive(a, b)
(a..b).to_a
end
end
Ruby course source
# ANSWER KEY (ADVANCED)
# Topic: topic_04_strings_symbols_ranges_regex
#
# Solution idea:
# - Normalize case first.
# - Remove surrounding whitespace.
# - Collapse internal whitespace into hyphens.
# - Strip any remaining characters that are not slug-safe for this exercise.
class Slugifier
def slugify(text)
text.downcase.strip.gsub(/\s+/, "-").gsub(/[^a-z0-9-]/, "")
end
end
Ruby course source
# This spec is your learning companion for topic_04_strings_symbols_ranges_regex.
#
# How to use this file:
# 1) Run tests and observe failures/successes.
# 2) Keep EXAMPLE specs green from the beginning.
# 3) Implement BASIC exercise until BASIC specs pass.
# 4) Implement ADVANCED exercise and pass edge cases.
#
# Expected final result:
# - All examples in this file pass.
# - You understand both the concept and the implementation tradeoffs.
require_relative "../example"
require_relative "../basic_exercise"
require_relative "../adv_exercise"
RSpec.describe "topic_04_strings_symbols_ranges_regex" do
describe "EXAMPLE purpose: understand the reference implementation" do
it "validates email formats" do
v = Validator.new
expect(v.valid_email?("a@b.com")).to eq(true)
expect(v.valid_email?("x y@z.com")).to eq(false)
end
end
describe "BASIC EXERCISE purpose: implement the comparable task" do
it "creates inclusive ranges" do
expect(RangeBuilder.new.inclusive(1, 3)).to eq([1, 2, 3])
end
end
describe "ADVANCED EXERCISE purpose: solve challenge and edge cases" do
it "slugifies text" do
expect(Slugifier.new.slugify(" Hello Ruby 3.4! ")).to eq("hello-ruby-34")
end
end
end
Ruby course source
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
exec "${ROOT_DIR}/run_tests.sh" "$(basename "${SCRIPT_DIR}")"
Ruby course source
Study Prompts
อ่าน test ก่อน แล้วบอกให้ได้ว่าพฤติกรรมใดเป็น example, basic exercise และ advanced exercise
ลองทำแบบฝึกหัดก่อนเปิด answer files แล้วจดว่าคำตอบต่างจากวิธีคิดแรกของคุณตรงไหน