Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > С/С++: Кроссплатформенное программирование, Qt/Gtk+/wxWidgets > Всем привет. Нужна помощь с UTF-8 и ASCII


Автор: cdmmon 24.4.2014, 22:22
Привет всем! 
Нужна помощь.
Пишу приложение в eclipse на с++. В данном приложении делаю запрос на сайт и получаю ответ (JSON), в виде стоки UTF-8. С сайта уходит "пйъя"("\u043f\u0439\u044a\u044f")
Вот сюда приходит колбэк:
int parse_string(void * ctx, const unsigned char * stringVal, unsigned int stringLen)
Используемое UI не поддерживает UNICODE (UTF8).
Вывожу содержимое в консоль:
parse_string-пйÑ?Ñ?, string length-8 (получается по 2 байта)
, дальше кастую к инту и вывожу в консоль:
208, 191, 208, 185, 209, 138, 209, 143
Кто может растолковать, как строится эта строка? а то ищу, читаю. не могу догнать пока..
Спасибо

Автор: tzirechnoy 24.4.2014, 22:28
http://en.wikipedia.org/wiki/UTF8#Description

Автор: cdmmon 24.4.2014, 22:36
Цитата(tzirechnoy @ 24.4.2014,  22:28)
http://en.wikipedia.org/wiki/UTF8#Description

ну это первое, что я прочитал. а можешь пояснить?

Добавлено через 6 минут и 26 секунд
Цитата(cdmmon @ 24.4.2014,  22:36)
Цитата(tzirechnoy @ 24.4.2014,  22:28)
http://en.wikipedia.org/wiki/UTF8#Description

ну это первое, что я прочитал. а можешь пояснить?

на сколько я понял их вики - если символ в UTF-8 имеет один байт, то он соответствует таблице ASCII, если имеет 2 байта, как киррилическая буква "п" в utf-8, то что за что отвечает?

Автор: cdmmon 25.4.2014, 00:23
так что? никто?
вот что я не могу понять:
041A (по юникоду 1050) - большая кириллическая «К» - выдаст нам 2 байта - D0 9A (при трех байтах будет - E0 90 9A), где D0 - это 11010000, две первые еденицы говорят о том, что по этому символу будет два байта (а третья еденица для чего?), и второй байт - 9А это 10011010, где 10 - это обозначение того, что это байт самого символа,  т.е. 154? первый старший бит считается? если это 154, тогда в какой таблице 154 равен большой кириллической букве "К"?

Добавлено через 7 минут и 31 секунду
Тоже самое и с моими "пйъя", которые приходят с сайта.
т.е. parse_string-208, 191, 208, 185, 209, 138, 209, 143  (string length-8 - по 2 байта), где 208 = 11010000  (11 - значит два байта, а что за третья еденица?), 191 - это буква "п"? Так же и с буквой "й" - 208 (2 байта) + 185(буква "й"?). А дальше? 209! - это уже 11010001 - эта зачем единица тут?

Автор: math64 25.4.2014, 07:51
Младшие биты первого байта - это старшие биты Unicode. В младших битах второго байта (и третьего, если есть) хранятся младшие биты Unicode. Их нужно собрать и склеить в одно целое (операции & и <<)

Добавлено через 6 минут и 6 секунд
PS: как я понял, твой колбэк вызывается из Java. В таком случае правильнее попроить Java перекодировать твою строку. Как это сделать, спрашивай на форуме Java.

