Helpex - Trao đổi & giúp đỡ Đăng nhập
19

Tôi muốn hiểu sự khác biệt giữa charwchar_t? Tôi hiểu rằng wchar_tsử dụng nhiều byte nhưng tôi có thể nhận được một ví dụ rõ ràng để phân biệt khi tôi sẽ sử dụng charvswchar_t

19 hữu ích 5 bình luận 11k xem chia sẻ
16

Về cơ bản, hãy sử dụng wchar_tkhi mã hóa có nhiều ký hiệu hơn số ký hiệu charcó thể chứa.

Bối cảnh
Các charloại có đủ năng lực để giữ bất kỳ ký tự (mã hóa) trong bộ ký tự ASCII.

Vấn đề là nhiều ngôn ngữ yêu cầu nhiều mã hóa hơn so với tài khoản ASCII. Vì vậy, thay vì 127 bảng mã có thể, cần nhiều hơn nữa. Một số ngôn ngữ có thể có hơn 256 bảng mã. Một charkiểu không đảm bảo phạm vi lớn hơn 256. Vì vậy, một kiểu dữ liệu mới là bắt buộc.

Các wchar_tký tự, hay còn gọi là ký tự rộng, cung cấp nhiều chỗ hơn cho các mã hóa.

Tóm tắt
Sử dụng charkiểu dữ liệu khi phạm vi mã hóa từ 256 trở xuống, chẳng hạn như ASCII. Sử dụng wchar_tkhi bạn cần dung lượng trên 256.

Ưu tiên Unicode để xử lý các bộ ký tự lớn (chẳng hạn như biểu tượng cảm xúc).

16 hữu ích 5 bình luận chia sẻ
21

Anwser ngắn:

Bạn không bao giờ được sử dụng wchar_ttrong C ++ hiện đại, ngoại trừ khi tương tác với các API dành riêng cho hệ điều hành (về cơ bản wchar_tchỉ sử dụng để gọi các hàm API của Windows).

Câu trả lời dài:

Thiết kế của thư viện C ++ tiêu chuẩn ngụ ý rằng chỉ có một cách để xử lý Unicode - bằng cách lưu trữ các chuỗi được mã hóa UTF-8 trong các mảng char, vì hầu như tất cả các hàm chỉ tồn tại trong các biến thể char (hãy nghĩ đến std::exception::what).

Trong một chương trình C ++, bạn có hai ngôn ngữ:

  • Ngôn ngữ thư viện C tiêu chuẩn được đặt bởi std::setlocale
  • Ngôn ngữ thư viện C ++ tiêu chuẩn được đặt bởi std::locale::global

Thật không may, không ai trong số họ định nghĩa hành vi của các hàm tiêu chuẩn mở tệp (như std::fopen, std::fstream::openv.v.). Hành vi khác nhau giữa các hệ điều hành:

  • Linux đang mã hóa bất khả tri, vì vậy những hàm đó chỉ cần chuyển chuỗi ký tự đến lệnh gọi hệ thống cơ bản
  • Trên Windows chuỗi ký tự được chuyển đổi thành chuỗi rộng bằng cách sử dụng ngôn ngữ cụ thể của người dùng trước khi lệnh gọi hệ thống được thực hiện

Mọi thứ thường hoạt động tốt trên Linux vì mọi người đều sử dụng ngôn ngữ dựa trên UTF-8 nên tất cả dữ liệu đầu vào và đối số của người dùng được chuyển cho các mainhàm sẽ được mã hóa UTF-8. Nhưng bạn vẫn có thể cần chuyển các ngôn ngữ hiện tại sang các biến thể UTF-8 một cách rõ ràng vì chương trình C ++ mặc định bắt đầu sử dụng "C"ngôn ngữ mặc định . Tại thời điểm này, nếu bạn chỉ quan tâm đến Linux và không cần hỗ trợ Windows, bạn có thể sử dụng mảng char và std::stringgiả sử đó là chuỗi UTF-8 và mọi thứ "chỉ hoạt động".

