Xuất tiếng việt ra màn hình console bị lỗi

Khi làm việc với console trên Windows sẽ gặp vấn đề trong việc hiển thị tiếng Việt (hoặc Unicode nói chung) cũng như các ký tự đặc trưng đúng như mong muốn.

Điều này do vấn đề về code page trên màn hình console không tương thích và cần thay đổi code page của màn hình console sang code page có thể hiển thị tiếng Việt.

Code page là gì?

Code page là một bảng dùng để tham khảo một tập ký tự, ví dụ:

  • 1258: code page của bảng tham khảo ký tự tiếng Việt.
  • 10001: code page của bảng tham khảo ký tự tiếng Nhật.
  • 10021: code page của bảng tham khảo ký tự tiếng Thái.
  • ...

Đặt vấn đề

Không thể hiển thị tiếng Việt trong console

Theo mặc định từ đó các phần mềm sẽ dựa trên code page hiện tại của nó để hiển thị các ký tự phù hợp, Command Prompt là một trong những ứng dụng đó. Tuy nhiên theo mặc định thì Command Prompt có thể kích hoạt không đúng code page mong muốn nên không hiển thị được tiếng Việt.

Ở phần 1, mình đã giới thiệu với các bạn về thư viện

BOOL WINAPI SetConsoleTextAttribute(
  _In_ HANDLE hConsoleOutput,
  _In_ WORD   wAttributes
);

1 và các hàm định dạng màn hình console. Trong bài viết này, mình sẽ nói về các hàm định dạng nội dung trong Console.

Nếu các bạn đã xem rồi thì chúng ta bắt đầu nào!

Hiển thị tiếng Việt và các ký tự đặc biệt

Console mặc định để Code page là 437 (OEM – United States), nó không hỗ trợ các ký tự đặc biệt cũng như tiếng Việt, nên nếu bạn hiển thị một chuỗi "Xin chào mọi người!" chẳng hạn, chắc chắn, nó sẽ bị lỗi font như hình dưới:

Xuất tiếng việt ra màn hình console bị lỗi

Dành cho các bạn chưa biết, Code page (còn được gọi là bộ ký tự (Character Set) hay bộ mã hóa (Encoding)) là một bảng các giá trị trong đó mỗi ký tự dược gán một con số đại diện. Một code page cho phép một máy tính xác định các ký tự và hiển thị nó cách chính xác.

Vậy để hiển thị tiếng Việt, ta phải chọn một code page phù hơp để thay thế cho 437. Unicode (uft-8) là một "ứng cứ viên sáng giá" đấy, với code page ID 65001, bộ mã này có thể hiển thị tới 1.112.064 ký tự.

Cú pháp:

BOOL WINAPI SetConsoleOutputCP(
  _In_ UINT wCodePageID
);

Tham số:

  • BOOL WINAPI SetConsoleTextAttribute(

    In HANDLE hConsoleOutput, In WORD wAttributes

    );

    2, như tên của nó là ID của code page bạn set cho Console. Như nói ở trên để hiển thị tiếng Việt và các ký tự đặc biệt ta dùng

    BOOL WINAPI SetConsoleTextAttribute(

    In HANDLE hConsoleOutput, In WORD wAttributes

    );

    3.

Demo:

Xuất tiếng việt ra màn hình console bị lỗi

Thay đổi màu chữ

Cú pháp:

BOOL WINAPI SetConsoleTextAttribute(
  _In_ HANDLE hConsoleOutput,
  _In_ WORD   wAttributes
);

Tham số:

  • BOOL WINAPI SetConsoleTextAttribute(

    In HANDLE hConsoleOutput, In WORD wAttributes

    );

    4 xem trong phần 1.
  • BOOL WINAPI SetConsoleTextAttribute(

    In HANDLE hConsoleOutput, In WORD wAttributes

    );

    5 là mã màu bạn một đặt cho chữ.

0 = Black

8 = Gray

1 = Blue

