คำแนะนำเกี่ยวกับการใช้ประเภทข้อมูล “ตัวหนังสือ” และ “ข้อความ” ใน C++

เรื่องวันนี้ก็เป็นอีกเรื่องที่พอดีแนะน้องฝึกงานไป เห็นว่าน่าสนใจเลยเอามาฝากกัน

Data type

เรื่อง data type ของ C++ นั้นเป็นเรื่องที่ค่อนข้างกว้าง เพราะว่า C++ นั้นผู้ใช้สามารถกำหนด data type ได้เอง และก็มีหลายประเภทอยู่เหมือนกัน วันนี้จะพูดแค่เฉพาะตัวพื้นฐานเลยก็แล้วกันนะครับ

ในปัจจุบันมี data type อยู่สองตัวที่ใช้กับตัวหนังสือโดยเฉพาะ ก็คือ char (character) กับ wchar_t (wchar_t) ความแตกต่างกันระหว่างสองตัวนี้ก็คือขนาด โดย char จะมีขนา 1 byte (8 bit) เสมอ ในขณะที่ wchar_t จะมีขนาดแปรผันไปตามแพลตฟอร์ม (แต่จะมีขนาดขั้นต่ำที่ 2 byte (16 bit)) ความแตกต่างนี้ทำให้จำนวนค่าที่เป็นไปได้นั้นก็ต่างกันด้วย โดย wchar_t จะมีค่าที่เป็นไปได้มากกว่า char (ซึ่งรองรับได้แค่ 256 ค่า) มาก

ตอนที่ใส่ค่าลงไปก็จะต่างกันนิดนึง ตรงที่ wchar_t เวลาจะใส่ค่าคงที่นี่ต้องมีตัว  L ข้างหน้าด้วยเพื่อที่จะบอกว่าเป็น wide character นะ

char a = 'a';
wchar_t b = L'b';

พอเราเอา data type นี้ไปใช้งานในรูปแบบของสตริง ก็จะมีคลาสสองคลาสที่มารองรับตรงนี้ ก็คือ std::string และ std::wstring ทั้งสองคลาสนี้มีรูปแบบที่เหมือนกันทุกประการ ต่างกันเพียงแค่ data type ของตัวอักษรที่ใช้ข้างใน

วิธีใช้ก็คล้าย ๆ กัน ถ้าจะใส่ค่าลงไปในสตริงเลย ตัว std::wstring เราก็ต้องใส่ L  ข้างหน้าด้วยเหมือนกันครับ

std::string narrow_string = "narrow";
std::string wide_string = L"wide";

อันที่จริงทั้งสองคลาสเป็นแค่ typedef ของ template class ที่ชื่อว่า std::basic_string นั่นเอง

typedef std::basic_string std::string;
typedef std::basic_string std::wstring;

ในโปรแกรมโปรแกรมนึงเนี่ย ไม่ควรใช้ char และ wchar_t (รวมทั้ง std::string และ std::wstring ด้วย) ผสมกันโดยไม่จำเป็น เหตุผลก็คือเรื่องความชัดเจนนั่นล่ะครับ คือบางทีเราจะงงว่าตกลงต้องใส่ L ข้างหน้าค่า Rvalue หรือไม่ต้องกันแน่

อันนี้รวมถึงบรรดาคลาสที่เกี่ยวข้องทั้งหมดนะครับ อย่างถ้าจะใช้ wchar_t ก็ควรใช้คู่กับ std::wcout, std::wfstream และอื่น ๆ ด้วย

เรื่องมึน ๆ ของ WIN32

สำหรับคนที่เขียนโปรแกรมบน WIN32 หรือ MFC จะมีเรื่องปวดหัวอีกอย่างนึงครับ ก็คือ data type ที่มีชื่อว่า TCHAR เจ้าเนี่ยอาจจะเป็น char หรือ wchar_t ก็ได้ แล้วแต่ว่ามี macroo UNICODE ประกาศเอาไว้อยู่หรือเปล่า

#ifdef UNICODE
typedef wchar_t TCHAR;
#else
typedef char TCHAR;
#endif

