Difference between revisions of "XML Decoders/ru"
m (Fixed syntax highlighting; deleted category included in page template) |
|||
(16 intermediate revisions by 5 users not shown) | |||
Line 1: | Line 1: | ||
+ | {{XML Decoders}} | ||
+ | |||
== XML декодеры == | == XML декодеры == | ||
+ | |||
+ | Дополнение к [[XML_Tutorial/ru]]. | ||
Начиная с ревизии SVN 12582, XMLReader в состоянии обработать данные в любой кодировке при использовании внешних декодеров. Эта статья - краткое описание, как оно работает. | Начиная с ревизии SVN 12582, XMLReader в состоянии обработать данные в любой кодировке при использовании внешних декодеров. Эта статья - краткое описание, как оно работает. | ||
Line 5: | Line 9: | ||
=== Доступные декодеры === | === Доступные декодеры === | ||
− | В | + | В состав пакета fcl-xml входит декодер, использующий библиотеку libiconv. Он имеет две разновидности: |
1. Модуль '''xmliconv.pas''', который использует существующий пакет iconvenc и предназначен для операционных систем Linux, FreeBSD и Darwin. | 1. Модуль '''xmliconv.pas''', который использует существующий пакет iconvenc и предназначен для операционных систем Linux, FreeBSD и Darwin. | ||
− | 2. Модуль '''xmliconv_windows.pas''' для Windows. Он связан с библиотекой iconv.dll, которую | + | 2. Модуль '''xmliconv_windows.pas''' для Windows. Он связан с библиотекой iconv.dll "родной" сборки (т.е. не из состава cygwin или mingw), которую придется распространять вместе с приложением. |
=== Структура декодера === | === Структура декодера === | ||
Line 20: | Line 24: | ||
Вот краткое описание: | Вот краткое описание: | ||
− | ==== | + | ==== Функция GetDecoder ==== |
function GetDecoder(const AEncoding: string; out Decoder: TDecoder): Boolean; stdcall; | function GetDecoder(const AEncoding: string; out Decoder: TDecoder): Boolean; stdcall; | ||
− | Во время инициализации программы декодер | + | Во время инициализации программы декодер необходимо зарегистрировать путем вызова процедуры <code>XMLRead.RegisterDecoder</code>, которой в качестве параметра передаётся функция <code>GetDecoder</code>. |
− | + | Если в процессе чтения XMLReader обнаруживает кодировку, которую он не может обработать сам, то он вызывает все зарегистрированные функции <code>GetDecoder</code> в том же порядке, в котором они были зарегистрированы, до тех пор, пока одна из них не возвратит ''True''. | |
− | Параметры функции <code>GetDecoder</code> - | + | Параметры функции <code>GetDecoder</code> - название кодировки и запись типа <code>TDecoder</code>, которую должна заполнить функция. Название кодировки содержит только символамы из множества ['A'..'Z', 'a'..'z', '0'.. '9', '.', '-,' _'], сравнивать названия кодировок следует независимо от регистра. Если декодер поддерживает данную кодировку, функция должна установить по крайней мере поле <code>Decode</code> в записи Decoder и возвратить ''True''. Установка остальных полей записи <code>Decoder</code> не является обязательной. |
− | ==== | + | ==== Процедура Cleanup ==== |
procedure Cleanup(Context: Pointer); stdcall; | procedure Cleanup(Context: Pointer); stdcall; | ||
− | + | Если функция <code>GetDecoder</code> установила поле <code>Decoder.Cleanup</code>, то указанная процедура будет вызвана один раз, когда чтение объекта завершено и декодер больше не нужен. Как следует из названия, декодер должен освободить все занятые ресурсы. | |
− | + | Значение <code>Decoder.Context</code> передается в качестве аргумента процедур <code>Decode</code> и <code>Cleanup</code> при каждом вызове. XMLReader сам не присваивает значение этому полю. | |
− | ==== | + | ==== Функция Decode ==== |
function Decode(Context: Pointer; InBuf: PChar; var InCnt: Cardinal; | function Decode(Context: Pointer; InBuf: PChar; var InCnt: Cardinal; | ||
OutBuf: PWideChar; var OutCnt: Cardinal): Integer; stdcall; | OutBuf: PWideChar; var OutCnt: Cardinal): Integer; stdcall; | ||
− | + | Функция <code>Decode</code> выполняет основную работу. Она должна преобразовать входные данные, указатель на которые находится в <code>InBuf</code>, в UTF-16 и записать перевод в буфер, на который указывает <code>OutBuf</code>. Размер входного буфера находится в <code>InCnt</code>, а размер буфера вывода находится в <code>OutCnt</code>. | |
− | <code>InCnt</code>, | + | |
+ | '''Важное замечание''': значение <code>InCnt</code> выражено в '''bytes''', в то время как <code>OutCnt</code> - в '''WideChars'''. | ||
+ | |||
+ | Функция должна уменьшить <code>InCnt</code> и <code>OutCnt</code> в соответствии с количеством обработанных данных. Каждый обработанный символ уменьшает <code>OutCnt</code> на единицу (или на 2 в случае, если в буфер записывается суррогатная пара); то, на сколько уменьшается <code>InCnt</code>, зависит от входной кодировки. | ||
+ | |||
+ | Функция не должна делать каких-либо предположений о начальном размере буферов: для примера, парсер может вызвать <code>Decode</code> с недостаточной длиной входного буфера. В этом случае <code>Decode</code> должна возвратить 0, индицируя о том, она ничего не декодировала, после чего парсер прочитает дополнительные входные данные и вызовет <code>Decode</code> снова. | ||
+ | |||
+ | Функция должна возвращать положительный результат, если она что-то обработала, ноль - если нет (по причине отсутствия места во входном или выходном буфере) и отрицательное значение в случае, если входные данные содержат недопустимую последовательность. В настоящее время любое отрицательное значение просто прерывает чтение с сообщением об ошибке декодирования, но в дальнейшем, возможно, будут определены разновидности ошибок. | ||
+ | |||
+ | В случае обнаружения ошибки во входных данных, декодер все равно должен уменьшить значение <code>OutCnt</code> на количество успешно обработанных символов. Это позволяет парсеру сообщать точное расположение ошибки в тексте. | ||
+ | |||
+ | === Пример декодера === | ||
+ | |||
+ | Ниже приводится пример декодера для кодировки cp866. Он не имеет внутреннего состояния и не использует поля <code>Cleanup</code> и <code>Context</code>. Декодер легко модифицировать для обработки любой аналогичной однобайтовой кодировки путем замены таблицы преобразования. | ||
+ | |||
+ | <syntaxhighlight lang=pascal> | ||
+ | unit xmlcp866; | ||
+ | |||
+ | interface | ||
+ | |||
+ | implementation | ||
− | + | uses | |
+ | SysUtils, xmlread; | ||
− | + | const | |
+ | cp866table: array[#128..#255] of WideChar=( | ||
+ | #$0410, #$0411, #$0412, #$0413, #$0414, #$0415, #$0416, #$0417, | ||
+ | #$0418, #$0419, #$041A, #$041B, #$041C, #$041D, #$041E, #$041F, | ||
+ | #$0420, #$0421, #$0422, #$0423, #$0424, #$0425, #$0426, #$0427, | ||
+ | #$0428, #$0429, #$042A, #$042B, #$042C, #$042D, #$042E, #$042F, | ||
+ | #$0430, #$0431, #$0432, #$0433, #$0434, #$0435, #$0436, #$0437, | ||
+ | #$0438, #$0439, #$043A, #$043B, #$043C, #$043D, #$043E, #$043F, | ||
+ | #$2591, #$2592, #$2593, #$2502, #$2524, #$2561, #$2562, #$2556, | ||
+ | #$2555, #$2563, #$2551, #$2557, #$255D, #$255C, #$255B, #$2510, | ||
+ | #$2514, #$2534, #$252C, #$251C, #$2500, #$253C, #$255E, #$255F, | ||
+ | #$255A, #$2554, #$2569, #$2566, #$2560, #$2550, #$256C, #$2567, | ||
+ | #$2568, #$2564, #$2565, #$2559, #$2558, #$2552, #$2553, #$256B, | ||
+ | #$256A, #$2518, #$250C, #$2588, #$2584, #$258C, #$2590, #$2580, | ||
+ | #$0440, #$0441, #$0442, #$0443, #$0444, #$0445, #$0446, #$0447, | ||
+ | #$0448, #$0449, #$044A, #$044B, #$044C, #$044D, #$044E, #$044F, | ||
+ | #$0401, #$0451, #$0404, #$0454, #$0407, #$0457, #$040E, #$045E, | ||
+ | #$00B0, #$2219, #$00B7, #$221A, #$2116, #$00A4, #$25A0, #$00A0); | ||
− | + | function cp866Decode(Context: Pointer; InBuf: PChar; var InCnt: Cardinal; OutBuf: PWideChar; | |
+ | var OutCnt: Cardinal): Integer; stdcall; | ||
+ | var | ||
+ | I: Integer; | ||
+ | cnt: Cardinal; | ||
+ | begin | ||
+ | cnt := OutCnt; // число widechars | ||
+ | if cnt > InCnt then | ||
+ | cnt := InCnt; | ||
+ | for I := 0 to cnt-1 do | ||
+ | begin | ||
+ | if InBuf[I] < #128 then | ||
+ | OutBuf[I] := WideChar(ord(InBuf[I])) | ||
+ | else | ||
+ | OutBuf[I] := cp866table[InBuf[I]]; | ||
+ | end; | ||
+ | Dec(InCnt, cnt); | ||
+ | Dec(OutCnt, cnt); | ||
+ | Result := cnt; | ||
+ | end; | ||
− | + | function GetCP866Decoder(const AEncoding: string; out Decoder: TDecoder): Boolean; stdcall; | |
+ | begin | ||
+ | // Большинство кодировок имеет один или несколько 'псевдонимов'. | ||
+ | if SameText(AEncoding, 'IBM866') or | ||
+ | SameText(AEncoding, 'cp866') or | ||
+ | SameText(AEncoding, '866') or | ||
+ | SameText(AEncoding, 'csIBM866') then | ||
+ | begin | ||
+ | Decoder.Decode := @cp866Decode; | ||
+ | Decoder.Cleanup := nil; | ||
+ | Decoder.Context := nil; | ||
+ | Result := True; | ||
+ | end | ||
+ | else | ||
+ | Result := False; | ||
+ | end; | ||
− | + | initialization | |
+ | RegisterDecoder(@GetCP866Decoder); | ||
+ | end. | ||
+ | </syntaxhighlight> |
Latest revision as of 09:05, 3 March 2020
│
English (en) │
español (es) │
русский (ru) │
中文(中国大陆) (zh_CN) │
XML декодеры
Дополнение к XML_Tutorial/ru.
Начиная с ревизии SVN 12582, XMLReader в состоянии обработать данные в любой кодировке при использовании внешних декодеров. Эта статья - краткое описание, как оно работает.
Доступные декодеры
В состав пакета fcl-xml входит декодер, использующий библиотеку libiconv. Он имеет две разновидности:
1. Модуль xmliconv.pas, который использует существующий пакет iconvenc и предназначен для операционных систем Linux, FreeBSD и Darwin.
2. Модуль xmliconv_windows.pas для Windows. Он связан с библиотекой iconv.dll "родной" сборки (т.е. не из состава cygwin или mingw), которую придется распространять вместе с приложением.
Структура декодера
Интерфейс с внешними декодерами сделан в простом процедурном стиле. Для написания декодера по существу используют следующие три процедуры:
GetDecoder
Decode
Cleanup
(опционально)
Вот краткое описание:
Функция GetDecoder
function GetDecoder(const AEncoding: string; out Decoder: TDecoder): Boolean; stdcall;
Во время инициализации программы декодер необходимо зарегистрировать путем вызова процедуры XMLRead.RegisterDecoder
, которой в качестве параметра передаётся функция GetDecoder
.
Если в процессе чтения XMLReader обнаруживает кодировку, которую он не может обработать сам, то он вызывает все зарегистрированные функции GetDecoder
в том же порядке, в котором они были зарегистрированы, до тех пор, пока одна из них не возвратит True.
Параметры функции GetDecoder
- название кодировки и запись типа TDecoder
, которую должна заполнить функция. Название кодировки содержит только символамы из множества ['A'..'Z', 'a'..'z', '0'.. '9', '.', '-,' _'], сравнивать названия кодировок следует независимо от регистра. Если декодер поддерживает данную кодировку, функция должна установить по крайней мере поле Decode
в записи Decoder и возвратить True. Установка остальных полей записи Decoder
не является обязательной.
Процедура Cleanup
procedure Cleanup(Context: Pointer); stdcall;
Если функция GetDecoder
установила поле Decoder.Cleanup
, то указанная процедура будет вызвана один раз, когда чтение объекта завершено и декодер больше не нужен. Как следует из названия, декодер должен освободить все занятые ресурсы.
Значение Decoder.Context
передается в качестве аргумента процедур Decode
и Cleanup
при каждом вызове. XMLReader сам не присваивает значение этому полю.
Функция Decode
function Decode(Context: Pointer; InBuf: PChar; var InCnt: Cardinal; OutBuf: PWideChar; var OutCnt: Cardinal): Integer; stdcall;
Функция Decode
выполняет основную работу. Она должна преобразовать входные данные, указатель на которые находится в InBuf
, в UTF-16 и записать перевод в буфер, на который указывает OutBuf
. Размер входного буфера находится в InCnt
, а размер буфера вывода находится в OutCnt
.
Важное замечание: значение InCnt
выражено в bytes, в то время как OutCnt
- в WideChars.
Функция должна уменьшить InCnt
и OutCnt
в соответствии с количеством обработанных данных. Каждый обработанный символ уменьшает OutCnt
на единицу (или на 2 в случае, если в буфер записывается суррогатная пара); то, на сколько уменьшается InCnt
, зависит от входной кодировки.
Функция не должна делать каких-либо предположений о начальном размере буферов: для примера, парсер может вызвать Decode
с недостаточной длиной входного буфера. В этом случае Decode
должна возвратить 0, индицируя о том, она ничего не декодировала, после чего парсер прочитает дополнительные входные данные и вызовет Decode
снова.
Функция должна возвращать положительный результат, если она что-то обработала, ноль - если нет (по причине отсутствия места во входном или выходном буфере) и отрицательное значение в случае, если входные данные содержат недопустимую последовательность. В настоящее время любое отрицательное значение просто прерывает чтение с сообщением об ошибке декодирования, но в дальнейшем, возможно, будут определены разновидности ошибок.
В случае обнаружения ошибки во входных данных, декодер все равно должен уменьшить значение OutCnt
на количество успешно обработанных символов. Это позволяет парсеру сообщать точное расположение ошибки в тексте.
Пример декодера
Ниже приводится пример декодера для кодировки cp866. Он не имеет внутреннего состояния и не использует поля Cleanup
и Context
. Декодер легко модифицировать для обработки любой аналогичной однобайтовой кодировки путем замены таблицы преобразования.
unit xmlcp866;
interface
implementation
uses
SysUtils, xmlread;
const
cp866table: array[#128..#255] of WideChar=(
#$0410, #$0411, #$0412, #$0413, #$0414, #$0415, #$0416, #$0417,
#$0418, #$0419, #$041A, #$041B, #$041C, #$041D, #$041E, #$041F,
#$0420, #$0421, #$0422, #$0423, #$0424, #$0425, #$0426, #$0427,
#$0428, #$0429, #$042A, #$042B, #$042C, #$042D, #$042E, #$042F,
#$0430, #$0431, #$0432, #$0433, #$0434, #$0435, #$0436, #$0437,
#$0438, #$0439, #$043A, #$043B, #$043C, #$043D, #$043E, #$043F,
#$2591, #$2592, #$2593, #$2502, #$2524, #$2561, #$2562, #$2556,
#$2555, #$2563, #$2551, #$2557, #$255D, #$255C, #$255B, #$2510,
#$2514, #$2534, #$252C, #$251C, #$2500, #$253C, #$255E, #$255F,
#$255A, #$2554, #$2569, #$2566, #$2560, #$2550, #$256C, #$2567,
#$2568, #$2564, #$2565, #$2559, #$2558, #$2552, #$2553, #$256B,
#$256A, #$2518, #$250C, #$2588, #$2584, #$258C, #$2590, #$2580,
#$0440, #$0441, #$0442, #$0443, #$0444, #$0445, #$0446, #$0447,
#$0448, #$0449, #$044A, #$044B, #$044C, #$044D, #$044E, #$044F,
#$0401, #$0451, #$0404, #$0454, #$0407, #$0457, #$040E, #$045E,
#$00B0, #$2219, #$00B7, #$221A, #$2116, #$00A4, #$25A0, #$00A0);
function cp866Decode(Context: Pointer; InBuf: PChar; var InCnt: Cardinal; OutBuf: PWideChar;
var OutCnt: Cardinal): Integer; stdcall;
var
I: Integer;
cnt: Cardinal;
begin
cnt := OutCnt; // число widechars
if cnt > InCnt then
cnt := InCnt;
for I := 0 to cnt-1 do
begin
if InBuf[I] < #128 then
OutBuf[I] := WideChar(ord(InBuf[I]))
else
OutBuf[I] := cp866table[InBuf[I]];
end;
Dec(InCnt, cnt);
Dec(OutCnt, cnt);
Result := cnt;
end;
function GetCP866Decoder(const AEncoding: string; out Decoder: TDecoder): Boolean; stdcall;
begin
// Большинство кодировок имеет один или несколько 'псевдонимов'.
if SameText(AEncoding, 'IBM866') or
SameText(AEncoding, 'cp866') or
SameText(AEncoding, '866') or
SameText(AEncoding, 'csIBM866') then
begin
Decoder.Decode := @cp866Decode;
Decoder.Cleanup := nil;
Decoder.Context := nil;
Result := True;
end
else
Result := False;
end;
initialization
RegisterDecoder(@GetCP866Decoder);
end.