9 = Light Blue

2 = Green

10 = Light Green

3 = Aqua

11 = Light Aqua

4 = Red

12 = Light Red

5 = Purple

13 = Light Purple

6 = Yellow

14 = Light Yellow

7 = White

15 = Bright White

Từ 16 màu trên, ta có thể sử dụng công thức

BOOL WINAPI SetConsoleTextAttribute(
  _In_ HANDLE hConsoleOutput,
  _In_ WORD   wAttributes
);

6 để tạo ra các màu mới, trong đó

BOOL WINAPI SetConsoleTextAttribute(
  _In_ HANDLE hConsoleOutput,
  _In_ WORD   wAttributes
);

7 là mã màu nền,

BOOL WINAPI SetConsoleTextAttribute(
  _In_ HANDLE hConsoleOutput,
  _In_ WORD   wAttributes
);

8 là mã màu chữ. Ví dụ: 2 = 0 * 16 + 2 suy ra

BOOL WINAPI SetConsoleTextAttribute(
  _In_ HANDLE hConsoleOutput,
  _In_ WORD   wAttributes
);

9 thì màu nền là Black, màu chữ là Green; 80 = 5 * 16 + 0 suy ra

void SetColor(int backgound_color, int text_color)
{
    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    int color_code = backgound_color * 16 + text_color;
    SetConsoleTextAttribute(hStdout, color_code);
}

0 thì màu nền là Purple, màu chữ là Black.

Theo công thức đó, ta có thể xây dựng một hàm như sau:

void SetColor(int backgound_color, int text_color)
{
    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    int color_code = backgound_color * 16 + text_color;
    SetConsoleTextAttribute(hStdout, color_code);
}

Demo:

Xuất tiếng việt ra màn hình console bị lỗi

Nói chính xác hơn thì

void SetColor(int backgound_color, int text_color)
{
    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    int color_code = backgound_color * 16 + text_color;
    SetConsoleTextAttribute(hStdout, color_code);
}

1 là

void SetColor(int backgound_color, int text_color)
{
    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    int color_code = backgound_color * 16 + text_color;
    SetConsoleTextAttribute(hStdout, color_code);
}

2 mới đúng, nó không phải là màu nền cho cả console, mà chỉ những nơi có chữ.

Để thay đổi màu nền cho cả console, các bạn phải dùng câu lệnh

void SetColor(int backgound_color, int text_color)
{
    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    int color_code = backgound_color * 16 + text_color;
    SetConsoleTextAttribute(hStdout, color_code);
}

3 (có thể mình sẽ nói về nó ở một bài viết sắp tới).

Di chuyển con trỏ đến vị trí (x; y)

Trước tiên, Console lấy gốc tọa độ là góc trên bên trái, trục x nằm ngang và trục y nằm dọc. Nó gần giống hệ tọa độ trong toán học, tuy nhiên khác một điều là chiều dương trục y là chiều đi xuống và cả hai trục đều không có chiều âm.

Cú pháp:

BOOL WINAPI SetConsoleCursorPosition(
  _In_ HANDLE hConsoleOutput,
  _In_ COORD  dwCursorPosition
);

Tham số:

  • void SetColor(int backgound_color, int text_color) {

    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);  
    int color_code = backgound_color * 16 + text_color;  
    SetConsoleTextAttribute(hStdout, color_code);  
    
    }

    4 là một

    void SetColor(int backgound_color, int text_color) {

    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);  
    int color_code = backgound_color * 16 + text_color;  
    SetConsoleTextAttribute(hStdout, color_code);  
    
    }

    5 đã nói ở phần 1.
  • void SetColor(int backgound_color, int text_color) {

    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);  
    int color_code = backgound_color * 16 + text_color;  
    SetConsoleTextAttribute(hStdout, color_code);  
    
    }

    6 là một

    void SetColor(int backgound_color, int text_color) {

    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);  
    int color_code = backgound_color * 16 + text_color;  
    SetConsoleTextAttribute(hStdout, color_code);  
    
    }

    7 chứa 2 giá trị

    void SetColor(int backgound_color, int text_color) {

    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);  
    int color_code = backgound_color * 16 + text_color;  
    SetConsoleTextAttribute(hStdout, color_code);  
    
    }

    8,

    void SetColor(int backgound_color, int text_color) {

    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);  
    int color_code = backgound_color * 16 + text_color;  
    SetConsoleTextAttribute(hStdout, color_code);  
    
    }

    9 là tọa độ của con trỏ mà bạn muốn di chuyển đến. Cấu trúc COORD xem trong phần 1.