Các vấn đề xuất hiện khi bạn muốn hỗ trợ Windows, vì ở đó bạn luôn có thêm ngôn ngữ thứ 3: ngôn ngữ được đặt cho người dùng hiện tại có thể được định cấu hình ở đâu đó trong "Bảng điều khiển". Vấn đề chính là ngôn ngữ này không bao giờ là ngôn ngữ unicode, vì vậy không thể sử dụng các chức năng như std::fopen(const char *)std::fstream::open(const char *)để mở tệp bằng đường dẫn Unicode. Trên Windows, bạn sẽ phải sử dụng giấy gói tùy chỉnh mà sử dụng chức năng Windows cụ thể phi tiêu chuẩn như _wfopen, std::fstream::open(const wchar_t *)trên Windows. Bạn có thể kiểm tra Boost.Nowide (chưa có trong Boost) để xem cách này có thể được thực hiện như thế nào: http://cppcms.com/files/nowide/html/

Với C ++ 17, bạn có thể sử dụng std::filesystem::pathđể lưu trữ đường dẫn tệp theo cách di động, nhưng nó vẫn bị hỏng trên Windows:

  • Hàm tạo ngầm std::filesystem::path::path(const char *)sử dụng ngôn ngữ dành riêng cho người dùng trên MSVC và không có cách nào để làm cho nó sử dụng UTF-8. Hàm std::filesystem::u8stringnên được sử dụng để tạo đường dẫn từ chuỗi UTF-8, nhưng quá dễ dàng để quên điều này và sử dụng hàm tạo ngầm định để thay thế.
  • std::error_category::message(int) cho cả hai loại lỗi trả về mô tả lỗi bằng cách sử dụng mã hóa dành riêng cho người dùng.

Vì vậy, những gì chúng tôi có trên Windows là:

  • Các chức năng thư viện tiêu chuẩn mở tệp bị hỏng và không bao giờ được sử dụng.
  • Các đối số được chuyển đến main(int, char**)bị hỏng và không bao giờ được sử dụng.
  • Các hàm WinAPI kết thúc bằng * A và macro bị hỏng và không bao giờ được sử dụng.
  • std::filesystem::path bị hỏng một phần và không bao giờ được sử dụng trực tiếp.
  • Các danh mục lỗi được trả về std::generic_categorystd::system_categorybị hỏng và không bao giờ được sử dụng.

Nếu bạn cần giải pháp lâu dài cho một dự án không tầm thường, tôi khuyên bạn nên:

  • Sử dụng Boost.Nowide hoặc triển khai trực tiếp chức năng tương tự - điều này sẽ sửa chữa thư viện tiêu chuẩn bị hỏng.
  • Triển khai lại các danh mục lỗi tiêu chuẩn được trả về std::generic_categorystd::system_categoryđể chúng luôn trả về các chuỗi được mã hóa UTF-8.
  • Bao bọc std::filesystem::pathđể lớp mới sẽ luôn sử dụng UTF-8 khi chuyển đổi đường dẫn thành chuỗi và chuỗi thành đường dẫn.
  • Gói tất cả các chức năng cần thiết std::filesystemđể chúng sử dụng trình bao bọc đường dẫn của bạn và các danh mục lỗi của bạn.

Thật không may, điều này sẽ không khắc phục được sự cố với các thư viện khác hoạt động với tệp, nhưng nhiều thư viện bị hỏng (không hỗ trợ unicode).

Bạn có thể kiểm tra liên kết này để được giải thích thêm: http://utf8everywhere.org/

21 hữu ích 0 bình luận chia sẻ
15

Không bao giờ sử dụng wchar_t.

Khi có thể, hãy sử dụng (một số loại mảng) char, chẳng hạn như std::string, và đảm bảo rằng nó được mã hóa bằng UTF-8.

Khi bạn phải giao tiếp với các API không nói UTF-8, hãy sử dụng char16_thoặc char32_t. Không bao giờ sử dụng chúng theo cách khác; chúng chỉ cung cấp những lợi thế hão huyền và khuyến khích mã bị lỗi.

Lưu ý rằng có rất nhiều trường hợp char32_tyêu cầu nhiều hơn một ký tự để đại diện cho một ký tự mà người dùng có thể nhìn thấy. OTOH, sử dụng UTF-8 charbuộc bạn phải xử lý chiều rộng thay đổi từ rất sớm.

15 hữu ích 1 bình luận chia sẻ
loading
Không tìm thấy câu trả lời bạn tìm kiếm? Duyệt qua các câu hỏi được gắn thẻ c++ , hoặc hỏi câu hỏi của bạn.

Có thể bạn quan tâm

loading