C++ и UTF8

Модератор: Модераторы разделов

Tim474
Сообщения: 81

C++ и UTF8

Сообщение Tim474 »

Можно ли работать со строками и символами, если системная кодировка - UTF8, так же, как и если бы была какая-нибудь однобайтовая кодировка? Нужно: считывание символов, сравнение символа строки с другим отдельным символом, вывод символа строки в консоль, замена одного символа строки другим.
Например:

Код: Выделить всё

#include <iostream>
#include <string>
using namespace std;
main()
{
  string str ("компьютер");
  cout << str[2] <<endl;
  return 0;
}

выводит знак вопроса. Если же строка содержит только латинские буквы, то работает правильно.
Спасибо сказали:
frp
Сообщения: 1445
ОС: Debian Squeeze

Re: C++ и UTF8

Сообщение frp »

Tim474 писал(а):
08.08.2009 08:35
выводит знак вопроса. Если же строка содержит только латинские буквы, то работает правильно.

Так и должно быть. Символы в UTF8 занимают от 1 до 6 байт (реально от 1 до 4). Русские символы занимают два байта. И когда вы выводите str[2], то получаете третий байт строки (первый байт буквы 'о') - \xD0 - это в UTF8 символ непечатный, поэтому печатает знак вопроса в ромбе.
Решение вашей проблемы - использовать Unicode-строки wstring или Qt-строки QString.
Спасибо сказали:
Аватара пользователя
Denjs
Сообщения: 1685
ОС: SuSe 10.2

Re: C++ и UTF8

Сообщение Denjs »

just use QT - and no problem ^_^
QDroid - Среда исполнения и фреймворк для QtScript.
OTPD - Открытые драйвера промышленных принтеров чеков и этикеток (кроссплатформенная подсистема печати).
Спасибо сказали:
watashiwa_daredeska
Бывший модератор
Сообщения: 4038
Статус: Искусственный интеллект (pre-alpha)
ОС: Debian GNU/Linux

Re: C++ и UTF8

Сообщение watashiwa_daredeska »

См. ICU.
Спасибо сказали:
Аватара пользователя
serzh-z
Бывший модератор
Сообщения: 8259
Статус: Маньяк
ОС: Arch, Fedora, Ubuntu

Re: C++ и UTF8

Сообщение serzh-z »

Denjs писал(а):
08.08.2009 11:58
just use QT - and no problem ^_^
Как в том анекдоте - "Так не болит?.. Ну вот, так и ходите...".
Спасибо сказали:
Tim474
Сообщения: 81

Re: C++ и UTF8

Сообщение Tim474 »

frp писал(а):
08.08.2009 09:44
Tim474 писал(а):
08.08.2009 08:35
выводит знак вопроса. Если же строка содержит только латинские буквы, то работает правильно.

Так и должно быть. Символы в UTF8 занимают от 1 до 6 байт (реально от 1 до 4). Русские символы занимают два байта. И когда вы выводите str[2], то получаете третий байт строки (первый байт буквы 'о') - \xD0 - это в UTF8 символ непечатный, поэтому печатает знак вопроса в ромбе.
Решение вашей проблемы - использовать Unicode-строки wstring или Qt-строки QString.


С wstring вроде кое-что получается, спасибо. Только можно ли где по ней найти подробную документацию, и желательно на русском?
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: C++ и UTF8

Сообщение NickLion »

Tim474 писал(а):
08.08.2009 08:35
Можно ли работать со строками и символами, если системная кодировка - UTF8, так же, как и если бы была какая-нибудь однобайтовая кодировка? Нужно: считывание символов, сравнение символа строки с другим отдельным символом, вывод символа строки в консоль, замена одного символа строки другим.
Например:

Код: Выделить всё

#include <iostream>
#include <string>
using namespace std;
main()
{
  string str ("компьютер");
  cout << str[2] <<endl;
  return 0;
}

выводит знак вопроса. Если же строка содержит только латинские буквы, то работает правильно.

Как уже сказал frp - все правильно, так и будет, поскольку символ в UTF-8 может занимать разное количество байт. Т.е. чтобы получить N-й символ, вам надо обработать все предыдущие. Следовательно возможен только последовательный доступ или хранить таблицу начала каждого символа. оба варианта не очень удобны. В общем, можно "почти" последовать совету Denjs. Qt хранит строки в формате UCS-2, где на каждый символ отводится ровно два байта (т.н. wide-char). А при выводе использовать преобразование UCS2-to-UTF8. Оно довольно простое, примерно так (переделывал прямо здесь, не гарантирую, что откомпилится ;) ):

Код: Выделить всё

unsigned long GetUTF8Length( const wchar_t* str ) {
    unsigned long len = wcslen( str );
    unsigned long uln = len;
    unsigned short w;
    for( int i = 0; i < uln; i++ ) {
        w = ( (word) str[ i ] );
        if( w >= 0x80 ) {
            len++;
            if( w >= 0x800 )
                len++;
        }
    }
    return len;
}

