URL original: http://www.blogwind.com/Wuvis/42999.shtml
En el marco .Net, la codificación utilizada por StreamReader debe especificarse en el constructor y no se puede cambiar en absoluto a mitad de camino.
En circunstancias normales, esto no causará ningún problema. Generalmente, si se lee un archivo desde un disco duro, la codificación dentro de un solo archivo es generalmente uniforme. Incluso si encuentra un error de lectura, puede cerrar StreamReader y reiniciar la lectura usando la nueva codificación.
Recientemente encontré la necesidad de modificar la codificación y mi programa no descartó la oportunidad de volver a leer. Debido a que el BaseStream del StreamReader que estoy usando es un Network Stream, no puedo cerrarlo... pero las cosas pasadas por Network Stream pueden contener diferentes codificaciones... GB2312, Big5, UTF8, ISO-8859-1, etc. .. Aunque primero se obtiene la información de codificación y luego se lee el contenido específico, una vez que la codificación de Stream Reader utilizada al principio es incorrecta, el contenido leído nunca se podrá recuperar... las palabras se perderán
... No puedo volver a obtenerlo... Después de codificar la información, vuelva a crear un nuevo Stream Reader, porque el contenido específico ha sido almacenado en el búfer del Stream Reader original...
La única solución es implementar un Stream Reader que pueda cambiar el Atributo CurrentEncoding...
Escríbalo todo desde cero. Al principio fue muy poco práctico. Primero obtuve el código fuente de mono e hice modificaciones desde el código de implementación de Stream Reader de mono.
Stream Reader es en realidad muy simple. Tiene dos búferes en su interior, uno es el búfer de entrada y el otro es el búfer decodificado. El primero se usa para almacenar en caché los datos originales leídos del flujo base y el segundo se usa para almacenar en caché las cosas. decodificado de acuerdo con los datos originales... ...Siempre que comprenda el método ReadBuffer en la implementación de mono, no es demasiado difícil modificar dinámicamente CurrentEncoding...
El protocolo de red con el que necesito lidiar es un protocolo de línea ... Solo llamé al método Readline de StreamReader en el programa, pero completamente. Los dos métodos de Read no se utilizan, lo que también me facilita mucho la modificación dinámica de la codificación...
Lo que hago es que cada vez que Llamo a Readline, no solo muevo el cursor (pos) del búfer decodificado, sino que también muevo un nuevo cursor en el búfer de entrada (pos_input). El método es muy simple. El método Readline necesita llamar a FindNextEOL para mover el cursor. busque el símbolo de nueva línea... Agrego una línea más al método FindNextEOL:
int BuscarNextEOL()
{
BuscarNextInputEOL();
....
La nueva función FindNextInputEOL es una réplica completa de FindNextEOL, excepto que la primera procesa el buffer de entrada, mientras que la segunda procesa el buffer decodificado...
De esta manera, puedo saber que después de cada Readline, el buffer de entrada tiene no ha sido decodificado por la capa superior ¿Cuáles son los datos originales leídos?
Luego, agregue el método Set al atributo CurrentEncoding:
colocar
{
codificación=valor;
decodificador = codificación.GetDecoder();
decoded_count = pos + decodificador.GetChars (input_buffer, pos_input, cbEncoded, pos_input, decoded_buffer, pos);
}
Al configurar una nueva codificación, el programa vuelve a decodificar los datos originales que no se han leído según el cursor del búfer de entrada (pos_input) y reemplaza el contenido en el búfer decodificado.
Entonces, la cosa está hecha... Ni siquiera necesitas hacer ninguna modificación al método Readline... Excepto poner la variable cbEncoded en el global...
Sin embargo, esta modificación hace que los dos métodos de Read sean completamente inutilizables. ... Una vez llamado... hará que los dos cursores en el búfer de entrada y el búfer decodificado no estén sincronizados... El código completo se adjunta a continuación. Espero que alguien pueda ayudarme a descubrir los dos métodos de lectura. ... Gracias de antemano …
/
// System.IO.StreamReader.cs
//
// Autor:
// Dietmar Maurer ( [email protected] )
// Miguel de Icaza ( [email protected] )
//
// (C) Ximian, Inc. http://www.ximian.com
// Copyright (C) 2004 Novell ( http://www.novell.com )
//
//
// Copyright (C) 2004 Novell, Inc ( http://www.novell.com )
//
// Por la presente se concede permiso, de forma gratuita, a cualquier persona que obtenga
// una copia de este software y los archivos de documentación asociados (el
// "Software"), para negociar con el Software sin restricciones, incluyendo
// sin limitación los derechos de uso, copia, modificación, fusión, publicación,
// distribuir, sublicenciar y/o vender copias del Software, y a
// permitir que las personas a quienes se proporciona el Software lo hagan, sujeto a
// las siguientes condiciones:
//
// El aviso de derechos de autor anterior y este aviso de permiso serán
// incluido en todas las copias o partes sustanciales del Software.
//
// EL SOFTWARE SE PROPORCIONA "TAL CUAL", SIN GARANTÍA DE NINGÚN TIPO,
// EXPRESA O IMPLÍCITA, INCLUYENDO PERO NO LIMITADA A LAS GARANTÍAS DE
// COMERCIABILIDAD, IDONEIDAD PARA UN FIN DETERMINADO Y
// NO INFRACCIÓN EN NINGÚN CASO LOS AUTORES O TITULARES DE LOS DERECHOS DE AUTOR.
// RESPONSABLE DE CUALQUIER RECLAMACIÓN, DAÑOS U OTRA RESPONSABILIDAD, YA SEA EN UNA ACCIÓN
// DE CONTRATO, AGRAVIO O DE OTRA MANERA, QUE SURJA DE, FUERA DE O EN RELACIÓN
// CON EL SOFTWARE O EL USO U OTROS NEGOCIOS EN EL SOFTWARE.
//
usando Sistema;
usando System.Text;
usando System.Runtime.InteropServices
espacio de nombres System.IO
;
{
[Serializable]
clase pública DynamicStreamReader: TextReader
{
const int DefaultBufferSize = 1024;
const int DefaultFileBufferSize = 4096;
mínimo
del búfer = 128;
//El buffer de entrada
//
byte [] input_buffer;
//
// El búfer decodificado del búfer de entrada anterior
//
char [] búfer_decodificado;
//
// Bytes decodificados en decoded_buffer.
//
int decodificado_count;
//
// Posición actual en el búfer decodificado
//
int pos;
//
// Posición actual en input_buffer
//
int pos_entrada;
//
// El tamaño del buffer que estamos usando
//
int tamaño_búfer;
int hacer_verificaciones;
Codificación de codificación;
Decodificador decodificador;
Stream base_stream;
bool mayBlock;
StringBuilder line_builder;
clase privada NullStreamReader: DynamicStreamReader
{
anulación pública int Peek ()
{
devolver -1;
}
anulación pública int lectura ()
{
devolver -1;
}
anulación pública int lectura ([entrada, salida] char[] buffer, índice int, recuento int)
{
devolver 0;
}
cadena de anulación pública ReadLine ()
{
devolver nulo;
}
cadena de anulación pública ReadToEnd ()
{
devolver Cadena.Vacío;
}
anulación pública Stream BaseStream
{
obtener {return Stream.Null}
}
anulación pública Codificación CurrentEncoding
{
obtener {return Encoding.Unicode}
}
}
público nuevo estático de solo lectura DynamicStreamReader Null = (DynamicStreamReader)(new NullStreamReader());
DynamicStreamReader interno() {}
DynamicStreamReader público (flujo de flujo)
: this (flujo, Encoding.UTF8, true, DefaultBufferSize) { }
public DynamicStreamReader(flujo de flujo, bool detect_encoding_from_bytemarks)
: this (flujo, Encoding.UTF8, detect_encoding_from_bytemarks, DefaultBufferSize) { }
public DynamicStreamReader (flujo de flujo, codificación de codificación)
: this (flujo, codificación, verdadero, DefaultBufferSize) { }
public DynamicStreamReader (flujo de flujo, codificación de codificación, bool detect_encoding_from_bytemarks)
: esto (transmisión, codificación, detect_encoding_from_bytemarks, DefaultBufferSize) { }
public DynamicStreamReader (transmisión de transmisión, codificación de codificación, bool detect_encoding_from_bytemarks, int buffer_size)
{
Inicializar (flujo, codificación, detectar_encoding_from_bytemarks, buffer_size);
}
public DynamicStreamReader (ruta de cadena)
: this (ruta, Encoding.UTF8, true, DefaultFileBufferSize) { }
public DynamicStreamReader (ruta de cadena, bool detect_encoding_from_bytemarks)
: this (ruta, Encoding.UTF8, detect_encoding_from_bytemarks, DefaultFileBufferSize) { }
public DynamicStreamReader (ruta de cadena, codificación de codificación)
: this (ruta, codificación, verdadero, DefaultFileBufferSize) { }
public DynamicStreamReader (ruta de cadena, codificación de codificación, bool detect_encoding_from_bytemarks)
: esto (ruta, codificación, detect_encoding_from_bytemarks, DefaultFileBufferSize) { }
public DynamicStreamReader (ruta de cadena, codificación de codificación, bool detect_encoding_from_bytemarks, int buffer_size)
{
si (nulo == ruta)
lanzar una nueva ArgumentNullException("ruta");
si (String.Empty == ruta)
throw new ArgumentException("No se permite ruta vacía");
si (ruta.IndexOfAny (Path.InvalidPathChars)! = -1)
lanzar una nueva ArgumentException ("la ruta contiene caracteres no válidos");
si (nulo == codificación)
lanzar una nueva ArgumentNullException ("codificación");
si (tamaño_búfer <= 0)
throw new ArgumentOutOfRangeException ("buffer_size", "El tamaño mínimo del búfer debe ser positivo");
string DirName = Path.GetDirectoryName(path);
si (DirName! = String.Empty &&! Directory.Exists (DirName))
lanzar una nueva DirectoryNotFoundException ("Directorio '" + DirName + "' no encontrado.");
si (! Archivo.Existe (ruta))
lanzar nueva FileNotFoundException ("Archivo no encontrado", ruta
Stream stream = (Stream) File.OpenRead (ruta);
Inicializar (transmisión, codificación, detectar_encoding_from_bytemarks, buffer_size);
}
inicialización de vacío interno (transmisión de flujo, codificación de codificación, bool detect_encoding_from_bytemarks, int buffer_size)
{
si (nulo == secuencia)
lanzar una nueva ArgumentNullException ("corriente");
si (nulo == codificación)
lanzar una nueva ArgumentNullException ("codificación");
si (!stream.CanRead)
lanzar una nueva ArgumentException ("No se puede leer la secuencia");
si (tamaño_búfer <= 0)
buffer_size
", "El tamaño mínimo del búfer debe ser positivo");
buffer_size = Tamaño mínimo del buffer;
base_stream = flujo;
input_buffer = nuevo byte [buffer_size];
this.buffer_size = buffer_size;
this.codificación = codificación;
decodificador = codificación.GetDecoder ();
byte [] preámbulo = codificación.GetPreamble ();
do_checks = detectar_encoding_from_bytemarks 1: 0;
do_checks += (preámbulo.Longitud == 0): 0: 2;
decoded_buffer = nuevo carácter [codificación.GetMaxCharCount (tamaño_búfer)];
recuento_decodificado = 0;
posición = 0;
pos_entrada =0;
}
Corriente virtual pública BaseStream
{
conseguir
{
devolver flujo_base;
}
}
Codificación virtual pública CurrentEncoding
{
conseguir
{
si (codificación == nulo)
lanzar nueva excepción ();
codificación de retorno;
}
colocar
{
codificación=valor;
decodificador = codificación.GetDecoder();
decoded_count = pos + decodificador.GetChars (input_buffer, pos_input, cbEncoded - pos_input, decoded_buffer, pos);
//DescartarDatosBuffered();
}
}
anulación pública nula Cerrar ()
{
Disponer (verdadero);
}
anulación protegida void Dispose (eliminación bool)
{
si (eliminando && base_stream! = nulo)
base_stream.Close ();
input_buffer = nulo;
búfer_decodificado = nulo;
codificación = nulo;
decodificador = nulo;
base_stream = nulo;
base.Dispose (eliminar);
}
//
// Proporciona detección automática de la codificación, además de omitirla
// marcas de bytes al comienzo de una secuencia.
//
int DoChecks (recuento int)
{
si ((do_checks & 2) == 2)
{
byte [] preámbulo = codificación.GetPreamble ();
int c = preámbulo.Longitud;
si (cuenta >= c)
{
ent i;
para (i = 0; i < c; i++)
if (input_buffer [i]! = preámbulo [i])
romper;
si (i == c)
devolver yo;
}
}
si ((do_checks & 1) == 1)
{
si (cuenta < 2)
devolver 0
si (input_buffer [0] == 0xfe && input_buffer [1] == 0xff)
{
this.encoding = Codificación.BigEndianUnicode;
devolver 2;
}
si (input_buffer [0] == 0xff && input_buffer [1] == 0xfe)
{
this.encoding = Codificación.Unicode;
devolver 2;
}
si (cuenta < 3)
devolver 0
si (input_buffer [0] == 0xef && input_buffer [1] == 0xbb && input_buffer [2] == 0xbf)
{
this.codificación = Codificación.UTF8;
devolver 3;
}
}
devolver 0;
}
público vacío DiscardBufferedData ()
{
pos = recuento_decodificado = 0;
mayBlock = falso;
// Descarta también el estado interno del decodificador.
decodificador = codificación.GetDecoder ();
}
int cbEncoded;
intparse_start;
// el buffer está vacío, llénalo de nuevo
privado int ReadBuffer ()
{
posición = 0;
pos_entrada = 0;
cbEncoded = 0;
// sigue repitiendo hasta que el decodificador nos proporcione algunos caracteres
recuento_decodificado = 0;
parse_start = 0;
hacer
{
cbEncoded = base_stream.Read (input_buffer, 0, buffer_size);
si (cbEncoded == 0)
devolver 0;
mayBlock = (cbEncoded <tamaño_búfer);
si (do_checks > 0)
{
Codificación antigua = codificación;
parse_start = DoChecks (cbEncoded);
si (antiguo! = codificación)
{
decodificador = codificación.GetDecoder ();
}
hacer_verificaciones = 0;
cbEncoded -= parse_start;
}
decoded_count += decoder.GetChars (input_buffer, parse_start, cbEncoded, decoded_buffer, 0);
parse_start = 0;
} mientras (cuenta_decodificada == 0);
devuelve cuenta_decodificada;
}
anulación pública int Peek ()
{
si (base_stream == nulo)
lanzar una nueva ObjectDisposedException ("StreamReader", "No se puede leer desde un StreamReader cerrado");
if (pos >= decoded_count && (mayBlock || ReadBuffer () == 0))
devolver -1;
devolver búfer_decodificado [pos];
}
anulación pública int lectura ()
{
lanzar una nueva excepción ("¡El lector dinámico no pudo leer!");
}
anulación pública int Read ([In, Out] char[] dest_buffer, int index, int count)
{
lanzar una nueva excepción ("¡El lector dinámico no pudo leer!");
}
bool encontradoCR_input;
int BuscarNextInputEOL()
{
carácter c = '