Добавлено: Чт, 9 июня 2016, 08:25:05
Кодировки
TEencoding, RawBytesStrng, TBytes.
http://www.webdelphi.ru/2010/04/3-varianta-raboty-s-kodirovkami-veb-stranic-v-delphi/
TEencoding, RawBytesStrng, TBytes.
http://www.webdelphi.ru/2010/04/3-varianta-raboty-s-kodirovkami-veb-stranic-v-delphi/
- Спойлер
- 3 варианта работы с кодировками веб-страниц в Delphi.
03/04/2010
Vlad
10 Комментарии
Delphi в Web
Сколько постов написано в блогах, сколько вопросов задано на форумах о кодировках и ещё большее количество подобных вопросов осталось не отвеченными или ответом было что-то наподобие «Поиском пользовались?» или «Сто раз обсуждалось!!!». Честно говоря, никогда не понимал таких «ответчиков», ИМХО не хочешь отвечать — лучше жуй и молчи, ответят те, кто захочет.
Понятное дело, что обучать иногда приходится не только с помощью пряника, но и с помощью кнута, но, раз уж такие вопросы всё время всплывают, следовательно они остаются актуальными.
Сегодня я попробую рассказать Вам всё, что мне известно о работе с кодировками в тексте. Вполне возможно, что эта статья поможет Вам при разработке своих проектов в Delphi да и у меня уже пару раз возникало желание немного систематизировать ту информацию. которая накопилась за время существования блога.
1. Delphi и Unicode
Если говорить о работе с Unicode в Delphi, то начать стоит с того, что полноценная поддержка unicode в Delphi стала возможна лишь после выхода Delphi 2009. Такое событие не могло пройти незамеченным, так как благодаря поддержке Unicode и, соответственно, для облегчения работы с кодировками текста в Delphi были реализованы новые возможности, методы, типы данных, о которых написано большое количество статей. На мой взгляд одной из лучших публикаций на эту темя является цикл из трех статей «Delphi и кодировка Unicode» где достаточно чётко и доступно рассказано о нововведениях Delphi 2009 для работы с unicod’ом. Думаю, что нет смысла подробно останавливаться на всех новшествах при наличии ссылки на целых три статьи на эту тему. Остается только упомянуть о том, с чем мы сегодня будем работать для представления веб-страницы в нормальном читаемом виде.
Для первого способа работы с кодировкой мы воспользуемся:
1.Класс TEncoding, который и дает нам возможность без лишних хлопот работать с кодировками
2. Тип данных TBytes — массива байтов строк
3.RawByteString — тип для передачи строковых данных для любой кодовой страницы без каких-либо преобразований последней.
В одной из статей блога рассматривалась работа с MLang и сегодня, в качестве второго способа, я продемонстрирую Вам пример её применения при работе с кодировками.
Ну и в качестве третьего способа работы с кодировками, воспользуемся «штатными» методами модуля system. Все три варианта работы с кодировками приведут к одному и тому же результату — текст веб-страницы будет читаемым, без «кракозябров» и вопросительных знаков. Какой способ лучше — решать только Вам.
2. Подготовка исходных данных для работы
Прежде, чем начать что-то кодировать и перекодировать, необходимо это «что-то» получить. В нашем случае — текст веб-страницы. Чтобы не залезать слишком глубоко в вопросы, касающиеся новых типов данных будем сохранять все данные из Сети не в строки или TStringList’ы, как мы к этому привыкли, а воспользуемся типом TBytes.
Используя Synapse исходник любой страницы можно получить, например так: with THTTPSend.Create do
begin
if HTTPMethod('GET',URL)then
begin
//в свойстве Document объекта THTTPSend содержится исходник страницы
//обрабатываем Document
end;
end;
Теперь, получив данные (свойство Document:TMemoryStream) скопируем эти данные в массив байтов строки TBytes, например так (способ достаточно медленный, приведен исключительно для примера):var B: TBytes;
i:integer;
begin
//загружаем контент страницы
with THTTPSend.Create do
begin
if HTTPMethod('GET',URL) then
begin
Document.Position:=0;
SetLength(B,Document.Size);
for i := 0 to Document.Size - 1 do
Document.Read(B[i],1);
end;
end;
Первый этап подготовки можно считать законченным — у нас есть массив байтов строки, которые необходимо представить в виде текста в правильной кодировке. А как узнать, что наш массив TBytes надо перевести в новую «правильную» кодировку? Естественно узнать в какой кодировке была исходная страница. Здесь можно пойти двумя путями:
1. Узнать кодировку из мета-тегов. Обычно кодировка страницы описывается мета-тегом следующего содержания:META http-equiv="content-type" content="text/html; charset=windows-1251
. Пример того как узнать кодировку из мета-тегов страницы Вы можете посмотреть в модуле HtmlCPConvert, который я выкладывал в блоге, когда рассматривал работу с MLang.
2. Узнать кодировку текста из заголовков сервера. Если рассматривать этот пример, используя для работу с HTTP-протоколом библиотеку Synapse, то заголовки сервера будут содержаться в свойстве Headers:TStringList. Найти элемент, содержащий сведения о кодировке и получить имя кодовой страницы можно, например так:function GetCharset(Headers: TStringList): string;
var i:integer;
begin
if Headers.Count=0 then Exit;
for I := 0 to Headers.Count - 1 do
begin
//Content-Type: text/html; charset=UTF-8
if Pos('content-type',LowerCase(Headers[i]))>0 then
if pos('=',Headers[i])>0 then
Result:=LowerCase(Copy(Headers[i],pos('=',Headers[i])+1,Length(Headers[i])-pos('=',Headers[i])))
else
Result:=DefCharset;
end;
end;
Так как в заголовке может отсутствовать сведения о кодировке, то переменная DefCharset должна содержать имя какой-либо дефолтной кодовой страницы, например windows-1251.
Теперь у нас есть все исходные данные:
1. Массив байтов строки B:TBytes;
2. Сведения о кодировке.
Приступим к преобразованию байтов в читабельный текст.
3. Массив байтов — в нормальный текст
3.1. Работа с TEncoding
Разработчики Delphi предусмотрели использование двух взаимопротивоположных метода:
1. function BytesOf(const Val: RawByteString): TBytes; — получение байтов из строки
2. function StringOf(const Bytes: TBytes): UnicodeString; — получение строки из массива байтов
Пусть Вас не пугает то, что StringOf возвращает UnicodeString, т.к., начиная с Delphi 2009
type
UnicodeString = string
Теперь, что касается непосредственно работы с TEncoding. Во-первых, от нас не требуется создавать отдельный экземпляр класса, наподобие такого:var Enc: TEncoding;
begin
Enc:=TEncoding.Create;
end;
Во-вторых, с помощью TEncoding мы можем менять кодовую страницу не преобразовывая массив байтов в строку. Для этого класс содержит следующие классовые методы:class function Convert(Source, Destination: TEncoding; Bytes: TBytes): TBytes; overload;
class function Convert(Source, Destination: TEncoding; Bytes: TBytes; StartIndex, Count: Integer): TBytes; overload;
Одним из этих методов мы и воспользуемся. Для того, чтобы продемонстрировать работу с TEncoding попробуем получить текст прямо из этого блога. Кодировка UTF-8, именно она наиболее часто является «проблемной» и возвращает в TMemo или ещё куда-либо «кракозябры».
Создадим простое приложение как показано на рисунке:
В ComboBox сразу занесем все варианты работы с текстом:
1. Без преобразования
2. TEncoding (модуль SysUtils)
3. MLang
4. Utf8ToAnsi (модуль System)
По нажатию кнопки TButton будем грузить страницу в массив байтов и, зависимости от выбранного варианта работы с текстом, преобразовывать массив в строку и выводить в Memo. Для первого варианта (без преобразования) код может быть следующий:with THTTPSend.Create do
begin
if HTTPMethod('GET',Edit1.Text)then
begin
Document.Position:=0;
SetLength(B,Document.Size);
for i := 0 to Document.Size - 1 do
Document.Read(B[i],1);
label4.Caption:=GetCharset(Headers);
end;
end;
Label6.Caption:=IntToStr(Length(B))+' байт текста';
case ComboBox1.ItemIndex of
0:Memo1.Text:=StringOf(B);
[...]
end;
Запускаем приложение и видим:
Как и ожидалось — вместо русских букв кракозябры. Преобразуем их с помощью классового метода TEncoding.Convert:var astr: String;
B: TBytes;
[...]
case ComboBox1.ItemIndex of
0:Memo1.Text:=StringOf(B);
1:begin
B:=TEncoding.Convert(TEncoding.UTF8,TEncoding.GetEncoding(1251),B);
astr:=StringOf(B);
Memo1.Lines.Add(astr);
end;
[...]
end;
Здесь следует отметить, что TEncoding.GetEncoding(1251) возвращает кодовую страницу кириллического текста. Если необходимо получить другую кодовую страницу, то можете либо воспользоваться классовыми свойствами TEncoding либо определить кодовую страницу как я, используя данные с MSDN о доступных в Windows кодовых страницах.
Проверим, что получилось в итоге. Грузим ту же самую страницу:
Как видите — кракозябры преобразовались в нормальный русский текст.
Какие плюсы дает нам использование TEncoding? Думаю, что ответ вполне очевиден — у нас появляется под рукой инструмент, позволяющий перекодировать строки из любых кодировок в любые, т.е. фактически возможности работы с текстом ограничиваются количеством доступных в Windows кодовых страниц, а их по данным MSDN 152 штуки. Прикиньте сколько вариантов получится, если в Convert используется пара кодовых страниц. Много .
Но, наряду с таким большим и жирным плюсом существует и минус — TEncoding есть только в версиях Delphi 2009 и выше, что исключает его использование в более ранних версиях. Как быть?
Если хотите получить не менее впечатляющие возможности работы с кодировками — используйте возможности MLang. Вот как раз и пример его использования.
3.2. Использование возможностей MLang для работы с кодовыми страницами
Позволю себе ещё раз напомнить, что скачать всё необходимое для реализации возможностей MLang Вы можете с этой страницы или, перейдя по этой ссылке. В архиве содержатся всего два модуля: MLang.pas и HtmlCPConvert.pas. Подключаем оба модуля к проекту и для получения читабельного текста пишем:
Memo1.Text:=RawHTMLToHTML(StringOf(B));
RawHTMLToHTML из модуля HtmlCPConvert переводит текст в кодировку windows-1251. Также есть и обратный метод для перевода текста в RawByteString и называется этот метод HTMLToRawHTML.
Результат преобразования текста с помощью MLang абсолютно идентичен предыдущему варианту:
Если Вам необходимо работать со множеством кодировок, как при использовании TEncoding, то придётся самостоятельно немного доработать модуль HtmlCPConvert. В целом возможности TEncoding и MLang вполне сопоставимы.
И, наконец, третий вариант — использование методов модуля System.pas.
3.3. Использование методов System.pas для перекодирования текста
В модуле System.pas можно найти следующие полезные функции для работы с кодировками:Utf8Encode(const US: UnicodeString): RawByteString;
function UTF8EncodeToShortString(const WS: WideString): ShortString;
function Utf8Decode(const S: RawByteString): WideString;
function UTF8ToUnicodeString(const S: RawByteString): UnicodeString;
function UTF8ToString(const S: RawByteString): string;
function AnsiToUtf8(const S: string): RawByteString;
function Utf8ToAnsi(const S: RawByteString): string;
Как видите, методы завязаны на работе с конкретными кодовыми страницами UTF-8, ANSI, а также на работе с новым типом данных RawByteString. Поэтому возможности третьего способы по сравнению с первыми двумя резко ограничены. Хотя в реалиях нынешнего Рунета их вполне достаточно, т.к. веб-страницы в кодировке koi8-r уже больше раритет, чем обыденность. Использовать методы достаточно просто. Например так:var RS: RawByteString;
[...]
begin
RS:=StringOf(B);
Memo1.Text:=Utf8ToAnsi(RS);
end;
я перевел массив байтов в стоку RawByteString и затем представил её в кодировке Ansi, получив в Memo читабельный русский текст.
Вот три способа работы с различными кодировками текста, которые могут применяться где угодно. И это только часть всех возможностей, которые предоставляет нам Delphi. Какой способ использовать — решать Вам.
Кстати, при работе с кодировками следует обратить внимание на то, что «кракозябры» в тексте — это следствие представления строки с использованием неправильной кодовый страницы. Если же вместо русских букв появляются знаки вопроса, то это следствие того, что в текущей кодовой странице текста нет тех символов, которые вы пробуете показать. Например, представляя текст ANSI в кодировке UTF-8 мы получаем кракозябры, а если попробовать представить текст ANSI Cyrillic (windows-1251) с кодовой страницей ANSI Latin 1 (windows-1252), то вместо русских букв получим знаки вопроса, т.к. в windows-1252 отсутствуют русские буквы и т.д.
Похожие публикации:
MLang в Delphi. Работа с кодировками Web-страниц. Материалы предоставлены автором блога «Переводы от GunSmoker« Небольшая аннотация к...
Google Calendar API в Delphi. Начало работы. Наконец-то разобрался со своим провайдером и доказал тех.поддержке, что проблемы...
Простой компонент Delphi 2010, для работы со Шкурами. Привлекательный и необычный интерфейс, играет не самую последнюю роль в...
Очистка HTML-документа от тегов. Пишу сейчас небольшое дополнение к «Блевантону» — включение в анализ...
Основы работы с тезаурусом Word в Delphi. Как я упоминал вчера, есть идея немного поразбираться с тезаурусом...
Теги: Synapse, кодировки, строки
|
10 Comments · Leave Comment
Александр
04 / 04 / 10
1. По последнему методу: использование функций — это скорее устаревший способ, оставленный для обратной совместимости.
В новых Delphi нужно использовать RawByteString + SetCodePage для произвольной кодировки (задаваемой в run-time) или AnsiString(CodePage) для фиксированной (например, type TString1251 = AnsiString(1251)).
Например, если мы точно знаем, что целевая кодировка — UTF-8, то мы объявляем S типа UTF8String, загружаем в неё контент, а дальше — простое присвоение. Вся конвертация выполнится автоматически и под капотом.
Если же кодировка не фиксирована и определяется в run-time, то мы объявляем S типа RawByteString и грузим в неё контент, после чего делаем, например, SetCodePage(S, 1251, False) — понятно, что у вас будет не константа 1251, а значение из переменной. После чего, как и ранее, обычное присвоение сделает всю работу.
2. Вообще, тут приведены несколько не сравнимые вещи. В том смысле, что способ 1 и 3 — это обёртки над одной и той же функцией (MultiByteToWideChar). Эти способы решают задачу перевода из одной кодировки в другую. Но они не говорят нам, как из ‘windows-1251’ получить 1251, а из ‘UTF-8’ получить 65001 для передачи в TEncoding.GetEncoding или SetCodePage.
Строго говоря, способ 2 тоже должен использовать какой-либо из способов 1 и 2 (или аналогичный) для конвертации кодировок (предложенная реализация вызывает MultiByteToWideChar напрямую). Поэтому, на самом деле, способ 2 состоит из двух частей: определение кодировки по заголовкам и конвертации кодировок.
Просто этот момент как-то размыто сказан. Непонятно, как предлагается осуществлять выбор кодовой страницы в первом и последнем способах.
Ответить
Vlad
04 / 04 / 10
Про SetCodePage хотел написать, а потом чё-то решил, что трех способов будет достаточно Цель поста была не то, чтобы сравнить способы, скорее показать как можно вообще проводить конвертацию кодировок между собой. Кому-то хватит использовать те же UTF8ToAnsi и он будет жить спокойно, а кто-то решит перевести KOI8-R в UTF-8 и тут уже дело будет обстоять по другому.
Но они не говорят нам, как из ‘windows-1251? получить 1251, а из ‘UTF-8? получить 65001 для передачи в TEncoding.GetEncoding или SetCodePage.
Подобные функции видел пока ещё не встречал, хотя не может быть, чтобы за 2 года кто-нибудь не додумался написать хотя бы обёртку над TEncoding. Надо будет на досуге погуглить эту тему. Если что — поделюсь поисками
Ответить
Алексей (Минск)
06 / 01 / 10
Странно у меня для ANSI пишет 932 со 100%
А для dos 866 но вероятность маленькая
Ответить
Аленсандр
10 / 07 / 10
А зачем Вы во втором варианте получали getcharset(headers), если потом явно преобразововали utf8 в 1251? Не подскажите, как получить getcharset, а потом преобразовать ее в 1251 не зная кодировки страници. Спасибо. Статьи Ваши очень познавательны.
Ответить
Fr0sT
10 / 26 / 10=>Код: Выделить всё
Document.Position:=0; SetLength(B,Document.Size); for i := 0 to Document.Size - 1 do Document.Read(B[i],1);
?Код: Выделить всё
Document.Position:=0; SetLength(B,Document.Size); Document.Read(B[0],Length(B));
>существует и минус – TEncoding есть только в версиях Delphi 2009 и выше, что исключает его использование в более ранних версиях. Как быть?
Ну, во-первых, вполне можно выдрать код TEncoding и после небольших подпиливаний заюзать его в D<2009, либо не мучиться с обёртками и юзать самую основу — WideCharToMultiByte/MultiByteToWideChar, на которой, собственно, и сидят все остальные реализации. Только придётся ещё самому обрабатывать случаи utf16/32: эти функции с ними не работают. Но там всё просто:
utf16le = widechar
utf16be = swapbytes(widechar)
ну и с utf32 то же.
>Но они не говорят нам, как из ‘windows-1251″ получить 1251, а из ‘UTF-8? получить 65001 для передачи в TEncoding.GetEncoding или SetCodePage.
Всё просто: массив структур
TCPInfo = record
CPNum: Word;
CPCode: string;
end
и поиск перебором. Строковые коды берутся из RFC по HTTP/XML.
Если интересует, я делал такое для библиотеки SimpleXML
Ответить
wait
02 / 04 / 11
от себя добавлю еще один удобный способ, когда кодировка содержимого известна (в данном случае UTF8), но естественно способ можно переделать и с динамическим определением:
var
Response: TStringStream;
<…>
begin
Response := TStringStream.Create(», TEncoding.UTF8);
if HttpGetBinary(‘путь_до_сайта’, Response) then
Memo1.Text:=Response.DataString;
end;
Ответить
Pingback: Парсинг JSON в Delphi под Windows и MacOS. | Delphi в Internet
Pingback: Криптография в Delphi средствами Indy и Synapse. | Delphi в Internet
Vitus
02 / 19 / 15
Спасибо за статью, очень помогла!!!
Ответить
Макс
11 / 02 / 15
Собственно, вопрос: каким образом можно всё это действо запустить обратно?
То есть мне необходимо перегнать из делфи запросом в URL через idHTTP. Третий день бьюсь, в одну сторону работает, в другую — нет.