คือทาง  Microsoft คงจะหวังดีอยากเห็นโปรแกรมเมอร์เขียนโค๊ดทีเดียวแต่คอมไพล์ออกมาเป็น ASCII ก็ได้ หรือ Unicode ก็ได้ ปัญหาก็คือ ไอ้เจ้า data type เนี่ยไม่ได้เกี่ยวข้อง (กันตรง ๆ ) กับ character encoding น่ะสิ

จะใช้ char เก็บค่าสตริง Unicode ก็ทำได้นะ ใช้ encoding แบบ UTF-8 ไงครับ

ที่แย่กว่านั้นคือทีนี้การเขียนโค๊ดต้องมีวินัยมากขึ้น จะใช้ฟังก์ชั่นชุดที่ไม่รองรับ TCHAR มาใช้ไม่ได้ ซึ่งที่จริงการเขียนให้ผิดมันก็ง่ายมากทีเดียว

สมมติง่าย ๆ ว่าผมเขียนโค๊ดแบบนี้

const TCHAR* pText = TEXT("hello world");
size_t length = strlen(pText);

จะคอมไพล์ผ่านบนโปรเจคที่ตั้งค่าเป็น  ASCII (ซึ่งไม่ใช้มาโคร UNICODE) แต่พอเปิด Unicode ปุ๊บจะเจ๊งทันที

คำตอบที่ถูกคือต้องใช้ _tstrlen() ซึ่ง พอมี _ อยู่ข้างหน้าชื่อฟังก์ชั่นจะให้ความรู้สึกว่ามันเป็น internal API ซึ่งมันก็จะดูไม่ค่อยปลอดภัย แต่ตัวนี้ใช้ได้ครับไม่มีปัญหา

การใช้ macro UNICODE เนี่ยมันดูศตวรรษที่ 20 มากครับ ในเมื่อเราอยู่ในศตวรรษที่ 21 กันแล้วก็ไม่ควรจะใช้ครับ (ฮา) ผมคิดว่าการใช้เพียง char หรือ wchar_t ตัวใดตัวนึงไปเลยเป็นวิธีที่ดีกว่า

จะใช้คลาส  string หรือใช้ char*  ดี ?

เป็นอีกเรื่องนึงที่เห็นจากการอ่านโค๊ดของน้องฝึกงานครับ คือ คุณเธอใช้ผสมกันระหว่าง string/wstring กับ char*/wchar_t*  ปนกันไปทั่วทุกภูมิภาค ที่จริงการใช้ pointer เป็นวิถีของภาษา C ซึ่ง C++ ก็รองรับเนื่องจากเหตุผลของการเข้ากันได้กับภาษา  C และการใช้ pointer ก็ใช้พื้นที่บนหน่วยความจำได้อย่างมีประสิทธิภาพมากกว่า

แต่ถ้าเลี่ยงได้ก็เลี่ยงครับ

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

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

ส่วนถ้าจำเป็นจะต้องเอาไปใช้กับฟังก์ชั่นที่รับพารามิเตอร์เป็น  char*/wchar_t* ก็แค่เรียกเมธอด  c_str()  ขึ้นมาก็จบละ

แล้วจะใช้  char หรือ wchar_t ?

ที่จริงมีจุดที่ต้องคำนึงถึงอยู่ประมาณนึงกับการใช้ char หรือ wchar_t แต่สำคัญจริง ๆ คือขนาดของ wchar_t  นั้นไม่ได้มีขนาด 16 บิทเสมอไป (ขึ้นอยู่กับ  platform และ compiler) ในขณะที่ char เองก็ไม่จำเป็นว่า 1  char = 1 ตัวอักษร (ขึ้นอยู่กับ  character encoding)

wchar_t  นั้นดูจะใช้งานง่ายกว่า char ในแง่ของการรองรับ encoding แบบ unicode ครับ แต่ว่า char เองก็มีจุดเด่นที่มันใช้พื้นที่ในหน่วยความจำน้อยกว่า

ในทางปฎิบัติ ถ้าเราเขียนโปรแกรมบน  Windows เรามักจะใช้  wchar_t หรือพวก  wide character แต่บน Unix เนี่ย char จะได้รับความนิยมมากกว่า

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

ใส่ความเห็น

อีเมลของคุณจะไม่แสดงให้คนอื่นเห็น ช่องข้อมูลจำเป็นถูกทำเครื่องหมาย *

This site uses Akismet to reduce spam. Learn how your comment data is processed.