Để sử dụng cách đơn giản, các bạn xây dựng hàm sau:

void GoTo(SHORT posX, SHORT posY)
{
  HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD Position;
    Position.X = posX;
    Position.Y = posY;
  SetConsoleCursorPosition(hStdout, Position);
}

Khi GoTo đến một tọa độ và in gì đó, nếu tại đó đang có chữ thì nó sẽ in đè lên.

Và vì nó không có tọa độ âm, nên nếu các bạn truyền vào giá trị âm cho

BOOL WINAPI SetConsoleCursorPosition(
  _In_ HANDLE hConsoleOutput,
  _In_ COORD  dwCursorPosition
);

0 hoặc

BOOL WINAPI SetConsoleCursorPosition(
  _In_ HANDLE hConsoleOutput,
  _In_ COORD  dwCursorPosition
);

1, nó

BOOL WINAPI SetConsoleCursorPosition(
  _In_ HANDLE hConsoleOutput,
  _In_ COORD  dwCursorPosition
);

2.

Demo:

Xuất tiếng việt ra màn hình console bị lỗi

Thay đổi Console Title

Mặc định file

BOOL WINAPI SetConsoleCursorPosition(
  _In_ HANDLE hConsoleOutput,
  _In_ COORD  dwCursorPosition
);

3 cosole khi chạy Title sẽ là đường dẫn của file. Tuy nhiên ta hoàn toàn có thể thay đổi nó.

Cú pháp:

BOOL WINAPI SetConsoleTitle(
  _In_ LPCTSTR lpConsoleTitle
);

Tham số:

  • BOOL WINAPI SetConsoleCursorPosition(

    In HANDLE hConsoleOutput, In COORD dwCursorPosition

    );

    4 là một

    BOOL WINAPI SetConsoleCursorPosition(

    In HANDLE hConsoleOutput, In COORD dwCursorPosition

    );

    5 có dạng

    BOOL WINAPI SetConsoleCursorPosition(

    In HANDLE hConsoleOutput, In COORD dwCursorPosition

    );

    6 hoặc

    BOOL WINAPI SetConsoleCursorPosition(

    In HANDLE hConsoleOutput, In COORD dwCursorPosition

    );

    7.

Demo:

Xuất tiếng việt ra màn hình console bị lỗi

Ẩn/Hiện con trỏ

Để ẩn/hiện con trỏ chuột, các bạn sử dụng hàm

BOOL WINAPI SetConsoleCursorPosition(
  _In_ HANDLE hConsoleOutput,
  _In_ COORD  dwCursorPosition
);

8.

Cú pháp:

BOOL WINAPI SetConsoleCursorInfo(
  _In_       HANDLE              hConsoleOutput,
  _In_ const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo
);

Tham số:

  • BOOL WINAPI SetConsoleTextAttribute(

    In HANDLE hConsoleOutput, In WORD wAttributes

    );

    4
  • void GoTo(SHORT posX, SHORT posY) { HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

    COORD Position;  
    Position.X = posX;  
    Position.Y = posY;  
    
    SetConsoleCursorPosition(hStdout, Position); }

    0 là một con trỏ có cấu trúc

    void GoTo(SHORT posX, SHORT posY) { HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

    COORD Position;  
    Position.X = posX;  
    Position.Y = posY;  
    
    SetConsoleCursorPosition(hStdout, Position); }

    1:

