• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Czy da się z konsoli zapisać plik txt zakodowany w UTF-8?

Object Storage Arubacloud
0 głosów
1,061 wizyt
pytanie zadane 20 września 2019 w C i C++ przez ampersand Nowicjusz (120 p.)
Witajcie

Uczę się C++. Do pisania używam VS Community 2019 pod Windowsem 7. Konsola używa kodowania CP852. Piszę program konsolowy używający polskich znaków. Czy jest możliwość wygenerowania pliku .txt, przy użyciu <fstream> tak, aby znaki w pliku były zakodowane w UTF-8?. Czy konsola używa domyślnie Notepada do generowania plików .txt? Udało mi się ustawić domyślne kodowanie w  UTF-8 w pliku .txt tworzonym z pulpitu, natomiast sam Notepad jest domyślnie ustawiony na ANSI i nie da się tego ruszyć.W taki też sposób generują mi się pliki .txt. z napisanego w VS programu konsolowego. Czy można do konsoli podać w programie parametr, dzięki któremu przy zapisie  plik będzie generowany za pomocą innego edytora niż Notepad? Ogólny mój cel to uzyskanie pliku .txt z polskimi znakami wygenerowanego przez program konsolowy.
1
komentarz 20 września 2019 przez adrian17 Ekspert (344,860 p.)

Czy konsola używa domyślnie Notepada do generowania plików .txt? 

Konsola jest tylko używana przez program do pokazywania rzeczy na monitorze i czytania wejścia od usera. Nie wpływa na inne rzeczy wykonywane przez program, jak pisanie do pliku. Program też nie "używa notepada" do pisania do pliku, na odwrót - to Notepad, podobnie jak Twój program, używa funkcji C lub C++owych do tworzenia i pisania do plików.

Więcej, proste pliki tekstowe z definicji nie mają w sobie informacji o kodowaniu. Edytor tekstu może zapisać dane z konkretnym kodowaniem, a przy czytaniu założyć lub zgadnąć na podstawie zawartości (niektóre kodowania zaczynają się od kilku znanych bajtów, ale nie jest to 100% pewny sposób) jakie jest kodowanie.

W każdym razie działanie z unicodem w C++ie, szczególnie na windowsie, to taki ból że sam nie jestem w tym zbyt dobry, więc nie będę dawał potencjalnie złej rady, sorry :(

Możesz przejrzeć odpowiedzi na stackoverflow typu https://stackoverflow.com/questions/3973582/how-do-i-write-a-utf-8-encoded-string-to-a-file-in-windows-in-c

2 odpowiedzi

+1 głos
odpowiedź 20 września 2019 przez j23 Mędrzec (194,920 p.)

Czy jest możliwość wygenerowania pliku .txt, przy użyciu <fstream> tak, aby znaki w pliku były zakodowane w UTF-8?

Spróbuj tak:

std::wofstream file("plik.txt");

std::locale lc(std::locale(""), new std::codecvt<char16_t, char, std::mbstate_t>);
file.imbue(lc);

file << L"grzegżółka";

Jak widać, dane wprowadzane do strumienia powinny być w znakach szerokich (wchar_t).

komentarz 20 września 2019 przez adrian17 Ekspert (344,860 p.)

Mi nie działa:

http://puu.sh/Ejv2l/e1c1b23624.png

(i nawet gdyby działało, to ogranicza się do podzbioru UTF-8 który możesz oryginalnie zakodować w wide stringu)

komentarz 21 września 2019 przez j23 Mędrzec (194,920 p.)
Chyba nie rozumiem, o co chodzi z tym ograniczeniem do podzbioru UTF-8. I w UTF-8, i w wide-stringu (UTF-16 na Win) mogę zakodować każdy znak, który jest przewidziany przez standard unikodu. Więc o co chodzi?

 

Jutro sprawdzę na Windowsie ten kod. Testowałem go (z drobną modyfikacją) na Linuksie i wszystko było ok.
komentarz 21 września 2019 przez j23 Mędrzec (194,920 p.)

Dobra, sprawdziłem kod w Visual C++ 2013 i faktycznie nie działa. Problem polega na tym, że za bardzo poszedłem z duchem czasu i pisałem pod standard C++17. Rozwiązaniem jest użycie specjalizacji std::codecvt_utf8_utf16<wchar_t> zamiast std::codecvt<char16_t, char, std::mbstate_t>. Według dokumentacji ta pierwsza jest deprecated od standardu C++17. Żeby było śmieszniej, ta druga specjalizacja jest zaimplementowana w mojej wersji VC++, ale zablokowana brakiem definicji _HAS_CHAR16_T_LANGUAGE_SUPPORT. Próba odblokowania kończy się stosem błędów. Wygląda na to, że VC++2013 nie traktuje char16_t jako natywny typ znakowy i dlatego użyta przeze mnie specjalizacja codecvt jest zablokowana na rzecz std::codecvt<unsigned short, char, std::mbstate_t>, która dokonuje konwersji MBCS <-> WCS. Normalnie wystarczyłoby ustawienie locale na kodowanie utf-8, ale u mnie ni cholery się nie dało.

komentarz 21 września 2019 przez adrian17 Ekspert (344,860 p.)

(używałem najnowszego VS2019)

W każdym razie jeśli ja nie zrozumiałem zbytnio co masz na myśli i nie mogę sprawić żeby działało, to początkujący który chce po prostu "wypisać utf8 do pliku" tym bardziej.

To właśnie miałem na myśli pisząc wcześniej to ;)

