2013-06-05 121 views
7

Tôi cần in một số ký tự unicode trên thiết bị đầu cuối Linux bằng cách sử dụng iostream. Những điều kỳ lạ xảy ra mặc dù. Khi tôi viết:Ký tự unicode C++ in

cout << "\u2780"; 

Tôi nhận được: , gần như chính xác những gì tôi muốn. Tuy nhiên, nếu tôi viết:

cout << '\u2780'; 

Tôi nhận được: 14851712.

Vấn đề là, tôi không biết chính xác nhân vật được in tại thời điểm biên dịch. Do đó, tôi muốn làm một cái gì đó như:

int x; 
// some calculations... 
cout << (char)('\u2780' + x); 

Bản in nào: . Sử dụng wcout hoặc wchar_t thay vì không hoạt động. Làm cách nào để in chính xác?

Từ những gì tôi tìm thấy trên Internet, điều quan trọng là tôi sử dụng trình biên dịch g ++ 4.7.2 ngay từ kho Debian Wheezy.

+0

đang sử dụng wchar_t với nhà điều hành 'L'? đăng mã đầy đủ của bạn nếu có thể hoặc [sscce.org] (SSCCE) – pinkpanther

+0

Nếu bạn không muốn gây rối với mã hóa Unicode, bạn có thể sử dụng bảng để ánh xạ các chuỗi thành các giá trị có thể có của 'x' thay vì thêm nó. – dyp

+0

Bản sao có thể có của [Cách in ký tự Unicode trong C++?] (Http://stackoverflow.com/questions/12015571/how-to-print-unicode-character-in-c) –

Trả lời

6

Ký tự Unicode \u2780 nằm ngoài phạm vi cho kiểu dữ liệu char. Bạn nên đã nhận được cảnh báo trình biên dịch này để cho bạn biết về nó: (ít nhất là g của tôi ++ 4.7.3 cung cấp cho nó)

test.cpp:6:13: warning: multi-character character constant [-Wmultichar] 

Nếu bạn muốn làm việc với các nhân vật như U + 2780 là đơn vị duy nhất bạn sẽ phải sử dụng datatype widechar wchar_t hoặc nếu bạn đủ may mắn để có thể làm việc với C++ 11, char32_t hoặc char16_t. Lưu ý rằng một đơn vị 16 bit không đủ để biểu thị toàn bộ các ký tự Unicode.

Nếu điều đó không có tác dụng với bạn, có thể do ngôn ngữ mặc định "C" không hỗ trợ cho đầu ra không phải ASCII. Để khắc phục sự cố đó, bạn có thể gọi số setlocale khi bắt đầu chương trình; cách mà bạn có thể xuất toàn bộ phạm vi của các nhân vật được hỗ trợ bởi locale của người sử dụng: (có thể có hoặc không có hỗ trợ cho tất cả các ký tự mà bạn sử dụng)

#include <clocale> 
#include <iostream> 

using namespace std; 

int main() { 
    setlocale(LC_ALL, ""); 
    wcout << L'\u2780'; 
    return 0; 
} 
+0

Tất nhiên có thể có cùng một vấn đề các ký tự khác (SMP) nếu 'sizeof (wchar_t) <4'. Tôi muốn đề nghị sử dụng 'char16_t' hoặc' char32_t' btw. – dyp

+2

bổ sung vào tiền tố mã hóa 'L', có' u8' cho mã hóa 'UTF8',' u' cho 'char16_t' và' U' cho 'char32_t'. – Appleshell

+0

'setlocale' khi chuyển một' "" 'cho tên miền địa phương đặt ngôn ngữ ưa thích của người dùng, đó không nhất thiết là một miền địa phương Unicode. – dyp

4

Khi bạn viết

cout << "\u2780"; 

Các trình biên dịch chuyển \ u2780 thành mã hóa thích hợp của ký tự đó trong bộ ký tự thực thi. Đó có thể là UTF-8, và vì vậy chuỗi kết thúc có bốn byte (ba cho ký tự, một cho null terminator).

Nếu bạn muốn tạo ký tự lúc chạy thì bạn cần một số cách để thực hiện khi chạy cùng một chuyển đổi sang UTF-8 mà trình biên dịch đang làm lúc biên dịch.


C++ 11 cung cấp một tiện dụng wstring_convert mẫu và codecvt khía cạnh có thể làm được điều này, tuy nhiên libstdC++, thư viện thực hiện tiêu chuẩn mà đi kèm với gcc, vẫn chưa nhận được xung quanh để thực hiện chúng (như của gcc 4.8) . Phần sau đây cho thấy cách sử dụng các tính năng này, nhưng bạn sẽ cần phải sử dụng một triển khai thư viện chuẩn khác hoặc chờ libstdC++ để triển khai chúng.

#include <codecvt> 

int main() { 
    char32_t base = U'\u2780'; 

    std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert; 
    std::cout << convert.to_bytes(base + 5) << '\n'; 
} 

Bạn cũng có thể sử dụng bất kỳ phương pháp nào khác để sản xuất UTF-8 mà bạn có sẵn.Ví dụ, biểu tượng, ICU và việc sử dụng thủ công các phần tử codecvt_byname trước C++ 11 sẽ hoạt động. (Tôi không hiển thị ví dụ về các vì mã mà sẽ được nhiều hơn nhiều so với một mã đơn giản cho phép wstring_convert.)


Một thay thế đó sẽ làm việc cho một số ít các nhân vật sẽ tạo ra một loạt các chuỗi sử dụng chữ.

char const *special_character[] = { "\u2780", "\u2781", "\u2782", 
    "\u2783", "\u2784", "\u2785", "\u2786", "\u2787", "\u2788", "\u2789" }; 

std::cout << special_character[i] << '\n'; 
0

Chương trình sẽ in một số nguyên vì C++ 11 §2.14.3/1:

Một multicharacter đen, hoặc một nhân vật bình thường theo nghĩa đen có chứa một đơn c-char không biểu diễn trong tập ký tự thực hiện, được hỗ trợ theo điều kiện, có kiểu int và có giá trị được xác định.

Bộ ký tự thực hiện là những gì char có thể đại diện, tức là ASCII.

Những gì bạn có là 14851712 hoặc trong thập lục phân e29e80, là biểu diễn UTF-8 của U + 2780. Đặt UTF-8, một mã hóa nhiều byte vào một int là điên rồ và ngu ngốc, nhưng đó là những gì bạn nhận được từ một tính năng "được hỗ trợ có điều kiện, được thực hiện xác định".

Để nhận giá trị UTF-32, hãy sử dụng U'\u2780'. Đầu tiên U chỉ định loại char32_t và mã hóa UTF-32 (tức là tối đa 31 bit nhưng không có cặp thay thế). Thứ hai \u chỉ định một tên ký tự phổ quát có chứa điểm mã. Để có được một giá trị được cho là tương thích với wcout, hãy sử dụng L'\u2780', nhưng điều đó không nhất thiết phải sử dụng giá trị thời gian chạy Unicode cũng như không cho bạn nhiều hơn hai byte dung lượng lưu trữ.

Để thao tác và in mã Unicode một cách đáng tin cậy, như các câu trả lời khác đã lưu ý, tiêu chuẩn C++ chưa hoàn toàn đạt được. Câu trả lời của Joni là cách tốt nhất, nhưng nó vẫn giả định rằng trình biên dịch và môi trường của người dùng đang sử dụng cùng một miền địa phương, thường không đúng.

Bạn cũng có thể chỉ định chuỗi UTF-8 trong nguồn bằng cách sử dụng u8"\u2780" và buộc môi trường thời gian chạy UTF-8 sử dụng một cái gì đó như std::locale::global(std::locale("en_US.UTF-8"));. Nhưng điều đó vẫn có các cạnh thô. Joni đề xuất sử dụng giao diện C std::setlocale từ <clocale> thay vì giao diện C++ std::locale::global từ <locale>, giải pháp cho giao diện C++ bị hỏng trong GCC trên OS X và có lẽ các nền tảng khác. Các vấn đề là đủ nhạy cảm với nền tảng mà bản phân phối Linux của bạn có thể đã đặt một bản vá vào gói GCC của riêng họ.

+0

Hoặc bạn hoặc tôi có thể bỏ lỡ một cái gì đó, bởi vì trình biên dịch bây giờ kêu gọi "U không được khai báo trong phạm vi". – Sventimir

+0

@Sventimir Rõ ràng nó không được hỗ trợ trong GCC 4.7.2, nhưng nó là một phần của tiêu chuẩn C++ 11. Chỉ cần đi với 'L'xxx''; trong Linux, nó nên làm về cơ bản giống nhau. – Potatoswatter

+0

Thêm hỗ trợ C++ 11 với lệnh gọi 'gcc --std = C++ 11' không hoạt động. Nó bây giờ biên dịch, nhưng in giá trị thập phân của char (10112), chứ không phải bản thân char. – Sventimir

0

Trong Linux, tôi đã thành công in ra bất kỳ unicode trực tiếp như trong cách ngây thơ nhất:

std::cout << "ΐ , Α, Β, Γ, Δ, ,Θ , Λ, Ξ, ... ±, ... etc"