Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C++ Builder > Кодировка в C++ Builder 2010


Автор: ipc 21.10.2010, 13:37
Доброго времени суток, господа. Помогите, пожалуйста, разобраться с проблемой. Почему русский текст моим приложением понимается как абракадабра (не вопросики).

Я заканчиваю курсовой проект, в котором часть мультиязычного функционала состоит в том, чтобы обращаться к удаленной базе данных. Для этого была выбрана кодировка utf8.

На стороне сервера MySQL, PHP
На стороне клиента Embarcadero Builder 2010 (с базовой Indy) 

Все таблицы  MySQL, и поля в них, со сравнением utf8_general_ci.

Код обращения на Билдере:

Код

        // посылаемое значение
        TStringStream *StrStreamSend  = new TStringStream();
        // возращаемый ответ от сервера
        TStringStream *StrStreamInc  = new TStringStream();

        // параметры...
        StrStreamSend->WriteString("ID=1");        

        // запрос
        IdHTTPmain->Post("http://mysite.ru/catcher.php", StrStreamSend, StrStreamInc);
        // в этом месте на сервере происходит запрос  (SELECT `names`WHERE `ID`='1') 
        // и результат первой записи выводится через echo
        IdHTTPmain->Disconnect();

        // вот здесь абракадабра ((((
        Memo1->Text = StrStreamInc->DataString;


Неисключено, что тему кодировки я не до конца понимаю, потому как безрезультатно гуглю уже третьи сутки, а сроки безжалостно поджимают. Если я все правильно понимаю, то: 
   - utf8 на то и такая громоздкая, чтобы в ней можно было передавать все символы большиства языков. 
   - Builder и Delphi 2010, как объявлено, заточены для работы с юникодом. 
Из этого всего получается, что преобразовывать к локализации ansi мне не требуется, достаточно полученную юникодовскую строку подать, допустим, в мемо, а оно отобразит как надо. Или не так? 

P/s/ абракадабру я копипастнул в файл и открыл lister'ом, он отобразил русский текст если указать, что он в UTF-8.

