XML Decoders/es
│
English (en) │
español (es) │
русский (ru) │
中文(中国大陆) (zh_CN) │
XML Decodificadores
A partir de la revisión de SVN 12582, el lector de XML es capaz de procesar los datos en cualquier codificación mediante el uso de descodificadores externos. Lo que sigue es una breve descripción de cómo funciona.
Decodificadores disponibles
En la actualidad, un decodificador que utiliza libiconv está disponible. Tiene dos implementaciones distintas. La primera, en la unidadxmliconv.pas, utiliza el paquete iconvenc existente y es compatible con Linux, FreeBSD y Darwin. La segunda, en la unidad xmliconv_windows.pas, es para Windows. Enlaza a la iconv.dll que debes distribuir con la aplicación.
Estructura del decodificador
La interacción con los decodificadores externos se hace mediante procedimiento sencillo. Escribir el decodificador es esencialmente escribir la implementación de los tres procedimientos siguientes:
GetDecoder
Decode
Cleanup
(optional)
Aquí una breve descripción del funcionamiento con un decodificador externo:
GetDecoder
function GetDecoder(const AEncoding: string; out Decoder: TDecoder): Boolean; stdcall;
En el momento de la iniciación del programa, el decodificador se registra llamando al procedimiemto XMLRead.RegisterDecoder
y pasando la función GetDecoder
como argumento.
Cada vez que el lector se encuentra con una etiqueta de codificación que no controla internamente, llama a todas las funciones GetDecoder
registradas en el mismo orden en que se registraron, hasta que una de ellas devuelve true.
Los argumentos de la función GetDecoder
son el nombre de la codificación y el registro TDecoder
que la función debe llenar. El nombre de codificación se limita a los caracteres de rango ['Z' .. 'A ', 'a' .. 'z', '0 '.. '9',, '.' '-','_'], y no distingue entre mayúsculas y minúsculas. Si el decodificador es compatible con la codificación dada, la función debe establecer al menos el miembro Decode
del registro suministrado y devolver True. Cumplimentar otros miembros de Decoder
es opcional.
Cleanup
procedure Cleanup(Context: Pointer); stdcall;
Si GetDecoder
establece el miembro Decoder.Cleanup
, se llamará por el lector de una vez, después de la transformación de la entidad actual esté acabada. Como sugiere el nombre, el decodificador debe liberar todos los recursos asignados.
El valor de Decoder.Context
se pasa a los procedimientos Decode
y Cleanup
cada vez que es llamado. El lector no da ningún significado a este valor.
Decode
function Decode(Context: Pointer; InBuf: PChar; var InCnt: Cardinal; OutBuf: PWideChar; var OutCnt: Cardinal): Integer; stdcall;
La función Decode
hace el trabajo principal. Convierte los datos de entrada apuntados por inbuf
a UTF-16 formato de boble byte de la plataforma actual y la pone en outbuf
. El tamaño del espacio de entrada se muestra en
InCnt
, el espacio disponible en el espacio de salida se encuentra en OutCnt
.
Una diferencia importante a destacar es que InCnt
se da en bytes, mientras que OutCnt
está en WideChars.
La función debe decrementar InCnt
y OutCnt
de acuerdo a la cantidad de datos procesados. Cada carácter procesados disminuye OutCnt
en una unidad (o dos en caso de que el sustituto escrito sea doble), la cantidad a disminuir de InCnt
depende de la codificación real.
No debe hacerse níngun presunción sobre el tamaño inicial de la memoria de trabajo: por ejemplo, el lector puede llamar al decodificador con sólo unos pocos bytes en la memoria de entrada. La función decodificador debe volver cero para indicar que nada se ha procesado, y el lector pondrá más datos en la entrada y llamará de nuevo al decodificador.
La función debe devolver un valor positivo si ha procesado algo, cero si no (por ejemplo, porque no hay espacio disponible en cualquiera de las memorias de entrada o de salida), y negativo en el caso de datos de entrada con secuencias ilegales. En el futuro, se intentará categorizar los errores de decodificación, pero en la actualidad cualquier valor de retorno negativo simplemente cancela la lectura con el mensaje 'Decoding error'.
Aún en el caso de error en los datos de entrada el decodificador debe disminuir OutCnt
para reflejar el número de caracteres procesado con éxito. Esto será utilizado por el lector para ofrecer información de localización en el mensaje de error de excepción.
Ejemplo de decodificador
Lo que sigue es un ejemplo de una unidad que decodifica cp866. Este decodificador no tiene estado, por lo que no utiliza los miembros Cleanup
y Context
. Este ejemplo es muy fácil de modificar para manejar cualquier codificación similar de un solo byte con sólo reemplazar la tabla de conversión.
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; // num of 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
// Most encodings typically have one or more alias names.
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.