ใครที่เคยใช้ภาษาซี หรือไพธอน รูบี หรือ php มาก่อน คงจะชินกับการแปลงข้อมูลตัวเลขเป็นสายอักขระโดยใช้
%d
%f
%x
พวกนี้
แต่พอมาใช้จาวาสคริปต์ก็จะต้องพบความจริงที่โหดร้ายที่ว่าจาวาสคริปต์ไม่มีเตรียมฟังก์ชันทำหรับที่จะทำแบบนั้นเอาไว้เลย
ฟังก์ชันนี้แรกเริ่มเดิมทีมาจากภาษาซี โดยที่เวลาใช้ฟังก์ชัน
printf
จะกำหนดรูปแบบแล้วก็ตัวแปรที่จะแทนค่าเข้าไปได้อิสระ เช่น
printf("%02d__%.3f",1,2)
จะแสดงค่า
01__2.000
ออกมา
แต่ถ้าแค่ต้องการแปลงเป็นสายอักขระเฉยๆเก็บไว้ในตัวแปรปกติจะใช้ฟังก์ชันชื่อ
sprintf
แทน
ฟังก์ชัน sprintf ได้ถูกนำมาใส่ใน php เช่นกัน วิธีใช้ก็เหมือนในภาษาซี
http://www.w3school.com.cn/php/func_string_sprintf.asp
ไพธอนและรูบีก็ได้นำความสามารถของฟังก์ชันนี้มาใช้ แต่ปรับให้ใช้กับตัวดำเนินการ
%
แทน
เช่น ถ้าเป็นในไพธอน ปกติเขียนสายอักขระแล้วต่อด้วย
%
แล้วตามด้วยทูเพิลของค่าที่ต้องการป้อนเข้า เช่น แบบนี้
'%06d %.5f'%(112,129.3)
จะได้
'000112 129.30000'
ส่วนในรูบีจะเขียนเป็น
'%06d %.5f'%[112,129.3]
ผลที่ได้ก็เหมือนกัน
ในจาวาสคริปต์ไม่สามารถทำแบบนี้ได้ แต่อย่างไรก็ตาม ยังดีที่ในจาวาสคริปต์เราสามารถเพิ่มเมธอดใหม่เข้าไปให้กับออบเจ็กต์ที่มีอยู่แล้วได้อย่างง่ายดาย
ใช่แล้ว เมื่อไม่มีอยู่ก็สร้างขึ้นมาใหม่ซะเองเลยสิ
ว่าแล้วก็ลองเขียนขึ้นเองดูโดยใช้เรกูลาร์เอ็กซ์เพรชชัน (regex)
เราสามารถใส่เมธอดใหม่ให้คล้ายกับเวลาที่ไพธอนหรือรูบีใช้
%
โดยในที่นี้จะให้เขียนเป็น
'%06d %.5f'.$(112,129.3)
ที่จริงอยากให้ใช้
%
ได้เหมือนกัน แต่ไม่มีวิธีที่จะทำได้ เลยใช้
$
แทน
หากใส่ฟังก์ชันลงไปให้
String.prototype.$
ก็จะสามารถเรียกใช้ได้จากสายอักขระทุกตัว
ต่อไปนี้เป็นโค้ดที่ใช้
String.prototype.$ = function (...a) {
i = -1
return this.replace(
/(%+)(#+)?(\+)?(0)?(\d+|\*)?(\.(\d+))?([bBcdeEfFgGiosuxX])/g,
function (...m) {
/* คำอธิบายตัวแปร
m[1]: กลุ่มเครื่องหมาย % ด้านหน้า
m[2]: กลุ่มเครื่องหมาย # ด้านหน้า ถ้ามี (มีผลกับ b, o, x, X)
m[3]: เครื่องหมาย + ถ้ามี
m[4]: ถ้ามีเลข 0 ให้เติมด้วย 0 ถ้าไม่มีให้เติมด้วยช่องว่าง
m[5]: ส่วนที่บอกว่าจะเติมสายอักขระให้ยาวอย่างต่ำกี่ตัว
m[6]: ส่วนที่บอกว่าจะเอาเลขทศนิยมกี่ตำแหน่ง
m[7]: จำนวนตำแหน่งทศนิยม
m[8]: ตัวกำหนดรูปแบบการเขียน
*/
// เริ่มจากจัดการกับเครื่องหมาย % ด้านหน้า
if (m[1].length % 2 == 0) {
// ถ้าจำนวนของ % เป็นเลขคู่ แค่ลดจำนวน % เหลือครึ่ง
return m[0].replace(/%%/g, "%")
}
// ถ้าจำนวนของ % เป็นเลขคี่ จึงดำเนินการแทนค่า
i++
// ขึ้นต้นด้วย % ที่นำหน้า ถ้ามี แต่เหลือครึ่งเดียว
s = m[1].slice(0, m[1].length / 2)
k = ""
// ใส่เรื่องหมาย + ถ้าระบุ + และค่าเป็นจำนวนบวกและไม่ใช่สายอักขระ
if (m[3] && !"cs".includes(m[8]) && a[i] > 0) {
k = "+"
}
// จำนวนที่จะเติม 0 หรือช่องว่าง
p = 0
// ถ้าใส่เป็น * มาให้ดึงตัวเลขข้างหน้ามาใช้แล้วแปลงค่าตัวถัดไปแทน
if (m[5] == "*") {
p = a[i]
i++ // เลื่อนไปอีกตำแหน่ง
} // ถ้ามีโดยใส่มาเป็นตัวเลขให้ใช้ตัวเลขนั้น
else if (m[5]) {
p = parseInt(m[5])
}
// แปลงข้อมูลตามชนิดที่ใส่มา
// เลขฐาน 2
if ("bB".includes(m[8])) {
// ถ้ามี #
if (m[2]) {
if (m[8] == "B") k += "0B"
else k += "0b"
}
k += a[i].toString(2)
} // โค้ดตัวหนังสือ
else if (m[8] == "c") {
k += String.fromCharCode(a[i])
} // เลขจำนวนเต็ม
else if ("diu".includes(m[8])) {
if (typeof (a[i]) != "number")
throw TypeError // ถ้าไม่ใช่ตัวเลขก็ให้เกิดข้อยกเว้นขึ้น
k += parseInt(a[i])
} // เลขทศนิยมในรูป E
else if ("eE".includes(m[8])) {
if // ถ้าระบุจำนวนตำแหน่งทศนิยม
(m[6]) e = a[i].toExponential(m[7])
else // ถ้าไม่ระบุจำนวนตำแหน่งทศนิยมก็ให้เป็น 6
e = a[i].toExponential(6)
// กรณีที่เลขหลัง +- มีหลักเดียว เติม 0 อีกตัว
if ("+-".includes(e[e.length - 2]))
e = e.replace("e+", "e+0").replace("e-", "e-0")
if (m[8] == "E") // กรณี E เป็นตัวพิมพ์ใหญ่
e = e.replace("e", "E")
k += e
} // เลขทศนิยม
else if ("fF".includes(m[8])) {
if (m[6]) // ถ้าระบุจำนวนตำแหน่งทศนิยม
k += a[i].toFixed(m[7])
else // ถ้าไม่ระบุจำนวนตำแหน่งทศนิยมก็ให้เป็น 6
k += a[i].toFixed(6)
} // เลขที่ปรับรูปตามสมควร
else if ("gG".includes(m[8])) {
if (typeof (a[i]) != "number")
throw TypeError // ถ้าไม่ใช่ตัวเลขก็ให้เกิดข้อยกเว้นขึ้น
if (a[i] >= 0.0001 && a[i] < 1000000) // กรณีเลขไม่มากหรือน้อยเกินไป
k += a[i]
else { // กรณีเลขมากหรือน้อยจนถึงระดับหนึ่ง
e = a[i].toExponential()
// กรณีที่เลขหลัง +- มีหลักเดียว เติม 0 อีกตัว
if ("+-".includes(e[e.length - 2]))
e = e.replace("e+", "e+0").replace("e-", "e-0")
if (m[8] == "G") // กรณี G เป็นตัวพิมพ์ใหญ่
e = e.replace("e", "E")
k += e
}
} // เลขฐาน 8
else if (m[8] == "o") {
if (m[2]) k += '0o' // ถ้ามี #
k += a[i].toString(8)
} // เลขฐาน 16 โดยใช้ตัวพิมพ์เล็ก
else if (m[8] == "x") {
if (m[2]) k += '0x' // ถ้ามี #
k += a[i].toString(16)
} // เลขฐาน 16 โดยใช้ตัวพิมพ์ใหญ่
else if (m[8] == "X") {
if (m[2]) k += '0X' // ถ้ามี #
k += a[i].toString(16).toUpperCase()
} // สายอักขระธรรมดา
else {
k += a[i]
}
// ใส่ 0 หรือช่องว่าง
_0 = ""
if (p) {
n0 = p - k.length
while (n0 > 0) {
n0--
// ใส่ 0 ถ้าระบุ 0 และไม่ใช่สายอักขระ
if (!"cs".includes(m[8]) && m[4] == "0")
_0 += "0"
// ใส่ช่องว่าง
else
_0 += " "
}
}
// ถ้าเลือกเติม 0 และมีเครื่องหมาย +- อยู่ให้แทรกไว้หลัง +-
if ("+-".includes(k[0]) && _0[0] == "0")
s += k[0] + _0 + k.slice(1)
else // ถ้าไม่มี +- ก็เติม 0 หรือช่องว่างไปเลย
s += _0 + k
return s
}
)
}
// ทดลองใช้
ss = `%+04d
%+4d
%+3d
%%d
%%%%02d
%%%d
%8.3f
%9.3E
%#03o
%#05x
%+07X
%#b
%c
%+7s
%7G
%g
%0*d
%0*.2f`
console.log(ss.$(1, 1.1, -2, 3.1, 4.2, 0.0053, 9, 1965, 1965, 61, 100, "ห", 0.02, 2e-5, 5, 1, 7, 1.11))
ผลที่ได้
+001
+1
-2
%d
%%02d
%3
4.200
5.300E-03
0o11
0x7ad
+0007AD
0b111101
d
ห
0.02
2e-05
00001
0001.11
ที่จริงผลบางส่วนอาจไม่ได้เหมือนกับ
sprintf
ของจริงทั้งหมด แต่ก็ถือว่าใช้แทนได้ตามวัตถุประสงค์ที่ต้องการ
ข้างล่างนี้คือพื้นที่ทดสอบ
sprintf
โดยใช้โค้ดข้างต้นนี้ในการแปลงอักษร ลองทดสอบใช้กันดูได้