void UCS2_to_UTF8( byte* utf8, const wchar_t* uni ) {
    unsigned short w;
    unsigned long len = wcslen( uni );
    for( int i = 0, j = 0; i < len; i++ ) {
        w = ( (word) uni[ i ] );
        if( w <  0x80 ) {
            utf8[ j++ ] = (byte) w;
        } else if( w < 0x800 ) {
            utf8[ j++ ] = 0xC0 | ( w >> 6 );
            utf8[ j++ ] = 0x80 | ( w & 0x3f );
        } else {
            utf8[ j++ ] = 0xe0 | ( w >> 12 );
            utf8[ j++ ] = 0x80 | ( ( w >> 6 ) & 0x3f );
            utf8[ j++ ] = 0x80 | ( w & 0x3f );
        }
    }
}

Можно написать и обратное преобразование.
Спасибо сказали:
Tim474
Сообщения: 81

Re: C++ и UTF8

Сообщение Tim474 »

так и будет, поскольку символ в UTF-8 может занимать разное количество байт.

Ну это и ежу понянтно. Меня интересовала не причина данной проблемы, а выход из неё.
wstring устраивает, только почему-то не работает getline (wcin,<переменная>);, точнее не работает, там где надо. Если просто пишу которкую программу

Код: Выделить всё

#include <iostream>
#include <string>
#include <locale>
using namespace std;
int gtl()
{
  setlocale(LC_CTYPE, "");
  wstring str;
  getline (wcin,str);
  wcout << str << endl;
}
main()
{
  gtl();
}
, то работает. А в более большой программе - нет, компилируется без предупреждений, но оператор getline как будто игнорирует, а wcin >> str; на этом месте работает вполне нормально. Никто не знает, в чём может быть подвох?
Спасибо сказали:
frp
Сообщения: 1445
ОС: Debian Squeeze

Re: C++ и UTF8

Сообщение frp »

Tim474 писал(а):
12.08.2009 12:41
wstring устраивает, только почему-то не работает getline (wcin,<переменная>);, точнее не работает, там где надо.

А можно пример исходника, где не работает?
Спасибо сказали:
Tim474
Сообщения: 81

Re: C++ и UTF8

Сообщение Tim474 »

А можно пример исходника, где не работает?


http://narod.ru/disk/11935874000/wheel.cpp.html, если по-индусски понимаешь. А то я С++ только учу, поэтому чтение данной программы может быть опасно для мозга.

Тот самый неработающий getline - 188 строчка.
Спасибо сказали:
Tim474
Сообщения: 81

Re: C++ и UTF8

Сообщение Tim474 »

И ещё почему-то не получается чтение из файла:

Код: Выделить всё

#include <iostream>
#include <fstream>
#include <string>
#include <locale>
using namespace std;

main()
{
  setlocale(LC_ALL, "");
  wifstream wordbase ("words.txt");
  wstring word;
  while (!wordbase.eof())
  {
    wordbase >> word;
    wcout << word <<endl;
  }
return 0;
}


В файле words.txt несколько русских слов, каждое начинается с новой строки.

Программа компилируется, но при запуске сообщает:
terminate called after throwing an instance of 'std::ios_base::failure'
what(): basic_filebuf::underflow invalid byte sequence in file
Aborted
Спасибо сказали:
frp
Сообщения: 1445
ОС: Debian Squeeze

Re: C++ и UTF8

Сообщение frp »

Tim474 писал(а):
13.08.2009 15:22
В файле words.txt несколько русских слов, каждое начинается с новой строки.

А слова в какой кодировке? wifstream и wstring используют UCS-2
Спасибо сказали:
Tim474
Сообщения: 81

Re: C++ и UTF8

Сообщение Tim474 »

Файл в UTF8.
wifstream и wstring используют UCS-2

А заставить wifstream использовать utf8 не получится?
Спасибо сказали:
watashiwa_daredeska
Бывший модератор
Сообщения: 4038
Статус: Искусственный интеллект (pre-alpha)
ОС: Debian GNU/Linux

Re: C++ и UTF8

Сообщение watashiwa_daredeska »

utf-8 — 8-битная кодировка. Строки в кодировке utf-8 надо хранить в string, а не в wstring.
Спасибо сказали:
Tim474
Сообщения: 81

Re: C++ и UTF8

Сообщение Tim474 »

utf-8 — 8-битная кодировка.

http://ru.wikipedia.org/wiki/UTF8
Текст, состоящий только из символов с номером меньше 128, при записи в UTF-8 превращается в обычный текст ASCII. И наоборот, в тексте UTF-8 любой байт со значением меньше 128 изображает символ ASCII с тем же кодом. Остальные символы Юникода изображаются последовательностями длиной от 2 до 6 байтов
Спасибо сказали:
watashiwa_daredeska
Бывший модератор
Сообщения: 4038
Статус: Искусственный интеллект (pre-alpha)
ОС: Debian GNU/Linux

Re: C++ и UTF8

Сообщение watashiwa_daredeska »

И что? utf-8 — 8-битная многобайтная кодировка. Строка utf-8 состоит из 8-битных байтов, а не 16- или 32-битных слов.
Спасибо сказали: