Obsługa polskich znaków w C++

Obsługa polskich znaków w C++
szybki_procesor
szybki_procesor
  • Rejestracja:około 5 lat
  • Ostatnio:ponad 4 lata
  • Postów:25
0

Podlinkowałby ktoś stronę z porządnym opisem obsługi polskich znaków w C i C++, chodzi mi o dokładne i szczegółowe wytłumaczenie niuansów, nie tylko co ale i w jaki sposób to działa.

Kopiuj
#include<iostream>
using namespace std;

int main()
{
	string test="ą";
	cout<<test<<endl;
	
	
	wstring wtest=L"ą";
	wcout<<wtest<<endl;
}

Konkratna sprawa która mnie zastanawia: dlaczego w tym programie pierwszy cout wypisuje ładnie ą, za to wcout wypisuje pustą linię?

edytowany 3x, ostatnio: cerrato
MarekR22
Moderator C/C++
  • Rejestracja:ponad 17 lat
  • Ostatnio:2 minuty
4
  1. Jakie kodowanie ma źródło?
  2. Jakie kodowanie używa kompilator do czytania źródła (czy jest zgodne z 1)
  3. Jakiego kodowania używa kompilator do zapisu literałów w kodzie wynikowym? Czy to kodowanie pokrywa polskie znaki?
  4. Jakie locale (kodowanie znaków) ma system na jakim uruchomiony jest program? To ma wpływ na to jak interpretowane jest wyjście z programu.

Na systemach POSIX na każde to pytanie odpowiedzią jest UTF-8 i nie ma problemów.
Na Windows jest bagno i trzeba uważać.

Czy wiesz jak działa kodowanie UTF-8 a jak działa Window1250/CP-1250?

Biorąc pod uwagę, że działa ci dla wstring/wcou, punkty 1 2 są ok, a problem jest dla punktów 3 4.


Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
edytowany 3x, ostatnio: MarekR22
szybki_procesor
szybki_procesor
  • Rejestracja:około 5 lat
  • Ostatnio:ponad 4 lata
  • Postów:25
0

Mam linuxa mint 20 w xfce, g++, xterm i xeda. W jaki sposób sprawdzać kodowanie? Bo w terminalu mam na pewno UTF-8. Nie uczyłem się o kodowaniach, wiem bardzo pobieżnie.

edytowany 1x, ostatnio: szybki_procesor
MarekR22
Moderator C/C++
  • Rejestracja:ponad 17 lat
  • Ostatnio:2 minuty
3

Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
Azarien
no nie wszystko jest prawdą w tym artykule, a taki pompatyczny tytuł :)
Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 7 godzin
1
szybki_procesor napisał(a):

Konkratna sprawa która mnie zastanawia: dlaczego w tym programie pierwszy cout wypisuje ładnie ą, za to wcout wypisuje pustą linię?

Bo linuxy są upośledzone pod względem wstringów..

MarekR22 napisał(a):

Na Windows jest bagno i trzeba uważać.

Ale za to wstring jest używalny i jest zdefiniowany jako UTF-16, a nie nie wiedzieć na co komu UTF-32.

edytowany 2x, ostatnio: Azarien
nalik
wstring to nie utf32. wstring to typ zdolny pomieścić utf32
Azarien
no ale ma 4 bajty na znak. nikomu to niepotrzebne, i jak widać praktycznie nie używane. tymczasem pod Windowsem wstring ma dwubajtowe znaki UTF-16 i działa bardzo dobrze. (no, uściślając „jest to typ zdolny pomieścić utf16” bo co tam trzymasz to twoja sprawa…)
nalik
To już inna kwestia czy potrzebne komukolwiek. Twórcy standardu cpp i kompilatorów mają wiele za uszami. Nie mniej, jakby standard był w tej kwestii jednoznaczny, to by problemów nie było. Ale jest jak jest.
nalik
  • Rejestracja:około 9 lat
  • Ostatnio:13 dni
  • Postów:1039
3
Azarien napisał(a):
szybki_procesor napisał(a):

Konkratna sprawa która mnie zastanawia: dlaczego w tym programie pierwszy cout wypisuje ładnie ą, za to wcout wypisuje pustą linię?

Bo linuxy są upośledzone pod względem wstringów..

MarekR22 napisał(a):

Na Windows jest bagno i trzeba uważać.

Ale za to wstring jest używalny i jest zdefiniowany jako UTF-16, a nie nie wiedzieć na co komu UTF-32.

wstring na windowsie: 2 bajty na znak, wstring na innych systemach 4 bajty na znak.
MS jak zwykle po swojemu 🤮

edytowany 1x, ostatnio: nalik
Azarien
no i bardzo dobrze że wstring na windowsie ma 2 bajty na znak. nikomu nie są potrzebne te 4-bajtowe znaki. dowód przez przykład: nikt ich nie używa :)
nalik
  • Rejestracja:około 9 lat
  • Ostatnio:13 dni
  • Postów:1039
