уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

Прошло больше года с момента первой публикации статьи про “MLang в Delphi”, которая была написана по материалам, предоставленным Александром – автором блога “БЛОГ GUNSMOKER-А” и, как я понимаю, ныне координатором русского Blaise Pascal Magazine. Статья эта до сих пор пользуется успехом у посетителей, которые хотят научиться работать с кодировками веб-страниц, однако, далеко не всегда статья читается целиком, включая комментарии как мои, так и других читателей – это обычное дело и в результате возникают всякого рода проблемы при использовании модулей для MLand в Delphi. Самая частая из которых – отсутствие поддержки версий Delphi ниже 2009. Поэтому на днях Александр прислал мне обновленную версию MLang для Delphi вплоть до Delphi 4. Ну, а я выкладываю его творение у себя в блоге с небольших рерайтом уже имеющейся статьи по MLang. Повторяться о том, что такое MLang и для чего он нужен я не буду – эту информацию Вы сможете получить из предыдущей публикации на тему. А перейду сразу к рассмотрению того, что добавлено и примера использования новых модулей для работы с кодировками в Delphi 4 – XE.

Новые возможности

Названия модулей. Теперь в состав входят следующие модули:

MLang.pas – заголовочник для MLang

Encodings.pas – модуль для работы с кодировками, позволяющий работать с TEncoding в старых версиях Delphi. Также модуль включает в себя такие “вкусности” для обладателей старых версий Delphi как:

{Определение кодировки текстового файла}
function GetFileEncoding(const AFileName: String): TEncoding;
{Перевод строки в массив байтов}
function StringToBytes(const AString: RawByteString): TBytes;
{Перевод массива байтов в строку}
function BytesToString(const ABytes: TBytes): RawByteString;
{Перевод строки в текст с определенной кодировкой и обратно}
function StringToText(const AString: RawByteString; AEncoding: TEncoding = nil): String;
function TextToString(const AString: String; AEncoding: TEncoding = nil): RawByteString;
{Получение текста из файла с определенной кодировкой и наоборот}
function FileToText(const FileName: String; AEncoding: TEncoding = nil): String;
procedure TextToFile(const FileName: string; const Contents: String; AEncoding: TEncoding = nil; Append: Boolean = False);
{Загрузка списка строк из файла в определенной кодировке и наоборот}
procedure LoadStringsFromFile(const AStrings: TStrings; const AFileName: String; const AEncoding: TEncoding = nil);
procedure SaveStringsToFile(const AStrings: TStrings; const AFileName: String; const AEncoding: TEncoding = nil);

Большинство из этих функций давно известна пользователям той же Delphi XE, а для пользователей, скажем Delphi 7 эти функции окажутся очень полезными в работе.

Этот же модуль содержит и уже бывшие ранее функции по преобразованию кодировок web-страниц:

function RawHTMLToHTML(const ARawHTML: RawByteString; const ACharset: Cardinal = 0): UnicodeString;
function HTMLToRawHTML(const AHTML: UnicodeString; const ACharset: Cardinal = 0): RawByteString;

И, наконец, добавлены аналогичные функции для работы с кодировками в XML-файлах:

function RawXMLToXML(const ARawXML: RawByteString; const ACharset: Cardinal = 0): UnicodeString;
function XMLToRawXML(const AXML: UnicodeString; const ACharset: Cardinal = 0): RawByteString;

 

Основное изменение в новых исходниках — конвертация строки. В старых исподниках использовался метод MultiByteToWideChar, который работает только для установленных в системе кодовых страниц, что не всегда приводит к положительному результату работы.

Compatibility.pas – модуль для обеспечения совместимость со старыми версиями Delphi.

Теперь перейдем к рассмотрению примера работы с MLang.

Пример использования MLang в Delphi

Александр накидал примерчик использования новых модулей, который использует в работе Indy, поэтому если Вы используете Delphi ниже 6 версии (не удивлюсь, если окажется, что так и есть), то помните, что Indy там нету по умолчанию.

Итак, форма приложения выглядит следующим образом:

encodings

Приложение, использующее MLang, позволяет:

  1. Получить страницу, используя кодировку сервера (из заголовков)
  2. Получить страницу, используя кодировку страницы (по мета-тегу)
  3. Получить страницу, попробовав угадать кодировку текста, игнорируя и заголовки и мета-теги
  4. Получить страницу и преобразовать кодировку, указав кодовую страницу вручную.

По-моему примеров более, чем достаточно, чтобы выбрать для себя подходящий вариант работы с кодировкой веб-страницы. Пройдемся по всем функциям программы по порядку.

Получение web-страницы с использованием кодировки сервера

Функция в программе выглядит следующим образом:

procedure TfmMain.btGetByServerContentTypeClick(Sender: TObject);
var
  Page: RawByteString;//текст страницы
  ServerEncoding: String; //кодировка серверв
  CharsetName: String; //имя кодировки
  Charset: Cardinal;//кодовая страница
begin
{загрузили страницу}
  Page := LoadPage(edURL.Text, ServerEncoding);
{получили название и номер кодовой страницы}
  CharsetName := ExtractCharsetName(mlHTML, ServerEncoding, True);
  Charset := CharsetNameToCharset(CharsetName);
{выписали в Memo результаты определения}
  mmResult.Lines.Text :=
    'Кодировка (имя): ' + CharsetName + sLineBreak +
    'Кодировка (код): ' + IntToStr(Charset) + sLineBreak +
    'Страница: ' + sLineBreak +
    sLineBreak +
    RawHTMLToHTML(Page, Charset);//преобразовали текст страницы
end;

