Sign in Join
Wiki copytemporary

XML Decoders/ru: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: XML Decoders/ru

OldNewLine
1+## XML декодеры
2+
3+Дополнение к [[XML Tutorial/ru]].
4+
5+Начиная с ревизии SVN 12582, XMLReader в состоянии обработать данные в любой кодировке при использовании внешних декодеров. Эта статья - краткое описание, как оно работает.
6+
7+### Доступные декодеры
8+
9+В состав пакета fcl-xml входит декодер, использующий библиотеку libiconv. Он имеет две разновидности:
10+
11+1\. Модуль **xmliconv.pas**, который использует существующий пакет iconvenc и предназначен для операционных систем Linux, FreeBSD и Darwin.
12+
13+2\. Модуль **xmliconv_windows.pas** для Windows. Он связан с библиотекой iconv.dll "родной" сборки (т.е. не из состава cygwin или mingw), которую придется распространять вместе с приложением.
14+
15+### Структура декодера
16+
17+Интерфейс с внешними декодерами сделан в простом процедурном стиле. Для написания декодера по существу используют следующие три процедуры:
18+
19+1. `GetDecoder`
20+2. `Decode`
21+3. `Cleanup` (опционально)
22+
23+Вот краткое описание:
24+
25+#### Функция GetDecoder
26+
27+`function GetDecoder(const AEncoding: string; out Decoder: TDecoder): Boolean; stdcall;`
28+
29+Во время инициализации программы декодер необходимо зарегистрировать путем вызова процедуры `XMLRead.RegisterDecoder`, которой в качестве параметра передаётся функция `GetDecoder`.
30+
31+Если в процессе чтения XMLReader обнаруживает кодировку, которую он не может обработать сам, то он вызывает все зарегистрированные функции `GetDecoder` в том же порядке, в котором они были зарегистрированы, до тех пор, пока одна из них не возвратит *True*. Параметры функции `GetDecoder` - название кодировки и запись типа `TDecoder`, которую должна заполнить функция. Название кодировки содержит только символамы из множества \['A'..'Z', 'a'..'z', '0'.. '9', '.', '-,' _'\], сравнивать названия кодировок следует независимо от регистра. Если декодер поддерживает данную кодировку, функция должна установить по крайней мере поле `Decode` в записи Decoder и возвратить *True*. Установка остальных полей записи `Decoder` не является обязательной.
32+
33+#### Процедура Cleanup
34+
35+`procedure Cleanup(Context: Pointer); stdcall;`
36+
37+Если функция `GetDecoder` установила поле `Decoder.Cleanup`, то указанная процедура будет вызвана один раз, когда чтение объекта завершено и декодер больше не нужен. Как следует из названия, декодер должен освободить все занятые ресурсы.
38+
39+Значение `Decoder.Context` передается в качестве аргумента процедур `Decode` и `Cleanup` при каждом вызове. XMLReader сам не присваивает значение этому полю.
40+
41+#### Функция Decode
42+
43+`function Decode(Context: Pointer; InBuf: PChar; var InCnt: Cardinal;`\
44+`                OutBuf: PWideChar; var OutCnt: Cardinal): Integer; stdcall;`
45+
46+Функция `Decode` выполняет основную работу. Она должна преобразовать входные данные, указатель на которые находится в `InBuf`, в UTF-16 и записать перевод в буфер, на который указывает `OutBuf`. Размер входного буфера находится в `InCnt`, а размер буфера вывода находится в `OutCnt`.
47+
48+**Важное замечание**: значение `InCnt` выражено в **bytes**, в то время как `OutCnt` - в **WideChars**.
49+
50+Функция должна уменьшить `InCnt` и `OutCnt` в соответствии с количеством обработанных данных. Каждый обработанный символ уменьшает `OutCnt` на единицу (или на 2 в случае, если в буфер записывается суррогатная пара); то, на сколько уменьшается `InCnt`, зависит от входной кодировки.
51+
52+Функция не должна делать каких-либо предположений о начальном размере буферов: для примера, парсер может вызвать `Decode` с недостаточной длиной входного буфера. В этом случае `Decode` должна возвратить 0, индицируя о том, она ничего не декодировала, после чего парсер прочитает дополнительные входные данные и вызовет `Decode` снова.
53+
54+Функция должна возвращать положительный результат, если она что-то обработала, ноль - если нет (по причине отсутствия места во входном или выходном буфере) и отрицательное значение в случае, если входные данные содержат недопустимую последовательность. В настоящее время любое отрицательное значение просто прерывает чтение с сообщением об ошибке декодирования, но в дальнейшем, возможно, будут определены разновидности ошибок.
55+
56+В случае обнаружения ошибки во входных данных, декодер все равно должен уменьшить значение `OutCnt` на количество успешно обработанных символов. Это позволяет парсеру сообщать точное расположение ошибки в тексте.
57+
58+### Пример декодера
59+
60+Ниже приводится пример декодера для кодировки cp866. Он не имеет внутреннего состояния и не использует поля `Cleanup` и `Context`. Декодер легко модифицировать для обработки любой аналогичной однобайтовой кодировки путем замены таблицы преобразования.
61+
62+``` pascal
63+unit xmlcp866;
64+
65+interface
66+
67+implementation
68+
69+uses
70+ SysUtils, xmlread;
71+
72+const
73+ cp866table: array[#128..#255] of WideChar=(
74+ #$0410, #$0411, #$0412, #$0413, #$0414, #$0415, #$0416, #$0417,
75+ #$0418, #$0419, #$041A, #$041B, #$041C, #$041D, #$041E, #$041F,
76+ #$0420, #$0421, #$0422, #$0423, #$0424, #$0425, #$0426, #$0427,
77+ #$0428, #$0429, #$042A, #$042B, #$042C, #$042D, #$042E, #$042F,
78+ #$0430, #$0431, #$0432, #$0433, #$0434, #$0435, #$0436, #$0437,
79+ #$0438, #$0439, #$043A, #$043B, #$043C, #$043D, #$043E, #$043F,
80+ #$2591, #$2592, #$2593, #$2502, #$2524, #$2561, #$2562, #$2556,
81+ #$2555, #$2563, #$2551, #$2557, #$255D, #$255C, #$255B, #$2510,
82+ #$2514, #$2534, #$252C, #$251C, #$2500, #$253C, #$255E, #$255F,
83+ #$255A, #$2554, #$2569, #$2566, #$2560, #$2550, #$256C, #$2567,
84+ #$2568, #$2564, #$2565, #$2559, #$2558, #$2552, #$2553, #$256B,
85+ #$256A, #$2518, #$250C, #$2588, #$2584, #$258C, #$2590, #$2580,
86+ #$0440, #$0441, #$0442, #$0443, #$0444, #$0445, #$0446, #$0447,
87+ #$0448, #$0449, #$044A, #$044B, #$044C, #$044D, #$044E, #$044F,
88+ #$0401, #$0451, #$0404, #$0454, #$0407, #$0457, #$040E, #$045E,
89+ #$00B0, #$2219, #$00B7, #$221A, #$2116, #$00A4, #$25A0, #$00A0);
90+
91+function cp866Decode(Context: Pointer; InBuf: PChar; var InCnt: Cardinal; OutBuf: PWideChar;
92+ var OutCnt: Cardinal): Integer; stdcall;
93+var
94+ I: Integer;
95+ cnt: Cardinal;
96+begin
97+ cnt := OutCnt; // число widechars
98+ if cnt > InCnt then
99+ cnt := InCnt;
100+ for I := 0 to cnt-1 do
101+ begin
102+ if InBuf[I] < #128 then
103+ OutBuf[I] := WideChar(ord(InBuf[I]))
104+ else
105+ OutBuf[I] := cp866table[InBuf[I]];
106+ end;
107+ Dec(InCnt, cnt);
108+ Dec(OutCnt, cnt);
109+ Result := cnt;
110+end;
111+
112+function GetCP866Decoder(const AEncoding: string; out Decoder: TDecoder): Boolean; stdcall;
113+begin
114+// Большинство кодировок имеет один или несколько 'псевдонимов'.
115+ if SameText(AEncoding, 'IBM866') or
116+ SameText(AEncoding, 'cp866') or
117+ SameText(AEncoding, '866') or
118+ SameText(AEncoding, 'csIBM866') then
119+ begin
120+ Decoder.Decode := @cp866Decode;
121+ Decoder.Cleanup := nil;
122+ Decoder.Context := nil;
123+ Result := True;
124+ end
125+ else
126+ Result := False;
127+end;
128+
129+initialization
130+ RegisterDecoder(@GetCP866Decoder);
131+end.
132+```
133+
134+---
135+*From the Free Pascal wiki, page "XML Decoders/ru", as it stood on 2020-03-03 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history