Материалы предоставлены
автором блога «Переводы от GunSmoker«
Небольшая аннотация к статье.
Думаю, что большинство разработчиков в процессе работы с web-страницами сталкивались с такой проблемой — определение кодировки страницы и перевод всего контента web-страницы в необходимую для дальнейшей работы кодировку.
Обычно вопросы, встречающиеся на форумах, по проблеме кодировок пестрят такими словосочетаниями как «кракозябры вместо русских букв» и т.д. Вариантов решения этой проблемы несколько, в том числе и вариант, предлагаемый Александром безусловно может и должен использоваться при работе с кодировками, а именно — использование в Delphi возможностей, предоставляемых библиотекой mlang.dll.
1. Введение в работу с MLang.
Во-первых, следует упомянуть, что библиотека mlang.dll присутствует во всех версиях windows, включая windows 95 с установленным Internet Explorer. Поэтому, использование этой бибилиотеки в Ваших приложениях не должно привести к каким-либо проблемам, необходимости распространения библиотеки вместе с приложением и т.д.
Судя по описанию на msdn.com основным назначением MLang является:
помощь разработчикам в решении проблем, возникающих в международных масштабах Интернета. Эти инструменты позволяют разработчику иметь дело с целым рядом международных кодировок…
Билиотека MLang, в числе прочих своих возможностей, позволяет нам:
- Перечислять кодовые страницы из базы MIME
- Преобразовывать кодировку текста
- Определять в какой кодировке находится текст
- и ещё много-много разных возможностей.
Чтобы воспользоваться воможностями библиотеки Mlang в Delphi нам потребуется заголовочный файл, ссылка на который находится в конце статьи. В этом файле определены необходимые типы данных для работы с библиотекой, перечислены интерфейсы и т.д. Давайте попробуем написать простенький пример, реализующий минимальные возможности библиотеки при работе с кодовыми страницами.
2. Пример реализации возможностей MLang в Delphi
Для начала, приведем пример перечисления всех кодовых страниц, имеющихся в базе MIME.
Создаем новое приложение и бросаем на главную форму всего один компонент — Memo.
В uses подключаем следующие модули: MLang, ActiveX, ComObj.
Теперь, в обрабочике onCreate формы определим следующие переменные:
var MultiLang: IMultiLanguage; ECP: IEnumCodePage; CPI: tagMIMECPINFO; Fetched: Cardinal;
Краткое описание переменных:
- IMultiLanguage — это интерфейс, который собирает информацию о наборах символов, кодовых страницах или локалях из базы данных MIME и преобразует строки из одной кодировки в другую.
- IEnumCodePage — интерфейс, с помощью которого можно получить информацию о кодовых страницах или определить, какие кодовые страницы допускаются при работе.
- tagMIMECPINFO — структура, содержащая детальную информацию о кодовой странице.
Теперь, создаем необходимые объекты:
... begin MultiLang:=CoCMultiLanguage.Create as IMultiLanguage; FillChar(CPI, SizeOf(CPI), 0); Fetched := 0; OleCheck(MultiLang.EnumCodePages(MIMECONTF_BROWSER, ECP)); ...
Обратите внимание на то, как создается ECP. Метод EnumCodePages создает объект ECP (IEnumCodePage), используя один из доступных флагов, которые используются для контроля перечисления кодовых страниц. В нашем случае — это флаг MIMECONTF_BROWSER т.е. перечисляем кодовые страницы предназначенные для отображения в браузере клиента. Также, Вы можете использовать ряд других флагов при перечислении доступных кодировок, например, MIMECONTF_MAILNEWS — кодовые страницы предназначенные для отображения в почтовых и новостных клиентах и т.д. Все доступные флаги определены в модуле MLang.pas.
Теперь перечислим все доступные кодировки и занесем их в Memo1:
... while Succeeded(ECP.Next(1, CPI, Fetched)) do begin if Fetched <= 0 then Break; Memo1.Lines.Add(CPI.wszWebCharset) end; end;
Метод Next у IEnumCodePage возвращает массив структур tagMIMECPINFO для указанного количества кодовых страниц. Параметры метода:
function Next(celt: Cardinal; out rgelt: tagMIMECPINFO; out pceltFetched: Cardinal): HResult; stdcall;
- celt — определяет количество элементов, которые будут возвращены в rgelt
- rgelt — указатель на массив, в котором возвращаются структуры tagMIMECPINFO
- pceltFetched — указатель на целое число, определяет количество структур tagMIMECPINFO фактически вернувшиеся в rgelt. Возвращаемое значение может быть меньше значения, заданного в параметре celt.
Теперь, что касается непосредственно структуры tagMIMECPINFO. Вот её описание в Delphi:
tagMIMECPINFO = packed record dwFlags: Cardinal; uiCodePage: SYSUINT; uiFamilyCodePage: SYSUINT; wszDescription: array[0..63] of WideChar; wszWebCharset: array[0..49] of WideChar; wszHeaderCharset: array[0..49] of WideChar; wszBodyCharset: array[0..49] of WideChar; wszFixedWidthFont: array[0..31] of WideChar; wszProportionalFont: array[0..31] of WideChar; bGDICharset: Byte; end;
dwFlags — сочетание значений, определенных в MIMECONTF или 0x0000.
uiCodePage — идентификатор кодовой страницы, который подходит к поддержке определения национальных языков (NLS).
uiFamilyCodePage — кодовая страница Microsoft Windows, с которой связан uiCodePage, а также атрибуты wszFixedWidthFont и wszProportionalFont. Если не определена в базе данных, то этот элемент имеет то же значение как и uiCodePage.
wszDescription — описание для uiCodePage.
wszWebCharset — имя набора символов соответствующих uiCodePage в форме, которую можно использовать с браузерами, например windows-1251, utf-8 и т.д.. Если нет в базе данных MIME, этот элемент имеет то же значение как и wszBodyCharset.
wszHeaderCharset — имя набора символов, которое соответствует uiCodePage и которое может быть использовано почтовым агентом в тегах заголовков. Если нет в базе данных, этот элемент имеет то же значение, что и wszBodyCharset.
wszBodyCharset — имя наборов символов, которое может быть использовано в тела почтового сообщения. Если нет в базе данных, этот элемент возвращает NULL.
wszFixedWidthFont — шрифт по умолчанию, который будет использоваться для моноширинного шрифта. Браузер может использовать это имя, чтобы выбрать шрифт для элемента текстового поля.
wszProportionalFont — шрифт, который будет использоваться по умолчанию для пропорциональных шрифтов. Браузер может использовать это имя, чтобы выбрать шрифт для элемента текста.
bGDICharset — Windows представление набора символов, который соответствует uiCodePage.
В приведенном выше листинге мы выводим в Memo список кодировок для браузера, т.е. те значения, которые обычно содержаться в мета-теге web-страницы или передаются в заголовках.
Теперь, можете поэкспериментировать с различными полями структуры, а мы перейдем непосредственно к предложению Александра по работе с кодировками web-станиц.
3. Работа с кодировками Web-страниц. Действие MLang в Delphi.
Александр предоставляет нам с Вами в использование ещё один модуль HtmlCPConvert, который содержит высокоуровневую обёртку над MLang. В модуле определены следующие методы:
CharsetNameToCharset — переводит содержимое мета-тега charset в CPID:Cardinal, годный для передачи в метод MultiByteToWideChar (содержится в kernel32.dll, модуль windows.pas)
RawHTMLToHTML — получает из web-страницы мета-тег с описанием кодировки, вызывает CharsetNameToCharset, после чего конвертирует весь буфер в WideString для получения читабельного текста.
HTMLToRawHTML — функция с действием противоположным RawHTMLToHTML, которая по подготовленному HTML-коду в unicode (WideString) делает HTML в нужной кодировке (кодировка снова берётся из мета-тэга «charset»).
Использование приведенных выше функций имеет ряд очевидных преисуществ перед теми методами, которые рассматривались в том числе и в моем блоге. Обычно мы ограничиваемся фиксированным набором кодировок с которыми собираемся работать в своей программе, например win-1251 и utf-8, не учитывая при этом такие кодировки как KOI-8R, которые также встречаются на web-страницах Интернета. Предлагаемый же способ дает нам возможность использовать в работе сразу всю базу MIME для работы с web-страницами, не ограничиваясь при этом только латиницей или кирилицей — достаточно вывести весь набор кодировок, поддерживаемых браузеров, чтобы прикинуть возможности использования модуля HtmlCPConvert.
Так что, надеюсь, что предоставленная Александром информация найдет широкое применения в Ваших проектах, в т.ч. и при разработке различного рода парсеров.
Ну и, конечно же, сами модули MLang.pas и HtmlCPConvert.pas в одном архиве:
Попробовал, при преобразовании строки из UTF-8 в win-1251 всесто русских символов получил вопросики. может есть особенности использования? приведите пример пожалуйста
Думал ошибка есть какая-нибудь в HtmlCPConvert. Нету. Все конвертится прекрасно. Дайте пожалуйста адрес странички, которую пробовали конвертировать и листинг кода как Вы конвертировали. А я пока примерчик набросаю в виде поста отдельного — думаю, что многим пригодится
в ридере упала рассылка с блога, думаю мой пример уже лишний, почитаю сейчас статью, если не получится напишу уж тогда
ок. Но то, что у вас вопросы появились вместо русских букв — это странно. По идее должны были быть кракозябры всякие, но никак не ???
Подскажите пожалуйста, как установить модули MLang и HtmlCPConvert.
Пытаюсь поставить на Делфи 6 — выдает ошибку — не может найти Etypes.dcu.
Или обязательно нужна девятая версия?
Etypes.dcu можно спокойно удалить из uses — это вспомогательный модуль, который использовал автор модуля в своем проекте.
спасибо, статья информативна и помогла мне;)
А как быть если кодировка в заголовке возвращаемого файла отличается от кодировки в мета файла?
У MLang есть автоопределение кодировки. Только в этом случае Вам надо немного на MSDN почитать про ту возможность.
Респект. Сейчас попробовал, заработало. А-то до этого на utf-8 появлялись кракозябры…
Всегда рад помочь :)
Здравствуйте! Не могли бы вы на конкретном примере показать как можно получать, например, TITLE или содержимые meta-keywords страниц в разных кодировках? было бы отлично.
http://www.webdelphi.ru/2011/03/chastichnoe-skachivanie-web-stranichki-s-pomoshhyu-synapse/
Конкретный пример вытаскивания title из кода странички
Возможно вы не правильно меня поняли. Я имею ввиду, что допустим я должен спарсить Title какой то страницы, адрес этой страниц и соответственно кодировка заранее не известны. То есть соль в том, чтобы разобраться как распознавать кодировку на странице и получать все, как есть на самом деле, а не ??? и крякозябры.
P.S. Просьба не бить, тухлые помидоры не кидать и не направлять в поиск.
>>P.S. Просьба не бить, тухлые помидоры не кидать и не направлять в поиск.
да как бы я редко кого пинаю в принципе :) Ок. Теперь более-менее понятно, что имелось в виду. Попробую помочь в следующем посте :)
RawHTMLToHTML не желает раскодировать сайт
http://oz.by/
хотя он в кодировке KOI8-R.
Может кто подскажет как его зарусить :)
Bazar, надеюсь Влад скоро расскажет как разобраться со всеми кодировками и сайтами, а не только с вашим (с) )
я уже понял что эта функция только для UTF-8.
спасибо Влад большое за статьи.
я уже не одно использовал из них :)
Да не за что :)
HtmlCPConvert выдает
неизвестный итендификатор UTF8ToUnicodeString(ARawHTML)
Может ETypes.dcu нужен?
ETypes.dcu можно смело вычеркивать из uses — этот модуль ни относился к работе с кодировками и содержал вспомогательные функции. ARAwHTML что содержит?
У меня Delphi 7 Enterprise Скачал и скопировал файлы HtmlCPConvert.pas MLang.pas в папку Lib. Подключаю их, возникает ошибка не найден ETypes.dcu. Нашел его в инете, кинул также в Lib. mlang перестал ругаться. Зато ругается и очень сильно HtmlCPConvert, вываливает кучу ошибок: [Error] HtmlCPConvert.pas(159): Undeclared identifier: ‘UTF8ToUnicodeString’ [Error] HtmlCPConvert.pas(186): Undeclared identifier: ‘IS_TEXT_UNICODE_ASCII16’ [Error] HtmlCPConvert.pas(187): Undeclared identifier: ‘IS_TEXT_UNICODE_REVERSE_ASCII16’ [Warning] HtmlCPConvert.pas(187): Combining signed and unsigned types — widened both operands [Error] HtmlCPConvert.pas(188): Undeclared identifier: ‘IS_TEXT_UNICODE_STATISTICS’ [Error] HtmlCPConvert.pas(189): Undeclared identifier: ‘IS_TEXT_UNICODE_REVERSE_STATISTICS’ [Error] HtmlCPConvert.pas(190): Undeclared identifier: ‘IS_TEXT_UNICODE_CONTROLS’ [Error] HtmlCPConvert.pas(191): Undeclared identifier: ‘IS_TEXT_UNICODE_REVERSE_CONTROLS’ [Error] HtmlCPConvert.pas(192): Undeclared identifier: ‘IS_TEXT_UNICODE_SIGNATURE’ [Error] HtmlCPConvert.pas(193): Undeclared identifier: ‘IS_TEXT_UNICODE_REVERSE_SIGNATURE’ [Error] HtmlCPConvert.pas(194): Undeclared… Подробнее »
HtmlCPConvert — это модуль-обёртка над MLang.pas. Писался модуль под версию D2009-2010, работает и в XE. Ваша версия Delphi в принципе не знает, что такое Unicode, RawByteString и т.д. Константы IS_TEXT_UNICODE_… — лежат в модуле Windows.pas.
Так что ваш вариант использования MLang — либо писать свою обёртку под D7, либо разбираться с тем как работает MLang и работать без всяких обёрток — ничего сложного в этом нет.
[…] с библиотекой MLang, о которой писал Vlad в своем блоге. По описанию выглядит привлекательно, единственный […]
А как быть с такой строкой — %D0%A4%D1%91%D0%B4%D0%BE%D1%80%D0%BE%D0%B2%D0%BA%D0%B0_(%D0%92%D0%BE%D0%BB%D1%8B%D0%BD%D1%81%D0%BA%D0%B0%D1%8F_%D0%BE%D0%B1%D0%BB%D0%B0%D1%81%D1%82%D1%8C)