ข้อความที่ตาเห็นราว 70 ตัว ผู้ให้บริการนับได้ 79
อย่างตอนที่เราทำระบบส่ง SMS แจ้งคำสั่งซื้อให้ลูกค้า ทางลูกค้าส่งตัวอย่างข้อความมาจากหน้าจอของผู้ให้บริการ SMS ข้อความแรกอ่านด้วยตาแล้วดูราว 70 ตัว แต่ผู้ให้บริการนับได้ 79 ตัว และคิดเป็น 2 เครดิต ลูกค้าจึงอยากได้หน้าจอที่ทีมพิมพ์ข้อความแล้วเห็นเลยว่าจะใช้กี่เครดิต ก่อนกดส่งจริง
พอต้องทำหน้าจอนี้ เราก็ต้องนับให้ตรงกับที่ผู้ให้บริการนับ ซึ่งไม่ได้นับตัวอักษรแบบที่ตาเราเห็น ต้องเข้าใจก่อนว่า SMS เก็บข้อความยังไง
หนึ่ง SMS ได้ 140 ไบต์ ภาษาอังกฤษจึงได้ 160 ตัว ภาษาไทยได้ 70
SMS หนึ่งข้อความบรรจุข้อมูลได้ 140 ไบต์ จะได้กี่ตัวอักษรขึ้นกับว่าเข้ารหัสข้อความแบบไหน มีสองแบบที่ใช้กัน
- GSM-7 ใช้ 7 บิตต่อตัว 140 ไบต์คือ 1,120 บิต จึงได้ 160 ตัว แต่มีตัวอักษรให้ใช้แค่ชุดเดียวตามมาตรฐาน GSM 03.38 คือภาษาอังกฤษ ตัวเลข สัญลักษณ์พื้นฐาน และอักษรยุโรปบางตัว
- UCS-2 ใช้ 2 ไบต์ต่อตัว ได้ 70 ตัว แต่ใส่อักษรได้แทบทุกภาษา รวมทั้งภาษาไทยและอีโมจิ
ระบบเลือกการเข้ารหัสจากตัวอักษรทั้งข้อความ ถ้ามีตัวที่ GSM-7 ไม่รู้จักแม้แค่ตัวเดียว ทั้งข้อความจะกลายเป็น UCS-2 ข้อความภาษาไทยจึงได้ 70 ตัวเสมอ ต่อให้ส่วนใหญ่เป็นตัวเลขหรือภาษาอังกฤษก็ตาม
ข้อความภาษาอังกฤษล้วนก็โดนได้เหมือนกัน ตัวที่เจอบ่อยคือเครื่องหมาย ’ แบบโค้ง ที่ Word หรือคีย์บอร์ดมือถือใส่ให้แทน ' ตรง ๆ ข้อความ We’ll text you จึงเหลือข้อความละ 70 ตัว แทนที่จะได้ 160
| ข้อความ | การเข้ารหัส | ได้ต่อข้อความ |
|---|---|---|
Your OTP is 482913 |
GSM-7 | 160 |
We’ll text you soon |
UCS-2 เพราะมี ’ | 70 |
รหัส OTP คือ 482913 |
UCS-2 เพราะมีภาษาไทย | 70 |
สระและวรรณยุกต์นับแยกตัว
ภาษาไทยเก็บสระบน สระล่าง และวรรณยุกต์เป็นตัวอักษรของตัวเอง วางซ้อนบนพยัญชนะตอนแสดงผล ตาเราเห็นช่องเดียว แต่ SMS นับทุกตัว
ที่ตาเห็นช่องเดียว นับ 3 ตัว คือ ท ี ่น้ำนับ 3 ตัว คือ น ้ ำกี่บาทนับ 6 ตัว
ประโยคที่มีวรรณยุกต์เยอะ ๆ จึงยาวกว่าที่เห็นได้เป็นสิบตัว ข้อความ 79 ตัวที่เล่าไว้ข้างบนก็มาจากเรื่องนี้ ตาเห็นราว 70 ตัว แต่สระกับวรรณยุกต์ทำให้ล้นไปเป็นข้อความที่สอง
อีโมจิส่วนใหญ่นับเป็น 2
UCS-2 เก็บอักษรได้ราว 65,000 ตัวแรกของ Unicode อีโมจิส่วนใหญ่อยู่เกินช่วงนั้น เลยต้องเก็บเป็นสองหน่วยต่อกัน เรียกว่า surrogate pair ผลคือ
| อีโมจิ | นับเป็น | เพราะ |
|---|---|---|
| ✅ ✨ ❤ | 1 | อยู่ในช่วงแรกของ Unicode |
| 📦 🚚 😀 | 2 | เป็น surrogate pair |
| 🙏🏻 | 4 | มือไหว้ 2 กับสีผิวอีก 2 |
| 🇹🇭 | 4 | ธงคืออักษรสองตัวต่อกัน ตัวละ 2 |
| 👨👩👧 | 8 | คนสามคน คนละ 2 กับตัวเชื่อมที่มองไม่เห็นอีก 2 ตัว |
อีโมจิครอบครัวหนึ่งตัวจึงกินที่ 8 ตัว เกินหนึ่งในสิบของข้อความไทยไปแล้ว ถ้าข้อความใกล้ 70 ตัว อีโมจิตัวเดียวก็พอทำให้กลายเป็น 2 ข้อความ
เกิน 70 ตัวแล้ว เหลือส่วนละ 67
ข้อความที่ยาวกว่าหนึ่ง SMS ต้องแบ่งส่งหลายข้อความ แล้วให้โทรศัพท์ปลายทางต่อกลับเป็นข้อความเดียว ทุกส่วนจึงต้องมีส่วนหัว เรียกว่า UDH บอกว่าเป็นส่วนที่เท่าไรจากทั้งหมดกี่ส่วน ส่วนหัวนี้ยาว 6 ไบต์ เหลือที่ใส่ข้อความ 134 ไบต์
- UCS-2 ได้ส่วนละ 67 ตัว (134 ÷ 2)
- GSM-7 ได้ส่วนละ 153 ตัว เพราะส่วนหัวกินที่ไป 7 ตัว
ตัวที่ 71 จึงไม่ได้เพิ่มมาแค่ตัวเดียว ทั้งข้อความจะแบ่งใหม่เป็นส่วนละ 67
| ภาษาไทย (UCS-2) | จำนวน SMS | ภาษาอังกฤษ (GSM-7) | จำนวน SMS |
|---|---|---|---|
| 1–70 ตัว | 1 | 1–160 ตัว | 1 |
| 71–134 ตัว | 2 | 161–306 ตัว | 2 |
| 135–201 ตัว | 3 | 307–459 ตัว | 3 |
ผู้ให้บริการคิดเครดิตตามจำนวน SMS คูณจำนวนเบอร์ผู้รับ ข้อความ 2 ส่วนส่งให้ลูกค้า 1,000 คน คือ 2,000 เครดิต ข้อความที่ตัดให้สั้นลงได้สองสามคำจึงประหยัดได้จริงเวลาส่งทีละมาก ๆ
ฝั่ง GSM-7 มีอีกเรื่องหนึ่ง ตัว ^ { } [ ] ~ \ | € อยู่ในตารางเสริมของ GSM-7 ต้องมีรหัสนำหน้าอีกหนึ่งตัว จึงนับตัวละ 2
นับในโค้ดให้ตรงกับที่ผู้ให้บริการนับ
ข้อความไทยนับแบบ UCS-2 คือนับเป็นหน่วย UTF-16 ฝั่ง JavaScript ทำง่าย เพราะ string.length นับแบบนี้อยู่แล้ว ส่วน PHP ต้องระวัง mb_strlen() นับเป็น code point อีโมจิ 📦 จึงได้ 1 แทนที่จะเป็น 2 ต้องแปลงเป็น UTF-16 ก่อนแล้วค่อยนับไบต์
const units = message.length; // 'ที่ 📦🙏🏻'.length === 10
const credits = units === 0 ? 0 : units <= 70 ? 1 : Math.ceil(units / 67);
// mb_strlen('ที่ 📦🙏🏻') ได้ 7 ซึ่งน้อยกว่าที่ผู้ให้บริการนับ
$units = intdiv(strlen(mb_convert_encoding($message, 'UTF-16BE', 'UTF-8')), 2); // 10
$credits = match (true) {
$units === 0 => 0,
$units <= 70 => 1,
default => (int) ceil($units / 67),
};
โค้ดนี้พอสำหรับระบบที่ข้อความมีภาษาไทยเสมอ อย่างระบบที่เราทำ ข้อความแจ้งคำสั่งซื้อทุกแบบเป็นภาษาไทย เราจึงนับแบบ UCS-2 อย่างเดียว ถ้าระบบส่งภาษาอังกฤษล้วนด้วย ต้องเช็กก่อนว่าทุกตัวอยู่ในชุด GSM-7 ไหม แล้วค่อยเลือกว่าจะนับแบบ 160/153 หรือ 70/67 และนับตัวในตารางเสริมเป็น 2
ถ้าต้องให้ทั้งหน้าจอและฝั่งเซิร์ฟเวอร์นับ ให้ใช้สูตรเดียวกันทั้งสองฝั่ง และมี test คุมไว้ด้วยข้อความตัวอย่างที่มีวรรณยุกต์และอีโมจิ เพราะสองที่นี้แยกกันเพี้ยนได้ง่ายเวลามีคนแก้ฝั่งเดียว
ให้ทีมลองพิมพ์ก่อนส่งจริง
หน้าจอที่เราทำให้ลูกค้าแสดงจำนวนตัวอักษรที่นับจริง จำนวนเครดิต และเครดิตที่เหลือในบัญชี พร้อมตารางบอกว่าอีโมจิแต่ละตัวในข้อความนับเป็นเท่าไร ทีมที่เขียนข้อความเห็นทันทีว่าตัวไหนทำให้ข้อความล้นไปอีกส่วน แล้วตัดสินใจเองได้ว่าจะเก็บไว้หรือเปลี่ยนคำ
เราทำเครื่องมือแบบเดียวกันไว้ที่นับตัวอักษร SMS ภาษาไทย พิมพ์หรือวางข้อความแล้วจะเห็นการเข้ารหัส จำนวนตัวที่นับ จำนวน SMS ตัวที่ยังเหลือในส่วนนั้น และข้อความแบ่งตรงไหน ถ้ากำลังเขียนข้อความ SMS ให้ระบบไหนอยู่ ลองวางดูก่อนส่งได้ครับ