Подскажите, пожалуйста, в чем может быть ошибка, или куда копать, я уже, пожалуй, все перепробывал((
Заранее, спасибо!

Автор: xvr 21.10.2010, 14:13
UTF8 это способ кодировки Unicode символов (которые сами по себе 2х байтовые) в виде потока байтов. 
Цитата(ipc @  21.10.2010,  13:37 Найти цитируемый пост)
utf8 на то и такая громоздкая, чтобы в ней можно было передавать все символы большиства языков.
Она не громоздкая. Чистый английский текст в UTF8 останется как был - чистым английским текстом.

Цитата(ipc @  21.10.2010,  13:37 Найти цитируемый пост)
 Builder и Delphi 2010, как объявлено, заточены для работы с юникодом. 
Угу, причем в родной, Unicode'овской кодировке (т.е. 2х байтовыми символами). UTF8 в эту кодировку надо переводить явно (как именно не знаю, нету у меня 2010 Билдера)


Автор: Alexeis 21.10.2010, 14:37
Билдер должен знать что к нему пришла строка в формате UTF8, только тогда он сможет корректно конвертировать ее в utf16 для отображения на windows контролах. 
  Для таких строк предусмотрен тип UTF8String. Строка как набор байтов это RawString . При копировании в RawString не осуществляются ни какие преобразования.

Автор: ipc 21.10.2010, 15:04
Спасибо, за такой быстрый ответ.

Т.е. получается (подведу вышесказанные ответы), что у меня приходит строка utf8, а в мемо отображается в utf16. И надо конвертировать как-то в utf16 потому как билдер не понимает, что в мемо подается строка utf8. Так?

Пробывал следующие конструкции : 

Код

UTF8ToUnicodeString(StrStreamInc->DataString)
UTF8ToUnicodeString(UTF8String(StrStreamInc->DataString))
UTF8ToUnicodeString(UTF8Encode(StrStreamInc->DataString))


не помогают((((( Как быть?

Автор: Alexeis 21.10.2010, 15:18
Цитата(ipc @  21.10.2010,  16:04 Найти цитируемый пост)
И надо конвертировать как-то в utf16 потому как билдер не понимает, что в мемо подается строка utf8. Так?

  Ничего не нужно конвертировать, он сам все умеет, важно, чтобы изначально получить строку в правильном формате. Скорее всего в строке utf16 хранятся данные не соответствующие контейнеру. Значит нужно создать контейнер правильного типа(UTF8String), зарезервировать необходимую память и побайтово скопировать туда (например при помощи memcpy). После этого все операции будут корректными.

Автор: ipc 21.10.2010, 15:55
Цитата(Alexeis @ 21.10.2010,  15:18)
Значит нужно создать контейнер правильного типа(UTF8String), зарезервировать необходимую память и побайтово скопировать туда (например при помощи memcpy).


Сделал следующее:

Код

UnicodeString srcStr = StrStreamInc->DataString;
UTF8String *dstStr = new UTF8String(srcStr.Length());
memcpy((void *)dstStr, &srcStr, srcStr.Length());


Результат не изменился..... Символы в контролле те же.

Автор: Alexeis 21.10.2010, 16:17
Для начала соответствуют ли тут типы? Если нет то произойдет конвертация с потерей информации 
Код

UnicodeString srcStr = StrStreamInc->DataString;

Во-вторых чеза?
Цитата(ipc @  21.10.2010,  16:55 Найти цитируемый пост)
UTF8String *dstStr = new UTF8String(srcStr.Length());

Код

    __fastcall AnsiStringBase(): Data(0) {}
    __fastcall AnsiStringBase(const AnsiStringBase& src);
    __fastcall AnsiStringBase(const char* src, int codePage);
    __fastcall AnsiStringBase(const char* src, int byteLen, int codePage);
    __fastcall AnsiStringBase(const wchar_t* src, int numwchar, int codePage);
    __fastcall AnsiStringBase(const char16_t* src, int numChar16, int codePage);
    __fastcall AnsiStringBase(const char32_t* src, int numChar32, int codePage);

    __fastcall AnsiStringBase(double src, int codePage);
    __fastcall AnsiStringBase(wchar_t src, int codePage);
    __fastcall AnsiStringBase(const WideString &src, int codePage);
    __fastcall AnsiStringBase(const UnicodeString &src, int codePage);
#if !defined(_DELPHI_STRING_UNICODE)
    __fastcall AnsiStringBase(const AnsiString &src, int /*codePage*/): Data(0) {

Где тут конструктор умеющий выделять длину?
Код

UTF8String dstStr;
dstStr.SetLength(srcStr.Length() * sizeof(wchar_t));

В третьих почему начало объекта должно совпадать с началом строки? И почему размер памяти равен длине строки?
Код

memcpy(&dstStr[1], &srcStr[1], srcStr.Length() * sizeof(wchar_t));


Автор: ipc 21.10.2010, 18:45
Да, типы соответствуют.

Alexeis, спасибо, все исправил. Теперь: 
Код

    UnicodeString srcStr = StrStreamInc->DataString;

    UTF8String dstStr;
    dstStr.SetLength(srcStr.Length() * sizeof(wchar_t));

    memcpy(&dstStr[1], &srcStr[1], srcStr.Length() * sizeof(wchar_t));

Результат изменился, но теперь вместо привычной абракадабры в несколько предложений выдало всего несколько символов " \ ?" и все.

Я что-то снова не то сделал?

Автор: oldcode 21.10.2010, 19:08
Код

L"ID=1");        
L"http://mysite.ru/catcher.php"

Автор: ipc 21.10.2010, 19:55
oldcode, не совсем понял, что ты хотел этим сказать)

Автор: Alexeis 21.10.2010, 20:20
ipc, если там текст английский, то utf8 совпадает с ansi и его должно быть видно в отладчике. Нужно проверить что в самой первой строке правильный текст.

Автор: ipc 22.10.2010, 10:03
Alexeis,сделал, чтобы возвращался смешанный текст (сначала пара слов английского, затем русский). 

Код

UnicodeString srcStr = StrStreamInc->DataString;

В отдладчике srcStr показывает эту пару слов английского, затем ту же абракадабру.
После memcpy dstStr равна первому символу из srcStr и символу конца строки.

Т.е. исходный текст: "English text. Русский текст".
srcStr: "English text. Р СѓСЃСЃРєРёР№ текст"
dstStr: "E\0"

Автор: Platov 22.10.2010, 10:15
Такой текст
Цитата(ipc @  22.10.2010,  10:03 Найти цитируемый пост)
"English text. Русский текст"


преобразуется туда и братно с помощью Utf8ToAnsi и AnsiToUtf8.

Автор: oldcode 22.10.2010, 10:15
http://www.codeproject.com/KB/string/utfConvert.aspx

Автор: ipc 22.10.2010, 11:33
Platov, Utf8ToAnsi я пробывал, как только появилась проблема. Эта функция возращает пустую строку((
К тому же, зачем мне в Ansi переводить, если контроллы в 2010 версии понимают в первую очередь юникод?

Автор: Platov 22.10.2010, 11:39
Цитата(ipc @  22.10.2010,  11:33 Найти цитируемый пост)
Utf8ToAnsi я пробывал, как только появилась проблема.

Попробуй!  smile 
Код

Caption=Utf8ToAnsi("English text. Русский текст");


У меня ведь работает. 

Автор: Alexeis 22.10.2010, 11:49
Хм... что-то похоже неверно с источником. Проверил у себя помещаю uft8 в UnicodeString получаю не читаемый английский текст. И это правильно, потому что он рассматривает 2 символа как 1.
Код

  String s;
  UTF8String u8str = UTF8Encode(WideString(L"english русский"));
  s.SetLength(u8str.Length());
  memcpy(&s[1], &u8str[1], u8str.Length());

u8str - "english СЂСѓСЃСЃРєРёР№"
s - { "湥汧獩⁨胑菑臑臑뫐룐말ȀText=\"Фон" }

Мои оба результата предсказуемы.
Это значит что в StrStreamInc->DataString уже пострадавший текст от конвертации Ansi -> utf16 ; Indy ожидал встретить Ansi и записал буфер как Ansi, после чего сам произвел конвертацию в юникод и тем самым испортил данные.

Можно попробовать следующий вариант. Убитый Utf8 в контейнере UTF16 сконвертить обратно в Ansi затем буфер данных Ansi скопировать в контейнер для UTF8. Но работать будет 50/50. Дело в том, что при порче данных запросто могут появиться символы не существующие в кодировке Ansi, поэтому при обратном преобразовании они заменяться на квадратики. Те же что имеют эквивалент восстановятся верно. 

  Вердикт. Использовать другой способ который даст доступ к исходному буферу и не произведет порчу данных. 

Автор: ipc 22.10.2010, 11:50
ЕСТЬ!!!! 
Всем СПАСИБО, огромнейшее!!! 
Alexeis, в частности, за терпение и разъяснение.
oldcode, очень полезная ссылка, решила проблему в раз!

Добавлено через 4 минуты и 29 секунд
Еще раз всем спасибо ребят Platov, Alexeis, oldcode.

Автор: oldcode 22.10.2010, 13:55
Цитата(ipc @ 22.10.2010,  11:50)
ссылка, решила проблему в раз!

покажи, как переписАл на ВСВ, а то  не вникал smile

Автор: ipc 22.10.2010, 15:57
Вот. Правда без Utf8ToAnsi все же не обошлось, а жаль, чувствую, это еще выйдет мне боком при парсинге.

Код

UnicodeString UTF8toUTF16(UnicodeString usUTF8){
    UnicodeString usUTF16 = "";

    int iLength = MultiByteToWideChar(CP_UTF8, 0, usUTF8.t_str(), -1, NULL, 0);
    if (iLength > 1){
        usUTF16.SetLength(usUTF8.Length() * sizeof(wchar_t));
        Utf8ToAnsi(MultiByteToWideChar(CP_UTF8, 0, usUTF8.t_str(), -1, &usUTF16[1], iLength));
    }
    return usUTF16;
}

Автор: 0x00 26.10.2010, 15:12
все не читал но скорее всего проблема на стороне скрипта.
там надо чтонибудь типа (Java) request.setCharacterEncoding("UTF-8"); поставить

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