Это поправленная копипаста из cppreference-а, идея понятная, но зачем же это так уродливо?
Дальше, codecvt_utf8 депрекейтед с 2017-го. Вместо него ничего не предложено, остался codecvt, который просто так не создать, у него конструктор приватный. Но его вроде бы можно получить через use_facet у локали... Это выглядит как-то так:
Это работает. Но тут важно угадать подходящую локаль. Пока я писал locale("en_US.UTF-8"), ловил сегфолт при первом вызове метода сс. При этом локаль en_US.UTF-8 существует: для несуществующих он кидает исключение "locale::facet::_S_create_c_locale name not valid" при попытке их создать. Но вот конвертировать в UTF-8 она, видимо, не умеет...
Но может быть можно обойтись без извращений и просто писать в wcout юникод? А он сам умный? Если бы.
Вот такая программа:
#include <iostream>
#include <string>
#include <locale>
#include <codecvt>
using namespace std;
int main() {
wstring ws1(L"абвгды");
// cout << endl; -- волшебная строчка!
wcout << ws1 << endl;
wcout.imbue(locale("C"));
wcout << ws1 << endl;
wcout.imbue(locale("en_US.UTF-8"));
wcout << ws1 << endl;
// wcout.imbue(locale("ru_RU.UTF-8")); -- тут я ловлю исключение, видимо такой локали у меня почему-то нет
// wcout << ws1 << endl;
wcout.imbue(locale(wcout.getloc(), new codecvt_utf8<wchar_t>)); // может ему кодека не хватает и надо явно прописать?
wcout << ws1 << endl;
}
Выводит на чистом транслите
abvgdy`
abvgdy`
abvgdy`
abvgdy`
Обратите внимание на букву ы!
А если раскомментировать волшебную строчку, то, конечно, случится волшебство. Вот такое:
01234K
01234K
01234K
01234K
И здесь понимание происходящего окончательно оставляет меня.
Единственное светлое пятно на фоне этого безнадёжного мрака -- utf8, кажется, нормально работает с обычным cout-ом, во всяком случае русские буковки рисует без проблем.
Расскажите пожалуйста, может есть какие-то гайды, как с этим нормально работать? Или стоит просто забыть о существовании стандартной библиотеки локализации навсегда?
Здравствуйте, SergH, Вы писали:
SH>Привет, а кто-нибудь пытался использовать iostream для чего-то более сложного, чем hello world? Я чего-то видимо совсем не понимаю...
А что вам мешает вызвать setlocale(LC_ALL, "en_US.UTF-8"); в начале?
SH>А если раскомментировать волшебную строчку, то, конечно, случится волшебство. SH>И здесь понимание происходящего окончательно оставляет меня.
C++ он такой. Cмотрите в сторону https://www.cplusplus.com/reference/cwchar/fwide https://www.cplusplus.com/reference/cstdio/freopen
Здравствуйте, kov_serg, Вы писали:
_>А что вам мешает вызвать setlocale(LC_ALL, "en_US.UTF-8"); в начале?
Видимо непонимание связи. Я думал, достаточно установить локаль потоку, и дальше он как-нибудь сам.
Помогло, спасибо!
А как угадать, какая локаль доступна?
Я пытался найти стандартный способ получить их список, но не нашёл.
И как-то можно контролировать, во что именно превратится юникод?
_>C++ он такой. Cмотрите в сторону _>https://www.cplusplus.com/reference/cwchar/fwide
Понял, то есть перемешивать использование cout и wcout нельзя.
Здравствуйте, SergH, Вы писали:
SH>А как угадать, какая локаль доступна? SH>Я пытался найти стандартный способ получить их список, но не нашёл.
в линухах locale -a
в винде наверное как-то так:
SergH:
SH>Начать с того, что функции конвертации в и из utf-8 мне написать удалось и они вроде даже работают.
Конвертацию между UTF8 и UTF16 можно сделать используя битовые трюки, без всяких локалей и библиотечных функций.
Встречал такой код на стековервлоу и успешно использовал.
Для конвертации национальных кодировок в/из UTF8 UTF16 можно использовать MultiByteToWideChar/WideCharToMultiByte для windows.
Для linux — libiconv.
Модератор-националист Kerk преследует оппонентов по политическим мотивам.
Здравствуйте, Bill Baklushi, Вы писали:
BB>SergH:
SH>>Начать с того, что функции конвертации в и из utf-8 мне написать удалось и они вроде даже работают.
BB>Конвертацию между UTF8 и UTF16 можно сделать используя битовые трюки, без всяких локалей и библиотечных функций. BB>Встречал такой код на стековервлоу и успешно использовал.
У меня такой файл валяется со времен института.
// utf.h#pragma once
#include <string>
using std::string;
using std::wstring;
enum { utf_max_len=6 };
// raw utfint uc_to_utf(int w, char *p);
int utf_to_uc(const char *p,int *w,int lim);
int could_be_utf(const char *s,int len);
// utf utils
string uc2utf(const wstring &s);
bool utf2uc(wstring& res,const string& src);
int checkutf(const string &s);
// utf.cpp#include"utf.h"int uc_to_utf(int w, char *p) {
int r,n;
if (w&0x80000000) return 0;
if (w&0xFC000000) n=6; else
if (w&0xFFE00000) n=5; else
if (w&0xFFFF0000) n=4; else
if (w&0xFFFFF800) n=3; else
if (w&0xFFFFFF80) n=2; else { p[0]=char(w); return 1; }
r=n; while (n>1) { p[--n]=(char)( (w & 0x3F) | 0x80 ); w>>=6; }
p[0]=(char)( (255<<(8-r)) | w ); return r;
}
int utf_to_uc(const char *p,int *w,int lim) {
if (lim<1) return 0;
int z=(unsigned char)p[0];
if ((z&0x80)==0) { if (w) *w=z; return 1; }
int n=0, q=0x80; do { n++; q>>=1; } while (z&q);
if ( n<2 || lim<n ) return 0; // invalid or no roomint r=z&(q-1);if (r==0) return 0; // prohibited coding int len=n; while (n>1) {
z=*++p; if ((z&0xC0)!=0x80) return 0; // invalid coding
r=(r<<6)|(z&0x3F); n--;
}
if (w) *w=r; return len;
}
int could_be_utf(const char *s,int len) { // -1-can not be utf, 0-could be utf, but no utf symbols, 1-valid utf and utf symbols foundint res=-1, i=0;
while (i<len) {
int w, k=utf_to_uc(s+i,&w,len-i);
if (k==0 || w>65535) return -1;
if (k>1) res=0;
i+=k;
}
return ++res;
}
string uc2utf(const wstring &s) {
char buf[utf_max_len+1];
string res;
int n=s.length();
for(int i=0;i<n;i++) {
int k=uc_to_utf(s[i],buf); buf[k]='\0';
res.append(buf);
}
return res;
}
bool utf2uc(wstring& res,const string& src) {
res.empty();
const char *p=src.c_str(); int n=src.length();
for(int i=0;i<n;) {
int w=0,k=utf_to_uc(p+i,&w,n-i);
if (k==0 || w>65535) return false;
i+=k; res+=wchar_t(w);
}
return true;
}
int checkutf(const string &s) { return could_be_utf(s.c_str(),s.length()); }
Здравствуйте, kov_serg, Вы писали:
_>в линухах locale -a _>в винде наверное как-то так:
Спасибо за способ для линукса, посмотрел, что там у меня. У меня они все называются немного иначе: ru_RU.utf8. Но всё равно locale::facet::_S_create_c_locale name not valid да и setlocale(LC_ALL, "ru_RU.utf8") не срабатывает. Но в списке есть...
Для винды да, видел такой код. Но я имел ввиду, конечно, что-то стандартное независимое из С++. Раз уж её надо указывать... Хотя может быть подразумевается, что её из какой-нибудь переменной окружения берут?
_>Хз я своими велосипедами пользуюсь, они более предсказуемы.
Да. Велосипед написать несложно. То, что он оказывается надежнее, проще и удобнее, на мой взгляд, говорит много плохого про стандартную локализацию а может и про всю iostream.
Здравствуйте, Bill Baklushi, Вы писали:
BB>Конвертацию между UTF8 и UTF16 можно сделать используя битовые трюки, без всяких локалей и библиотечных функций. BB>Встречал такой код на стековервлоу и успешно использовал.
Можно посмотреть определение utf-8 в википедии и реализовать преобразование utf-8 <--> unicode (UCS4) полностью самостоятельно Там довольно просто всё.
Возня с битами может получится не очень эффективной, но если там не сотни мегабайт, то должно прокатить.
Специально делать для utf16... вот не помню, чтобы он мне вообще был нужен.
BB>Для конвертации национальных кодировок в/из UTF8 можно использовать MultiByteToWideChar/WideCharToMultiByte для windows. BB>Для linux — libiconv.
Нуу, да, это понятно, это я тоже умею.
Меня шокировала сложная и продуманная библиотека локализации в С++, которую я оказался не в состоянии использовать.
Так-то варианты понятно что есть.
При выводе на консоль через cout/wcout я решил отказаться от std::endl в одном своём проекте. Вместо этого выводил \n в явном виде, чтобы быстрее работало.
Здравствуйте, Sm0ke, Вы писали:
S>При выводе на консоль через cout/wcout я решил отказаться от std::endl в одном своём проекте. Вместо этого выводил \n в явном виде, чтобы быстрее работало.
endl вроде еще flush делает. А так — да, в остальном он равнозначен "\n".