typedef struct _CONSOLE_CURSOR_INFO {
  DWORD dwSize;
  BOOL  bVisible;
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;

Trong đó,

void GoTo(SHORT posX, SHORT posY)
{
  HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD Position;
    Position.X = posX;
    Position.Y = posY;
  SetConsoleCursorPosition(hStdout, Position);
}

2 là kích thước của con trỏ.

void GoTo(SHORT posX, SHORT posY)
{
  HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD Position;
    Position.X = posX;
    Position.Y = posY;
  SetConsoleCursorPosition(hStdout, Position);
}

3 nếu

void GoTo(SHORT posX, SHORT posY)
{
  HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD Position;
    Position.X = posX;
    Position.Y = posY;
  SetConsoleCursorPosition(hStdout, Position);
}

4 con trỏ sẽ được hiện, nếu

void GoTo(SHORT posX, SHORT posY)
{
  HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD Position;
    Position.X = posX;
    Position.Y = posY;
  SetConsoleCursorPosition(hStdout, Position);
}

5 con trỏ sẽ ẩn, ta chỉ cần quan tâm đến tham số này.

Các bạn có thể viết một hàm như sau:

void ShowCur(bool CursorVisibility)
{
  HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_CURSOR_INFO ConCurInf;
    ConCurInf.dwSize = 10;
    ConCurInf.bVisible = CursorVisibility;
    SetConsoleCursorInfo(handle, &ConCurInf);
}

Demo:

Vô hiệu hóa Select (bôi đen text)

Phải là "bôi trắng" chứ nhỉ :v

Select thuộc chế độ Quick Edit, vậy để không cho người dùng kéo chuột để bôi đen chữ, thì ta Disable Quick Edit Mode thôi.

Sử dụng hàm

void GoTo(SHORT posX, SHORT posY)
{
  HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD Position;
    Position.X = posX;
    Position.Y = posY;
  SetConsoleCursorPosition(hStdout, Position);
}

6.

Cú pháp:

BOOL WINAPI SetConsoleMode(
  _In_ HANDLE hConsoleHandle,
  _In_ DWORD  dwMode
);

Tham số:

  • void GoTo(SHORT posX, SHORT posY) { HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

    COORD Position;  
    Position.X = posX;  
    Position.Y = posY;  
    
    SetConsoleCursorPosition(hStdout, Position); }

    7 ở đây là

    void SetColor(int backgound_color, int text_color) {

    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);  
    int color_code = backgound_color * 16 + text_color;  
    SetConsoleTextAttribute(hStdout, color_code);  
    
    }

    5 đến Input.
  • void GoTo(SHORT posX, SHORT posY) { HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

    COORD Position;  
    Position.X = posX;  
    Position.Y = posY;  
    
    SetConsoleCursorPosition(hStdout, Position); }

    9 để vô hiệu hóa Select là

    BOOL WINAPI SetConsoleTitle(

    In LPCTSTR lpConsoleTitle

    );

    0 (NOT của ENABLE là DISABLE).

Xây dựng hàm như sau:

BOOL WINAPI SetConsoleTextAttribute(
  _In_ HANDLE hConsoleOutput,
  _In_ WORD   wAttributes
);

0

Demo:

Kết

Mình đã giới thiệu đến các bạn về

BOOL WINAPI SetConsoleTextAttribute(
  _In_ HANDLE hConsoleOutput,
  _In_ WORD   wAttributes
);

1 và một số hàm trong trong thư viện này, các bạn có thể sử dụng để vọc vạch làm vài game Console khi chán làm bài tập giải thuật nhé.

Nếu thấy bài viết hay và giúp được gì đó cho các bạn hãy rate 5* và chia sẻ cho mọi người tham khảo. Nếu có thắc mắc hoặc thấy bài viết còn sai sót hãy để lại comment bên dưới :v