3

OP, dużo by pisać. Jest to puszka pandory i jedna z tych rzeczy, której do tej pory komitetowi standaryzacyjnemu nie udało się ogarnąć do końca.

Zachęcam do sprawdzenia wyniku tego programu:

Kopiuj
#include <iostream>
#include <string>
#include <locale>
#include <codecvt>
using namespace std;

template <typename O, typename S>
void write_hex(O &out, const S &s) {
	for (auto x : s) {
    	out << std::hex << static_cast<uint16_t>(x) << " ";
    }
    out << endl;
}


int main() {
    wstring_convert<codecvt_utf8<char16_t>, char16_t> cvu16;
    wstring_convert<codecvt_utf8<wchar_t>> cvu32;
	
    cout << "string:" << endl; 
    string str = u8"abecą";
    cout << str << endl;
    write_hex(cout, str);
    cout << endl;
	
    cout << "u16string:" << endl; 
    u16string str_u16 = u"abecą";
    cout << cvu16.to_bytes(str_u16) << endl;
    write_hex(cout, str_u16);
    cout << endl;
  
    cout << "wstring:" << endl; 
    wstring str2 = L"abecą";
    wcout << str2 << endl;
    cout << cvu32.to_bytes(str2) << endl;
    write_hex(cout, str2);
    cout << endl;
}

Dla leniwych: https://ideone.com/VIRApH

A więc to nie kwestia kodowania wewnątrz programu, a systemu operacyjnego. Linux od dawna wspiera utf-8. Za to nie wspiera utf16/utf32 jako locale, bo są niekompatybilne z ASCII.
Więc wewnątrz swojego programu możesz używać wstring, zakodowanego jako utf16, ale już nie wypiszesz na konsolę bez konwersji. EDIT: Trzeba, jak zostało wspomniane poniżej w innych postach, ustawić locale, na zgodne z systemem.

Swoją drogą, codecvt_utf8 jest deprecated :), ale nie ma dla niego alternatywy w bibliotece standardowej. Nad wsparciem dla unicode pracuje podgrupa w komitecie standaryzacyjnym. Może w końcu kiedyś udam im się wypracować to co inne języki, nawet te młodsze, już dawno mają ;)
Więc może zainteresuj się boost.locale.

edytowany 5x, ostatnio: nalik
MarekR22
coś ci się pokręciło. std::wstring na Linux nie może być utf-16 bo wchar_t na Linux jest 32-bitowe (ergo używane jest utf-32), tylko na Windows wchar_t ma 16 bitów. Tak problemem jest konwersja kodowania z utf-32 do utf-8 jaką powinno wykonać std::wcout.
nalik
Ja nie twierdzę że wstring to utf16. Nie mniej na 4 bajtach da się zapisać znak utf16. Standard nie określa kodowanie na wstring. Ale główny problem to ta konwersja do tego co jest ustawione w systemie.
nalik
Co do locale, wystarczy spróbować ustawić utf16 albo 32 i zobaczyć co się stanie;)
Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 7 godzin
1
nalik napisał(a):

A więc to nie kwestia kodowania wewnątrz programu, a systemu operacyjnego. Linux od dawna wspiera utf-8. Za to nie wspiera utf16 jako locale, bo są niekompatybilne z ASCII.
Więc wewnątrz swojego programu możesz używać wstring, zakodowanego jako utf16, ale już nie wypiszesz na konsolę bez konwersji.

A pod Windows mogę :P

MarekR22
Moderator C/C++
  • Rejestracja:ponad 17 lat
  • Ostatnio:2 minuty
3

A to ciekawe, poza ustawieniem locale na std::wcout trzeba wyłączyć synchronizację z API C, żeby to zadziałało:
https://wandbox.org/permlink/f6YpuptWoHbz0bYa

Kopiuj
#include<iostream>
#include <locale>

int main()
{
    std::ios_base::sync_with_stdio(false);
    std::wcout.imbue(std::locale{""});
    std::cout << "char ąłóżę" << std::endl;
    std::wcout << L"wchar_t ałóężź" << std::endl;
}

Wskazówkę znalazłem tu, ale boost okazał się zbędny.


Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
edytowany 1x, ostatnio: MarekR22
nalik
Ciekawe. A jednocześnie strasznie nieintuicyjne.
BG
  • Rejestracja:prawie 6 lat
  • Ostatnio:dzień
  • Postów:289
4

