URL original: http://www.blogwind.com/Wuvist/42999.shtml
Na estrutura .Net, a codificação usada pelo StreamReader deve ser especificada no construtor e não pode ser alterada no meio do caminho.
Em circunstâncias normais, isso não causará problemas. Geralmente, se um arquivo é lido de um disco rígido, a codificação dentro de um único arquivo é geralmente uniforme. Mesmo se encontrar um erro de leitura, você pode fechar o StreamReader e reiniciar a leitura usando a nova codificação.
Recentemente, encontrei a necessidade de modificar a codificação e meu programa não desativou a capacidade de reler. Como o BaseStream do StreamReader que estou usando é um Network Stream, não consigo fechá-lo... mas as coisas passadas pelo Network Stream podem conter codificações diferentes... GB2312, Big5, UTF8, ISO-8859-1, etc. .. Embora as informações de codificação sejam obtidas primeiro e depois o conteúdo específico seja lido. No entanto, uma vez que a codificação do Stream Reader usada no início esteja errada, o conteúdo lido nunca poderá ser recuperado...as palavras serão perdidas...
I. não consigo obtê-lo novamente... Após codificar as informações, recrie um novo Stream Reader, pois o conteúdo específico foi armazenado em buffer pelo Stream Reader original...
A única solução é implementar um Stream Reader que possa alterar o Atributo CurrentEncoding...
Escreva tudo do zero No início foi muito impraticável. Primeiro obtive o código-fonte do mono e fiz modificações no código de implementação do Stream Reader do mono.
O Stream Reader é realmente muito simples. Ele possui dois buffers internos, um é o buffer de entrada e o outro é o buffer decodificado. O primeiro é usado para armazenar em cache os dados originais lidos do fluxo base, e o último é usado para armazenar em cache as coisas. decodificado de acordo com os dados originais... ...Contanto que você entenda o método ReadBuffer na implementação de mono, não é muito difícil modificar dinamicamente CurrentEncoding...
O protocolo de rede com o qual preciso lidar é um protocolo de linha ...Eu só chamei o método Readline do StreamReader no programa, mas completamente Os dois métodos de Read não são usados, o que também torna muito mais fácil para mim modificar dinamicamente a codificação...
O que eu faço é que toda vez que eu chamo Readline, eu não apenas movo o cursor (pos) do buffer decodificado, mas também movo um novo cursor no buffer de entrada (pos_input), o método é muito simples. O método Readline precisa chamar FindNextEOL para mover o cursor para. encontre o símbolo de nova linha... Adiciono mais uma linha ao método FindNextEOL:
int FindNextEOL()
{
FindNextInputEOL();
....
A nova função FindNextInputEOL é uma réplica completa de FindNextEOL, exceto que a primeira processa o buffer de entrada, enquanto a última processa o buffer decodificado...
Desta forma, posso saber que após cada Readline, o buffer de entrada foi não foi decodificado pela camada superior Quais são os dados originais lidos...
Em seguida, adicione o método Set ao atributo CurrentEncoding:
definir
{
codificação=valor;
decodificador = encoding.GetDecoder();
decoded_count = pos + decoder.GetChars (input_buffer, pos_input, cbEncoded, pos_input, decoded_buffer, pos);
}
Ao definir uma nova codificação, o programa redecodifica os dados originais que não foram lidos de acordo com o cursor do buffer de entrada (pos_input) e substitui o conteúdo no buffer decodificado.
Então está feito... Você nem precisa fazer nenhuma modificação no método Readline... Exceto colocar a variável cbEncoded no global...
Porém, essa modificação torna os dois métodos de Read completamente inutilizáveis ... Uma vez chamado... fará com que os dois cursores no buffer de entrada e o buffer decodificado fiquem fora de sincronia... O código completo está anexado abaixo. Espero que alguém possa me ajudar a descobrir os dois métodos de leitura. ... Desde já, obrigado …
/
//System.IO.StreamReader.cs
//
//Autor:
// Dietmar Maurer ( [email protected] )
// Miguel de Icaza ( [email protected] )
//
// (C) Ximian, Inc.
// Copyright (C) 2004 Novell ( http://www.novell.com )
//
//
// Copyright (C) 2004 Novell, Inc ( http://www.novell.com )
//
// A permissão é concedida gratuitamente a qualquer pessoa que obtenha
// uma cópia deste software e dos arquivos de documentação associados (o
// "Software"), para negociar o Software sem restrições, incluindo
// sem limitação, os direitos de usar, copiar, modificar, mesclar, publicar,
// distribuir, sublicenciar e/ou vender cópias do Software, e para
// permitir que as pessoas a quem o Software é fornecido o façam, sujeito a
//as seguintes condições:
//
// O aviso de direitos autorais acima e este aviso de permissão devem ser
// incluído em todas as cópias ou partes substanciais do Software.
//
// O SOFTWARE É FORNECIDO "COMO ESTÁ", SEM GARANTIA DE QUALQUER TIPO,
// EXPRESSA OU IMPLÍCITA, INCLUINDO, MAS NÃO SE LIMITANDO ÀS GARANTIAS DE
// COMERCIALIZAÇÃO, ADEQUAÇÃO A UM DETERMINADO FIM E
// NÃO VIOLAÇÃO EM HIPÓTESE ALGUMA SERÃO OS AUTORES OU TITULARES DOS DIREITOS AUTORAIS.
// RESPONSÁVEL POR QUALQUER RECLAMAÇÃO, DANOS OU OUTRA RESPONSABILIDADE, SEJA EM AÇÃO
// DE CONTRATO, ATO ILÍCITO OU OUTRO, DECORRENTE DE, OU EM RELAÇÃO
// COM O SOFTWARE OU O USO OU OUTRAS NEGOCIAÇÕES DO SOFTWARE.
//
usando Sistema;
usando System.Text;
usando System.Runtime.InteropServices
namespace System.IO
;
{
[Serializável]
classe pública DynamicStreamReader: TextReader
{
const int DefaultBufferSize = 1024;
const int DefaultFileBufferSize = 4096;
const int MínimoBufferSize = 128
;
//O buffer de entrada
//
input_buffer
;
// O buffer decodificado do buffer de entrada acima
//
char[] decoded_buffer
;
// Bytes decodificados em decoded_buffer.
//
int decoded_count
;
// Posição atual no decoded_buffer
//
posição interna
//
//Posição atual no input_buffer
//
int pos_input
;
//O tamanho do buffer que estamos usando
//
int buffer_size;
int do_checks;
Codificação de codificação;
Decodificador decodificador
de fluxo base_stream;
bool mayBlock;
StringBuilder line_builder;
classe privada NullStreamReader: DynamicStreamReader
{
substituição pública int Peek ()
{
retornar -1;
}
substituição pública int Read()
{
retornar -1;
}
public override int Read ([In, Out] char[] buffer, int index, int count)
{
retornar 0;
}
string de substituição pública ReadLine ()
{
retornar nulo;
}
string de substituição pública ReadToEnd ()
{
retornar String.Empty;
}
substituição pública Stream BaseStream
{
obter {retornar Stream.Null};
}
substituição pública Codificação CurrentEncoding
{
obter {retornar Codificação.Unicode};
}
}
public new static readonly DynamicStreamReader Null = (DynamicStreamReader)(new NullStreamReader());
interno DynamicStreamReader() {}
public DynamicStreamReader (stream stream)
: this (stream, Encoding.UTF8, true, DefaultBufferSize) { }
public DynamicStreamReader (Stream stream, bool detect_encoding_from_bytemarks)
: this (stream, Encoding.UTF8, detect_encoding_from_bytemarks, DefaultBufferSize) { }
public DynamicStreamReader(Stream stream, Encoding encoding)
: this (stream, encoding, true, DefaultBufferSize) { }
public DynamicStreamReader (Stream stream, Encoding encoding, bool detect_encoding_from_bytemarks)
: isto (stream, codificação, detect_encoding_from_bytemarks, DefaultBufferSize) { }
public DynamicStreamReader (fluxo de fluxo, codificação de codificação, bool detect_encoding_from_bytemarks, int buffer_size)
{
Inicializar (stream, codificação, detect_encoding_from_bytemarks, buffer_size);
}
public DynamicStreamReader(caminho da string)
: este (caminho, Encoding.UTF8, true, DefaultFileBufferSize) { }
public DynamicStreamReader (caminho da string, bool detect_encoding_from_bytemarks)
: este (caminho, Encoding.UTF8, detect_encoding_from_bytemarks, DefaultFileBufferSize) { }
public DynamicStreamReader (caminho da string, codificação de codificação)
: este (caminho, codificação, verdadeiro, DefaultFileBufferSize) { }
public DynamicStreamReader (caminho da string, codificação de codificação, bool detect_encoding_from_bytemarks)
: este (caminho, codificação, detect_encoding_from_bytemarks, DefaultFileBufferSize) { }
public DynamicStreamReader (caminho da string, codificação de codificação, bool detect_encoding_from_bytemarks, int buffer_size)
{
if (nulo == caminho)
lançar novo ArgumentNullException("caminho");
if (String.Empty == caminho)
throw new ArgumentException("Caminho vazio não permitido");
if (path.IndexOfAny (Path.InvalidPathChars)! = -1)
throw new ArgumentException("caminho contém caracteres inválidos");
if (nulo == codificação)
lançar novo ArgumentNullException ("codificação");
if (buffer_size <= 0)
throw new ArgumentOutOfRangeException ("buffer_size", "O tamanho mínimo do buffer deve ser positivo")
;
if (DirName! = String.Empty &&! Directory.Exists(DirName))
lançar novo DirectoryNotFoundException ("Diretório '" + DirName + "' não encontrado.");
if (!Arquivo.Exists(caminho))
throw new FileNotFoundException("Arquivo não encontrado.", caminho
Stream stream = (Stream) File.OpenRead (caminho);
Inicializar (stream, codificação, detect_encoding_from_bytemarks, buffer_size);
}
internal void Initialize (Stream stream, Encoding encoding, bool detect_encoding_from_bytemarks, int buffer_size)
{
if (nulo == fluxo)
lançar novo ArgumentNullException ("stream");
if (nulo == codificação)
lançar novo ArgumentNullException ("codificação");
se (!stream.CanRead)
lançar novo ArgumentException ("Não é possível ler o fluxo");
if (buffer_size <= 0)
throw new ArgumentOutOfRangeException ("buffer_size", "O tamanho mínimo do buffer deve ser positivo")
;
buffer_size = MínimoBufferSize;
base_stream = fluxo;
input_buffer = novo byte [buffer_size];
this.buffer_size = buffer_size;
this.encoding = codificação;
decodificador = encoding.GetDecoder ();
byte [] preâmbulo = encoding.GetPreamble ();
do_checks=detect_encoding_from_bytemarks ?
do_checks += (preâmbulo.Comprimento == 0) ?
decoded_buffer = novo caractere [encoding.GetMaxCharCount (buffer_size)];
contagem_decodificada = 0;
posição = 0;
entrada_pos =0;
}
fluxo virtual público BaseStream
{
pegar
{
retornar base_stream;
}
}
codificação virtual pública CurrentEncoding
{
pegar
{
if (codificação == nulo)
lançar nova exceção();
codificação de retorno;
}
definir
{
codificação=valor;
decodificador = encoding.GetDecoder();
decoded_count = pos + decoder.GetChars (input_buffer, pos_input, cbEncoded - pos_input, decoded_buffer, pos);
//DiscardBufferedData();
}
}
substituição pública void Fechar ()
{
Dispor(verdadeiro);
}
substituição protegida void Dispose (descarte de bool)
{
if (dispondo && base_stream! = Nulo)
base_stream.Close();
buffer_de entrada = nulo;
buffer_decodificado = nulo;
codificação = nulo;
decodificador = nulo;
fluxo_base = nulo;
base.Dispose (descartando);
}
//
// Fornece detecção automática da codificação, bem como pular
//marcas de bytes no início de um stream.
//
int DoChecks (contagem interna)
{
se ((fazer_verificações & 2) == 2)
{
byte [] preâmbulo = encoding.GetPreamble();
int c = preâmbulo.Comprimento;
se (contagem >= c)
{
int eu;
para (eu = 0; eu <c; eu++)
if (input_buffer [i]! = preâmbulo [i])
quebrar;
se (eu == c)
retornar eu;
}
}
if ((do_checks & 1) == 1)
{
se (contagem <2)
retornar 0
se (buffer de entrada [0] == 0xfe && buffer_de entrada [1] == 0xff)
{
this.encoding = Encoding.BigEndianUnicode;
retorno 2;
}
if (input_buffer [0] == 0xff && input_buffer [1] == 0xfe)
{
this.encoding = Encoding.Unicode;
retorno 2;
}
se (contagem <3)
retornar 0
se (buffer de entrada [0] == 0xef && buffer_de entrada [1] == 0xbb && buffer_de entrada [2] == 0xbf)
{
this.encoding = Encoding.UTF8;
retorno 3;
}
}
retornar 0;
}
public void DiscardBufferedData()
{
pos = contagem_decodificada = 0;
mayBlock = falso;
// Descarta também o estado interno do decodificador.
decodificador=encoding.GetDecoder();
}
int cbEncoded;
int parse_start;
// o buffer está vazio, preencha-o novamente
privado int ReadBuffer ()
{
posição = 0;
entrada_pos = 0;
cbEncoded = 0;
// continua em loop até que o decodificador nos forneça alguns caracteres
contagem_decodificada = 0;
parse_start = 0;
fazer
{
cbEncoded = base_stream.Read (input_buffer, 0, buffer_size);
if (cbEncoded == 0)
retornar 0;
mayBlock = (cbEncoded <buffer_size);
if (do_checks > 0)
{
Codificação antiga = codificação;
parse_start = DoChecks (cbEncoded);
if (antigo! = codificação)
{
decodificador=encoding.GetDecoder();
}
do_checks = 0;
cbEncoded -= parse_start;
}
decoded_count += decoder.GetChars (input_buffer, parse_start, cbEncoded, decoded_buffer, 0);
parse_start = 0;
} while (contagem_decodificada == 0);
return contagem_decodificada;
}
substituição pública int Peek()
{
if (base_stream == nulo)
throw new ObjectDisposedException ("StreamReader", "Não é possível ler de um StreamReader fechado");
if (pos >= decoded_count && (mayBlock || ReadBuffer () == 0))
retornar -1;
retornar buffer_decodificado [pos];
}
substituição pública int Read()
{
throw new Exception("O Leitor Dinâmico não conseguiu ler!");
}
substituição pública int Read ([In, Out] char[] dest_buffer, int index, int count)
{
throw new Exception("O Leitor Dinâmico não conseguiu ler!");
}
bool encontradoCR_input;
int FindNextInputEOL()
{
caractere c = '