Автор: cdmmon 25.4.2014, 08:19
Разобрался, как перевести в Юникод: (вдруг пригодится кому..)
берем за основу следующие вводные данные:
const byte* src (это указатель на нашу строку), size_t count (это длина нашей строки, т.е. для русского по 2 байта на каждый символ, при моих 4-х символах  (пйъя) размер равен 8-ми)
    char output[count + 1]; //создаем массив + 1, запишем туда конец строки)
    int ctr = 0; //счетчик нашей новой строки
    while(*src != 0) { //тут все понятно
        if((*src & 0x80) == 0){ /*проверяем, если текущий байт меньше 128, значит просто берем значение тупо из ASCII*/
            //ASCII
            output[ctr++] = *src++;
        }else if((*src & 0xE0) == 0xC0){ /*проверяем первый байт на то, что там у нас стоят две единицы, что готорит о том, что на текущий символ задействовано 2 байта, т.е. к примеру для симовла"п" (208) первый байт - это 1101 0000 (208 или 0xD0), где первые две единицы это инфа о том, что у нас два байта, третья единица нужна будет для перевода в Юникод */
            // 2 байта
            byte b1 = *src++; //запоминаем первый байт
            byte b2 = *src++; //и второй байт
            if((b2 & 0xC0) != 0x80) /*проверяем на ошибку во втором байте - второй байт в двоичной ситеме всегда должен начинатся с 10 (1000 0000 = 0х80). В ином случае - ошибка. */
                    break;
            output[ctr++] = ((b1 & 0x1F) << 6) | (b2 & 0x3F);/* приводим значение двух байт к таблице юникода: в первых скобках убираем две первые единицы, т.к. они нужны только для информирования о кол-ве байт для текущего символа, и результат сдвигаем. получается к примеру для буквы "п" первый байт равет 208 (1101 0000 или D0), где 0xD0 & 0x1F (1F = 0001 1111) - откидывается первые три цифны - 110, и получается 0001 0000, после здвига получается 0100 0000 0000 = 1024. Во вторых скобках обрабатываем байт, в котором содержится код символа: для буквы "п" это 191 (1011 1111 или 0xBF). Дальше откидываем первые две цифры (они всегда равны 10 для второго байта) - BF & 3F (0011 1111) = 0011 1111 (3F). Дальше складываем результат ообеих скобок: 1024 + 63 = 1087 - в таблице юникода это и есть символ "п"*/
        }else if((*src & 0xF0) == 0xE0){//аналогично обрабатываем 3 байта
            // 3 bytes
            byte b1 = *src++;
            byte b2 = *src++;
            byte b3 = *src++;
            if((b2 & 0xC0) != 0x80)
                    break;
            if((b3 & 0xC0) != 0x80)
                    break;
            output[ctr++] = ((b1 & 0x0F) << 12) | ((b2 & 0x1F) << 6) |
                    (b3 & 0x3F);
        }else{
            output[ctr] = '\0';
            return output;
        }
    }
    output[ctr] = '\0';//по окончанию проверки строки, записываем в массив конец строки. ВСЕ!

Добавлено @ 08:32
Цитата(math64 @ 25.4.2014,  07:51)
Младшие биты первого байта - это старшие биты Unicode. В младших битах второго байта (и третьего, если есть) хранятся младшие биты Unicode. Их нужно собрать и склеить в одно целое (операции & и <<)

Добавлено @ 07:57
PS: как я понял, твой колбэк вызывается из Java. В таком случае правильнее попроить Java перекодировать твою строку. Как это сделать, спрашивай на форуме Java.

да, спс. это я уже понял.

по поводу колбэка - это отдельная либа под с++. 
сецчас перевести в юникод - у меня получилось. Но у меня не поддерживается юникод. теперь не могу сообразить, как символ "п" из таблицы юникода, равный 1087 (43F) перевести в ASCII, равный 239?
Есть идеи?

Автор: math64 25.4.2014, 10:27
Если нет официальной поддержки кодировок, переводить можно так:
Код

wchar_t cp1251[128] = L"...АБВГДЕ...Яабвгде...я"; // символы  в порядке кодировки cp1251, коды 128 ... 255
// или если компилятор не поддерживает L"...":
wchar_t cp1251[128] =  { ..., 0x44E, 0x44F }; // символы  в порядке кодировки cp1251, коды 128 ... 255
void unicodeTo1251(const wchar_t* from, char* to) {
  while(*from) {
     wchar_t ch = *from++;
     if (ch < 128) {
        *to++ = (char)ch;
     } else {
        for (int i = 0; i < 128; i++)
           if (cp1251[i] == ch)
              break;
        if (i < 128)
           *to++ = (char)(i+128);
        else
           *to++ = '?';
     }
  }
  *to = '\0';
}

Автор: cdmmon 25.4.2014, 10:41
Цитата(math64 @ 25.4.2014,  10:27)
Если нет официальной поддержки кодировок, переводить можно так:
Код

wchar_t cp1251[128] = L"...АБВГДЕ...Яабвгде...я"; // символы  в порядке кодировки cp1251, коды 128 ... 255
// или если компилятор не поддерживает L"...":
wchar_t cp1251[128] =  { ..., 0x44E, 0x44F }; // символы  в порядке кодировки cp1251, коды 128 ... 255
void unicodeTo1251(const wchar_t* from, char* to) {
  while(*from) {
     wchar_t ch = *from++;
     if (ch < 128) {
        *to++ = (char)ch;
     } else {
        for (int i = 0; i < 128; i++)
           if (cp1251[i] == ch)
              break;
        if (i < 128)
           *to++ = (char)(i+128);
        else
           *to++ = '?';
     }
  }
  *to = '\0';
}

спасибочки. Ща протестим.. ))

Автор: cdmmon 25.4.2014, 13:43
Цитата(math64 @ 25.4.2014,  10:27)
Если нет официальной поддержки кодировок, переводить можно так:
Код

        ...
        for (int i = 0; i < 128; i++)
           if (cp1251[i] == ch)
              break;
        if (i < 128)
           *to++ = (char)(i+128);
        else
           *to++ = '?';
        ...

У тебя обшибчка.. надо вынести переменную int i из цикла (объявить ее перед циклом), чтобы она была доступна после окончания действия цикла..
Смысл понятен. Но теперь есть другой вопрос. 
Может есть идеи, как можно заполнить строку переменной cp1251 в соответствие от разного типа кодировок?
А то вручную все вбивать не охота. И может есть идеи, как определить мне, в какую кодировку мне пытаться переводить мой УТФ? т.е. в ср1251, или 1253...? Потому что по идеи мне может прийти туда и другие кодировки..

Добавлено через 1 минуту и 40 секунд
я не могу себе представить, как мне заполнить переменную cp1251 - там в ср1251 столько левых символов между теми символами, которые мне нужны. их либо надо чем то заполнить, либо все таки как то ввести туда. Но как?

Автор: math64 25.4.2014, 17:13
Цитата(cdmmon @  25.4.2014,  13:43 Найти цитируемый пост)
У тебя обшибчка.. надо вынести переменную int i из цикла (объявить ее перед циклом), чтобы она была доступна после окончания действия цикла..

я вводил код на лету. естественно, есть ошибка.

Цитата(cdmmon @  25.4.2014,  13:43 Найти цитируемый пост)
Может есть идеи, как можно заполнить строку переменной cp1251 в соответствие от разного типа кодировок?

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

Автор: cdmmon 25.4.2014, 18:04
Вот так выглядет мой вариант для ср1251:

Код

String Utils::WStringToString(WString wstr){
    String str = "";
    wchar_t cp1251[129] = L"ЂЃ,ѓ„…†‡€‰Љ<ЊЌЋЏђ‘’“”•-—□™љ>њќћџ ЎўЈ¤Ґ¦§Ё©Є«¬­®Ї°±Ііґµ¶•ё№є»јЅѕїАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюя";
    for (int count = 0; count < wstr.length(); count++){
        wchar_t ch = static_cast<wchar_t>(wstr[count]);
        if (ch < 128) {
            str += static_cast<char>(ch);
        } else {
            int i = 0;
            for (; i < 128; i++)
                if (cp1251[i] == ch)
                    break;
            if (i < 128)
                str += static_cast<char>(i+128);
            else
                str += "?";
        }
    }
    return str;
}


Первые 64 бита ковырял вручную из ворда, так что может где-то и не угадал, но для меня эти символы не так важны..

Добавлено через 32 секунды
Цитата(math64 @  25.4.2014,  17:13 Найти цитируемый пост)
Если нужно возиться с разными кодировками, возьмите готовую библиотеку, например libiconv.

Спасибо, почитаю..

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)