2016-12-12 38 views
6

Jeśli dodasz wartość wchar_t, char16_t lub char32_t do wąskiego ostream, wydrukuje ona wartość liczbową punktu kodowego.iostreams - Drukuj wartość `wchar_t` lub` charXX_t` jako znak

#include <iostream> 
using std::cout; 
int main() 
{ 
    cout << 'x' << L'x' << u'x' << U'x' << '\n'; 
} 

wydruki x120120120. Wynika to z faktu, że istnieje operator<< dla określonej kombinacji basic_ostream z jego charT, ale nie ma analogicznych operatorów dla innych typów znaków, więc są one przekształcane w trybie cichym na int i drukowane w ten sposób. Podobnie, dla wąskiego literałami łańcuch (L"x", u"x", U"X") są przekształcone do void* cicho i drukowane wartością wskaźnika, a nie wąski Ciąg przedmiotów (wstring, u16string, u32string) nie będzie jeszcze kompilacji.

Więc pytanie: Jaki jest najmniej okropny sposób, aby wydrukować wchar_t, char16_t lub wartości char32_t na wąskiej ostream, jako znak, a nie jako wartości numerycznej na kod? Powinno poprawnie konwertować wszystkie punkty kodowe, które są reprezentowalne w kodowaniu ostream, do tego kodowania i powinno zgłaszać błąd, gdy nie można przedstawić punktu kodowego. (Na przykład, biorąc pod uwagę u'…' i UTF-8 ostream sekwencję trzech bajtów 0xE2 0x80 0xA6 powinny być zapisywane w strumieniu, lecz podane u'â' i ostream KOI8-R błędu należy podać).

Podobnie , jak można wydrukować nie wąski ciąg znaków C lub obiekt typu string na wąskim ostream, konwertując na kodowanie wyjściowe?

Jeśli nie można tego zrobić w ramach ISO C++ 11, przyjmuję odpowiedzi specyficzne dla platformy.

(Zainspirowany this question.)

+2

W skrócie, musisz albo 1) użyć szerokiego ostream, albo 2) przekonwertować dane szerokiego znaku do wąskiego kodowania samodzielnie (co jest potencjalnie stratną konwersją). Ostream nie może zrobić dla ciebie tego nawrócenia. Spójrz na ['std :: wstring_convert'] (http: //en.cppreference.com/w/cpp/locale/wstring_convert) lub użyj biblioteki takiej jak [ICONV] (https://www.gnu.org/software/libiconv/) lub [ICU] (http: // site.icu- project. org /). –

Odpowiedz

2

Jak można zauważyć, nie ma operator<<(std::ostream&, const wchar_t) dla wąskiego ostream. Jeśli chcesz użyć tej składni, możesz nauczyć ostream, jak to zrobić z wchar s, aby ta procedura została wybrana jako lepsze przeciążenie niż ta, która wymaga najpierw konwersji na liczbę całkowitą.

Jeśli czujesz się na siłach:

namespace std { 
    ostream& operator<< (ostream& os, wchar_t wc) { 
    if(unsigned(wc) < 256) // or another upper bound 
     return os << (unsigned char)wc; 
    else 
     throw your_favourite_exception; // or handle the error in some other way 
    } 
} 

przeciwnym razie zrobić prosty struct że transparentnie obejmuje wchar_t i ma zwyczaj friend operator<< i konwertować szerokości znaków, że przed ich wyprowadzania.

Edit: Aby dokonać konwersji w locie do i od lokalizacji, można skorzystać z funkcji z <cwchar>, jak:

ostream& operator<< (ostream& os, wchar_t wc) { 
    std::mbstate_t state{}; 
    std::string mb(MB_CUR_MAX, '\0'); 
    size_t ret = std::wcrtomb(&mb[0], wc, &state); 
    if(ret == static_cast<std::size_t>(-1)) 
     deal_with_the_error(); 
    return os << mb; 
} 

Nie zapomnij ustawić locale do domyślny system:

std::locale::global(std::locale("")); 
std::cout << L'ŭ'; 
+0

To nie konwertuje wartości na wąskie kodowanie wyjściowe. To niezbędne, a także utwór, którego jeszcze nie umiem. – zwol

+0

@zwol Jak inaczej chcesz przekonwertować postać szerszą niż ją akceptującą, jeśli jest w ASCII i odrzucając w inny sposób? Trzeba wtedy określić konkretność, np. Usunąć akcenty lub coś podobnego. –

+0

Twój przykład używa "x", który to przekazuje (dla 'L'x'', musisz zrobić to samo dla innych typów), więc założyłem, że właśnie o to prosisz. –