Ja tylko delikatnie chciałbym przypomnieć, że Windowsowy wstring pozwala trzymać napisy w kodowaniu UCS-2 a nie UTF16. UTF16 jest kodowaniem o zmiennej długości (2 do 4 bajtów na znak).
Z punktu widzenia poprawności programu nie ma znaczenia, czy napis trzymamy w std::string w UTF8 czy w std::wstring w UTF16 - tak czy inaczej nie można zakładać, że i-ty znak będzie na pozycji txt[i]

UTF32 jest już kodowaniem o stałej "szerokości", więc ten niedobry Linuksowy 32-bitowy wchar jest przynajmniej w stanie go jednoznacznie reprezentować.

BTW - w ramach ciekawostki - dawno dawno temu, w NDK do Androida wchar_t był zdefiniowany jako char (zmienili w wersji 2.3).

Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 7 godzin
2
Bartłomiej Golenko napisał(a):

Ja tylko delikatnie chciałbym przypomnieć, że Windowsowy wstring pozwala trzymać napisy w kodowaniu UCS-2 a nie UTF16.

Tak było gdzieś za czasów Windows NT. Obecnie natywnym kodowaniem pod Windows jest UTF-16. I tak, to czasami oznacza że jeden znak zajmuje dwa wchary.

szybki_procesor
szybki_procesor
  • Rejestracja:około 5 lat
  • Ostatnio:ponad 4 lata
  • Postów:25
0

#MarekR22

Dzęki działa, ja jeszcze wpadłem na coś takiego:

Kopiuj
#include<iostream>
#include <locale.h>
using namespace std;

int main()
{
	std::locale::global(std::locale("pl_PL.utf8"));
	
	
	string test="ą";
	cout<<test<<endl;
	
	freopen ("/dev/tty", "w", stdout);
	
	
	wstring wtest=L"ćą";
	wcout<<wtest<<endl;
}

Pytanie tylko czy jest jakiś podręcznik, strona, dokumment czy jakikolwiek inny tekst który solidnie omawia te sprawy?
edit:
Co powiecie na to? http://www.cplusplus.com/reference/iostream/wcout/

A program should not mix output operations on wcout with output operations on cout (or with other narrow-oriented output operations on stdout): Once an output operation has been performed on either, the standard output stream acquires an orientation (either narrow or wide) that can only be safely changed by calling freopen on stdout.

Teraz pytanie: Dlaczego rozwiązanie #MarekR22 działa? xD Co stdio z C ma tutaj do rzeczy?
http://www.cplusplus.com/reference/ios/ios_base/sync_with_stdio/

edytowany 6x, ostatnio: szybki_procesor
Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 7 godzin
0
szybki_procesor napisał(a):

Teraz pytanie: Dlaczego rozwiązanie #MarekR22 działa? xD Co stdio z C ma tutaj do rzeczy?

Wygląda mi to na bug, po prostu… miało być tak pięknie, UTF-8 w konsoli, a tu się okazuje że i tak nie działa to na zasadzie „po prostu” tylko jakieś locale, jakieś freopen..

i w tym kontekście...

MarekR22 napisał(a):

Na systemach POSIX na każde to pytanie odpowiedzią jest UTF-8 i nie ma problemów.
Na Windows jest bagno i trzeba uważać.

No jak widać są problemy. Czyli też jest bagno.

szybki_procesor
szybki_procesor
W sensie undefined behaviour?
Azarien
co tu ma UB do rzeczy?
06
utf-8 będzie działać, jeśli wysyłany tekst też będzie w takim kodowaniu. Podobnie w Windowsie - nie będzie problemów, jeśli aplikacja będzie w kodowaniu cp852 (co jest trochę niepraktyczne). Jeśli chodzi o wchar_t, to AFAIK na obu systemach trzeba ustawiać locale na "zewnętrzne" kodowanie.
szybki_procesor
szybki_procesor
  • Rejestracja:około 5 lat
  • Ostatnio:ponad 4 lata
  • Postów:25
0
Kopiuj
#include<iostream>
#include <locale>
using namespace std;
int main()
{
	std::locale::global(std::locale("pl_PL.utf8"));
	
	//freopen ("/dev/tty", "w", stdout);
	
	
	
	
	for(int i=0;i<500;i++)
	{
		wcout<<static_cast<wchar_t>(i)<<L" "<<i<<endl;
		
	}
	
}

Może mi ktoś wytłumaczyć do tu się odpierdala dzieje? Dlaczego wypisuje tylko do 156 a potem koniec?

Kopiuj
	for(int i=160;i<500;i++)
	{
		
		wcout<<static_cast<wchar_t>(i)<<L" "<<i<<endl;
		
	}

A od 160 wypisuje, co tam siedzi w tych 157, 158, 159?

edytowany 3x, ostatnio: cerrato
nalik
https://ideone.com/IVPr0y tutaj wypisuje wszystko
szybki_procesor
szybki_procesor
Ciekawe, czyli to coś "u mnie" tylko co?

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.