działanie z unicodem w C++ie, szczególnie na windowsie, to ból

BTW:

Chyba nie rozumiem, o co chodzi z tym ograniczeniem do podzbioru UTF-8. I w UTF-8, i w wide-stringu (UTF-16 na Win) mogę zakodować każdy znak, który jest przewidziany przez standard unikodu. Więc o co chodzi?

Źle się wypowiedziałem. Chodziło mi o w zasadzie tylko o haczyk z pierwszego zdania tutaj: https://stackoverflow.com/a/1810488/2468469

komentarz 21 września 2019 przez j23 Mędrzec (194,920 p.)

Próbowałeś z std::codecvt_utf8_utf16<wchar_t>?

komentarz 21 września 2019 przez adrian17 Ekspert (344,860 p.)
A, tak, teraz działa. Ale wciąż trzeba przejść przez prawodopodobnie-UTF-16 żeby użyć UTF-8 co jest dość niefajne :P
komentarz 21 września 2019 przez j23 Mędrzec (194,920 p.)
Jeśli dane wejściowe ze standardowego wejścia masz w CP852, to tak czy siak musi gdzieś być ten znak szeroki, bo inaczej jak SBCS -> WCS -> UTF-8 nie przekonwertujesz. Można sobie machnąć funkcje konwertujące SBCS <-> UTF-8 i używać zwykłych strumieni, albo używać od razu strumieni szerokich.
komentarz 22 września 2019 przez ampersand Nowicjusz (120 p.)

@j23,

 

Nie czaję o co tu chodzi więc i po omacku eksperymentuję:)

Znajdę gdzieś może fajniejsze źródło na ten temat niż:   https://en.cppreference.com/w/cpp/locale    ?

Te specjalizacje są dość "elastyczne"; np w miejsce "char" mogę równie dobrze podstawić "wchar_t, "TCHAR" itp. Efekt jest ten sam.

Podany przez Ciebie przykład u mnie działa, ale tylko jeśli wstringa  zapiszę w kodzie - tak jak u Ciebie. Natomiast nie mogę w pliku zapisać danych wklepanych przez wcin>>.

Próbowałem tak:




std::locale lc(std::locale(""), new std::codecvt<char16_t, char, std::mbstate_t>);

  
 std::wstring wstr;


 const wchar_t* wc_t[] = { wstr.c_str() }; 
 
 std::wofstream zapisz("plik.txt");

 zapisz.imbue(lc);

 std::wcin >> wstr;

 zapisz << L"ĄąĘęĆ棳ÓóŃńŻżŹź" << std::endl << *wc_t << std::endl;

 zapisz << *wc_t << std::endl << L"ĄąĘęĆ棳ÓóŃńŻżŹź" << std::endl;

Co więcej dane w pliku urywają się gdy wpiszę więcej polskich znaków. np "aębącśe" da "a©bĄc˜ e" (to chyba ANSI). Gdybym wczytał o jeden lub dwa więcej wtedy dane  w pliku urwą się. Nie wczyta się wtedy również tekst w cudzysłowach. 

 

komentarz 22 września 2019 przez j23 Mędrzec (194,920 p.)

Nie ustawiłeś kodowania dla strumienia wejściowego, czyli:

std::locale lc(".852"); // lub ".OCP"

std::wcin.imbue(lc); 

Jest to konieczne, żeby strumień wiedział, z jakiego kodowania ma konwertować na wide-stringa. To samo trzeba zrobić z std::wcout.

+1 głos
odpowiedź 20 września 2019 przez adrian17 Ekspert (344,860 p.)

W każdym razie działanie z unicodem w C++ie, szczególnie na windowsie, to taki ból że sam nie jestem w tym zbyt dobry, więc nie będę dawał potencjalnie złej rady, sorry :(

Podtrzymuję, ale mój najbliższy strzał (który wydaje się działać) to byłoby po prostu użyć `u8` w literałach (co oznacza literał utf-8):

std::string str = u8"ąęó日本語";

std::ofstream f("out.txt");
f << str;

 

Podobne pytania

0 głosów
1 odpowiedź 590 wizyt
0 głosów
0 odpowiedzi 217 wizyt
pytanie zadane 25 października 2019 w Systemy operacyjne, programy przez kamilvvv Początkujący (430 p.)
0 głosów
1 odpowiedź 228 wizyt
pytanie zadane 8 czerwca 2019 w Nasze projekty przez Maro200 Nowicjusz (190 p.)

92,572 zapytań

141,422 odpowiedzi

319,643 komentarzy

61,959 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...