Функция LoadPage, используя Indy (IdHTTP) загружает контент страницы в RawByteString (строку без определенной кодировки):

function LoadPage(const AURL: String; out AServerEncoding: String): RawByteString; overload;
var
  HTTP: TIdHTTP;
  Strs: TMemoryStream;
begin
  AServerEncoding := '';
  HTTP := TIdHTTP.Create(nil);
  try
    {Грузим контент}
    Strs := TMemoryStream.Create;
    try
      HTTP.HandleRedirects := True;
      HTTP.Get(AURL, Strs);
      SetLength(Result, Strs.Size);
      Move(Strs.Memory^, Pointer(Result)^, Strs.Size);
    finally
      FreeAndNil(Strs);
    end;
    // Получаем строку для передачи в функцию поиска кодировки
    AServerEncoding := 'charset=' + HTTP.Response.CharSet;
  finally
    FreeAndNil(HTTP);
  end;
end;

ExtractCharsetName — извлекает из строки имя кодировки:

function ExtractCharsetName(const AType: TMarkupLanguage; const AHTML: String; const AContentTypeOnly: Boolean): String;

Здесь: AType: TMarkupLanguage — флаг, определяющий из какого файла мы получаем кодировку. Может принимать значения:

  • mlInvalid — тип не определен (например, когда работаем с текстовых файлом)
  • mlHTML — HTML-код
  • mlXML — XML-код Функция определена в Encoding.pas. CharsetNameToCharset — По имени кодовой страницы получает её номер.

function CharsetNameToCharset(const ACharsetName: String): Cardinal;

Функция определена в Encoding.pas. RawHTMLToHTML — проводит преобразование текста по заданной кодовой странице. С этой функцией мы уже знакомы:

function RawHTMLToHTML(const ARawHTML: RawByteString; const ACharset: Cardinal): UnicodeString;

Получение web-страницы с использованием кодировки, определенной в мета-теге

Работа в данном случае выглядит на 99% также как и в предыдущем случае, за исключением способа получения названия кодовой страницы. Здесь получение значение для переменной CharsetName выглядит следующим образом:

CharsetName := ExtractCharsetName(mlHTML, Page);

В остальном весь код тот же.

Получение web-страницы с автоопределением кодовой страницы

Здесь мы уже не трогаем ни заголовки, ни мета-тега, а соответственно и вид обработчика несколько изменяется:

procedure TfmMain.btGetByAutoDetectClick(Sender: TObject);
var
  Page: RawByteString;
  CharsetName: String;
  Charset: Cardinal;
begin
  Page := LoadPage(edURL.Text);
 
  CharsetName := '???';
  Charset := DetectHTMLCharset(Page);//угадываем кодировку
 
  mmResult.Lines.Text :=
    'Кодировка (имя): ' + CharsetName + sLineBreak +
    'Кодировка (код): ' + IntToStr(Charset) + sLineBreak +
    'Страница: ' + sLineBreak +
    sLineBreak +
    RawHTMLToHTML(Page, Charset);
end;

Всё, в принципе, то же, за исключением замечательной функции DetectHTMLCharset:

function DetectHTMLCharset(const AText: RawByteString): Cardinal;

Эта функция, получив в качестве входного параметра текст со страницы и, используя интерфейс IMultiLanguage2 из MLang пробует «угадать» номер кодовой страницы в которой приведен текст. Здесь без разницы, что содержит AText — мета-теги с кодировкой, XML-код или строку  — функция «угадывает» кодировку по всему объему AText. Проверил работоспособность функции на трех сайтах с разными кодировками: windows-1251, utf-8, KOI8-R — ошибок в работе не обнаружено, текст конвертируется правильно.

Получение web-страницы с указанием собственной кодировки

Этот вариант работы для самых “отчаянных“ программистов – вместо определение кодировки вы её указываете вручную, используя номер или название кодовой страницы и вызываете метод RawHTMLToHTML для преобразования.

На этом обзор новых возможностей модулей для работы с MLang в Delphi 4-XE закокнчен. Исходники обновленных модулей можно скачать по ссылке ниже:

[download id=»90″ format=»1″]

Не забудьте сказать “Спасибо” Александру – это всё его работа, а я только сделал небольшой обзор в блоге и немного потестировал модули.

Книжная полка

Описание: Рассмотрены практические вопросы по разработке клиент-серверных приложений в среде Delphi 7 и Delphi 2005 с использованием СУБД MS SQL Server 2000, InterBase и Firebird. Приведена информация о теории построения реляционных баз данных и языке SQL. Освещены вопросы эксплуатации и администрирования СУБД.
купить книгу delphi на ЛитРес
Описание: Рассмотрены малоосвещенные вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные режимы их работы, особенности для протоколов TCP и UDP и др.
купить книгу delphi на ЛитРес
0 0 голоса
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
2 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
Pcrepair
Pcrepair
18/01/2013 02:14

а вот тут косяки:
модуль Encodings
(* 17.01.2013 удалено из за сбоя на http://copypast.ru/2012/07/31/japonskie_shkolnicy.htm*)
//OleCheck(MultiLang.DetectInputCodepage(MLDETECTCP_HTML, 0, PAnsiChar(AText), Len, DE, Scores));
и на других УРЛ это непонятно для чего установленное исключение срабатывает, пишет «Неопознанная Ошибка»

Тема
Тема
19/02/2015 21:48

Подключил encodings как в примере:

implementation
uses
Encodings,
Compatibility;
{$I jedi.inc}
{$R *.dfm}

но на старой Delphi всё равно не работает
S.SaveToFile(‘1.txt’,Encoding.UTF8); — too many actual parameters
Что я не так делаю?