Ursprüngliche URL: http://www.blogwind.com/Wuvist/42999.shtml
Im .Net-Framework muss die von StreamReader verwendete Codierung im Konstruktor angegeben werden und kann auf halbem Weg überhaupt nicht geändert werden.
Unter normalen Umständen wird dies keine Probleme verursachen. Wenn eine Datei von einer Festplatte gelesen wird, ist die Codierung innerhalb einer einzelnen Datei im Allgemeinen einheitlich. Selbst wenn Sie einen Lesefehler finden, können Sie den StreamReader schließen und den Lesevorgang mit der neuen Kodierung neu starten.
Ich bin vor Kurzem auf die Notwendigkeit gestoßen, die Codierung zu ändern, und mein Programm hat die Möglichkeit zum erneuten Lesen nicht deaktiviert. Da der BaseStream des StreamReader, den ich verwende, ein Netzwerk-Stream ist, kann ich ihn nicht schließen ... aber die vom Netzwerk-Stream übergebenen Dinge können unterschiedliche Codierungen enthalten ... GB2312, Big5, UTF8, ISO-8859-1 usw. .. Obwohl zuerst die Codierungsinformationen abgerufen und dann der spezifische Inhalt gelesen wird, kann der gelesene Inhalt jedoch nie wiederhergestellt werden, sobald die zu Beginn verwendete Stream Reader-Codierung falsch ist ... Wörter gehen verloren
... Ich kann es nicht wieder abrufen ... Erstellen Sie nach dem Codieren der Informationen einen neuen Stream Reader neu, da der spezifische Inhalt vom ursprünglichen Stream Reader gepuffert wurde ...
Die einzige Lösung besteht darin, einen Stream Reader zu implementieren, der das ändern kann CurrentEncoding-Attribut ...
Alles von Grund auf neu schreiben. Zuerst war es sehr unpraktisch, den Quellcode von Mono zu erhalten und Änderungen am Stream Reader-Implementierungscode von Mono vorzunehmen.
Der Stream Reader ist eigentlich sehr einfach. Er enthält zwei Puffer, einen als Eingabepuffer und einen als dekodierten Puffer. Ersterer wird zum Zwischenspeichern der aus dem Basisstream gelesenen Originaldaten und letzterer zum Zwischenspeichern der Dinge verwendet Gemäß den Originaldaten dekodiert... ...Solange Sie die ReadBuffer-Methode in der Mono-Implementierung verstehen, ist es nicht allzu schwierig, CurrentEncoding dynamisch zu ändern...
Das Netzwerkprotokoll, mit dem ich mich befassen muss, ist ein Leitungsprotokoll ...Ich habe im Programm nur die Readline-Methode von StreamReader aufgerufen, aber vollständig. Die beiden Methoden von Read werden nicht verwendet, was es mir auch viel einfacher macht, die Codierung dynamisch zu ändern ...
Was ich mache, ist, dass ich jedes Mal Wenn ich Readline aufrufe, bewege ich nicht nur den Cursor (pos) des dekodierten Puffers, sondern auch einen neuen Cursor im Eingabepuffer (pos_input). Die Readline-Methode muss FindNextEOL aufrufen, um den Cursor zu bewegen Finden Sie das Newline-Symbol ... Ich füge der FindNextEOL-Methode eine weitere Zeile hinzu:
int FindNextEOL()
{
FindNextInputEOL();
....
Die neue Funktion FindNextInputEOL ist eine vollständige Nachbildung von FindNextEOL, mit der Ausnahme, dass erstere den Eingabepuffer verarbeitet, während letztere den dekodierten Puffer verarbeitet ...
Auf diese Weise kann ich wissen, dass nach jeder Readline der Eingabepuffer vorhanden ist wurden nicht von der oberen Ebene dekodiert. Welche Originaldaten wurden gelesen?
Fügen Sie dann die Set-Methode zum CurrentEncoding-Attribut hinzu:
Satz
{
Kodierung=Wert;
decoder =kodierung.GetDecoder();
decoded_count = pos + decoder.GetChars (input_buffer, pos_input, cbEncoded, pos_input, decoded_buffer, pos);
}
Beim Festlegen einer neuen Kodierung dekodiert das Programm die nicht gelesenen Originaldaten entsprechend dem Cursor des Eingabepuffers (pos_input) neu und ersetzt den Inhalt im dekodierten Puffer.
Dann ist die Sache erledigt ... Sie müssen nicht einmal Änderungen an der Readline-Methode vornehmen ... Außer, dass Sie die Variable cbEncoded in die globale Variable einfügen ...
Diese Änderung macht jedoch die beiden Methoden von Read völlig unbrauchbar ... Einmal aufgerufen ... führt dies dazu, dass die beiden Cursor im Eingabepuffer und im dekodierten Puffer nicht synchron sind ... Der vollständige Code ist unten angehängt. Ich hoffe, jemand kann mir helfen, die beiden Lesemethoden herauszufinden ... Dank im Voraus …
/
// 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 )
//
// Die Erlaubnis wird hiermit jeder Person, die sie erhält, kostenlos erteilt
// eine Kopie dieser Software und der zugehörigen Dokumentationsdateien (die
// „Software“), mit der Software uneingeschränkt zu handeln, einschließlich
// ohne Einschränkung die Rechte zur Nutzung, zum Kopieren, Ändern, Zusammenführen, Veröffentlichen,
// Kopien der Software vertreiben, unterlizenzieren und/oder verkaufen, und an
// Erlauben Sie Personen, denen die Software zur Verfügung gestellt wird, dies, vorbehaltlich
// die folgenden Bedingungen:
//
// Der obige Urheberrechtshinweis und dieser Erlaubnishinweis gelten
// in allen Kopien oder wesentlichen Teilen der Software enthalten.
//
// DIE SOFTWARE WIRD „WIE BESEHEN“ ZUR VERFÜGUNG GESTELLT, OHNE JEGLICHE GEWÄHRLEISTUNG,
// AUSDRÜCKLICH ODER STILLSCHWEIGEND, EINSCHLIESSLICH, ABER NICHT BESCHRÄNKT AUF DIE GEWÄHRLEISTUNGEN VON
// MARKTGÄNGIGKEIT, EIGNUNG FÜR EINEN BESTIMMTEN ZWECK UND
// NICHTVERLETZUNG DER RECHTE
// HAFTBAR FÜR JEGLICHE ANSPRÜCHE, SCHÄDEN ODER ANDERE HAFTUNG, SEI ES IN EINER KLAGE
// AUS VERTRAG, UNERLAUBTER HANDLUNG ODER ANDERWEITIG, DIE SICH AUS, AUS ODER IN ZUSAMMENHANG ERGEBEN
// MIT DER SOFTWARE ODER DER NUTZUNG ODER ANDEREN HANDELN MIT DER SOFTWARE.
//
using System;
Verwenden von System.Text;
mit System.Runtime.InteropServices;
Namespace System.IO
{
[Serialisierbar]
öffentliche Klasse DynamicStreamReader: TextReader
{
const int DefaultBufferSize = 1024;
const int DefaultFileBufferSize = 4096;
const int MinimumBufferSize = 128;
//
//Der Eingabepuffer
//
byte [] input_buffer;
//
// Der dekodierte Puffer aus dem obigen Eingabepuffer
//
char [] decoded_buffer;
//
// Decodierte Bytes in decoded_buffer.
//
int decoded_count;
//
// Aktuelle Position im decoded_buffer
//
int pos;
//
// Aktuelle Position im input_buffer
//
int pos_input;
//
// Die Puffergröße, die wir verwenden
//
int buffer_size;
int do_checks;
Kodierungskodierung;
Decoder-Decoder;
Stream base_stream;
bool mayBlock;
StringBuilder line_builder;
private Klasse NullStreamReader: DynamicStreamReader
{
öffentliches Override int Peek()
{
return -1;
}
public override int Read()
{
return -1;
}
public override int Read ([In, Out] char[] buffer, int index, int count)
{
0 zurückgeben;
}
öffentliche Überschreibungszeichenfolge ReadLine ()
{
null zurückgeben;
}
öffentliche Überschreibungszeichenfolge ReadToEnd ()
{
return String.Empty;
}
public überschreibt Stream BaseStream
{
get { return Stream.Null }
}
public override Encoding CurrentEncoding
{
get { return Encoding.Unicode }
}
}
public new static readonly DynamicStreamReader Null = (DynamicStreamReader)(new NullStreamReader());
internal DynamicStreamReader() {}
public DynamicStreamReader(Stream stream)
: this (stream, Encoding.UTF8, true, DefaultBufferSize) { }
public DynamicStreamReader(Stream stream, bool discover_encoding_from_bytemarks)
: this (stream, Encoding.UTF8, discover_encoding_from_bytemarks, DefaultBufferSize) { }
public DynamicStreamReader(Stream stream, Codierung kodieren)
: this (Stream, Encoding, True, DefaultBufferSize) { }
public DynamicStreamReader(Stream Stream, Encoding Encoding, BoolDetect_encoding_from_bytemarks)
: this (Stream, Encoding, discover_encoding_from_bytemarks, DefaultBufferSize) { }
public DynamicStreamReader(Stream stream, Codierung kodieren, bool discover_encoding_from_bytemarks, int buffer_size)
{
Initialisieren (Stream, Codierung, discover_encoding_from_bytemarks, buffer_size);
}
public DynamicStreamReader(string path)
: this (path, Encoding.UTF8, true, DefaultFileBufferSize) { }
public DynamicStreamReader(string path, bool discover_encoding_from_bytemarks)
: this (path, Encoding.UTF8, discover_encoding_from_bytemarks, DefaultFileBufferSize) { }
public DynamicStreamReader(string path, Codierung kodieren)
: this (Pfad, Codierung, true, DefaultFileBufferSize) { }
public DynamicStreamReader(string path, Codierung codieren, bool discover_encoding_from_bytemarks)
: this (Pfad, Kodierung, discover_encoding_from_bytemarks, DefaultFileBufferSize) { }
public DynamicStreamReader(String-Pfad, Kodierungskodierung, bool discover_encoding_from_bytemarks, int buffer_size)
{
if (null == Pfad)
throw new ArgumentNullException("path");
if (String.Empty == Pfad)
throw new ArgumentException("Leerer Pfad nicht zulässig");
if (path.IndexOfAny (Path.InvalidPathChars) != -1)
throw new ArgumentException("Pfad enthält ungültige Zeichen");
if (null == Kodierung)
throw new ArgumentNullException ("encoding");
if (buffer_size <= 0)
throw new ArgumentOutOfRangeException ("buffer_size", "Die Mindestgröße des Puffers muss positiv sein");
string DirName = Path.GetDirectoryName(path);
if (DirName != String.Empty && !Directory.Exists(DirName))
throw new DirectoryNotFoundException ("Verzeichnis '" + DirName + "' nicht gefunden.");
if (!File.Exists(path))
throw new FileNotFoundException("Datei nicht gefunden.", path);
Stream stream = (Stream) File.OpenRead (path);
Initialisieren (Stream, Codierung, discover_encoding_from_bytemarks, buffer_size);
}
internal void Initialize (Stream-Stream, Encoding-Codierung, bool discover_encoding_from_bytemarks, int buffer_size)
{
if (null == Stream)
throw new ArgumentNullException ("stream");
if (null == Kodierung)
throw new ArgumentNullException ("encoding");
if (!stream.CanRead)
throw new ArgumentException („Stream kann nicht gelesen werden“);
if (buffer_size <= 0)
throw new ArgumentOutOfRangeException ("buffer_size", "Die Mindestgröße des Puffers muss positiv sein");
if (buffer_size < MinimumBufferSize)
buffer_size = MinimumBufferSize;
base_stream = stream;
input_buffer = neues Byte [buffer_size];
this.buffer_size = buffer_size;
this.encoding = Kodierung;
decoder = Encoding.GetDecoder ();
Byte [] Preamble = Encoding.GetPreamble ();
do_checks = discover_encoding_from_bytemarks 1 : 0;
do_checks += (preamble.Length == 0) 0 : 2;
decoded_buffer = new char [encoding.GetMaxCharCount (buffer_size)];
decoded_count = 0;
pos = 0;
pos_input =0;
}
öffentlicher virtueller Stream BaseStream
{
erhalten
{
return base_stream;
}
}
public virtual Encoding CurrentEncoding
{
erhalten
{
if (Kodierung == null)
wirf eine neue Ausnahme ();
Rückgabekodierung;
}
Satz
{
Kodierung=Wert;
decoder =kodierung.GetDecoder();
decoded_count = pos + decoder.GetChars (input_buffer, pos_input, cbEncoded - pos_input, decoded_buffer, pos);
//DiscardBufferedData();
}
}
public override void Close ()
{
Entsorgen(true);
}
protected override void Dispose (bool entsorgen)
{
if (Entsorgen von && base_stream != null)
base_stream.Close ();
input_buffer = null;
decoded_buffer = null;
Kodierung = null;
Decoder = null;
base_stream = null;
base.Dispose (entsorgen);
}
//
// Bietet automatische Erkennung der Kodierung sowie Überspringen
// Bytemarkierungen am Anfang eines Streams.
//
int DoChecks (int count)
{
if ((do_checks & 2) == 2)
{
byte [] preamble =kodierung.GetPreamble ();
int c = preamble.Length;
if (Anzahl >= c)
{
int i;
für (i = 0; i < c; i++)
if (input_buffer [i] != Präambel [i])
Pause;
wenn (i == c)
gib i zurück;
}
}
if ((do_checks & 1) == 1)
{
wenn (Anzahl < 2)
return 0;
if (input_buffer [0] == 0xfe && input_buffer [1] == 0xff)
{
this.encoding = Encoding.BigEndianUnicode;
Rückkehr 2;
}
if (input_buffer [0] == 0xff && input_buffer [1] == 0xfe)
{
this.encoding = Encoding.Unicode;
Rückkehr 2;
}
if (count < 3)
return 0;
if (input_buffer [0] == 0xef && input_buffer [1] == 0xbb && input_buffer [2] == 0xbf)
{
this.encoding = Encoding.UTF8;
Rückkehr 3;
}
}
return 0;
}
public void DiscardBufferedData ()
{
pos = decoded_count = 0;
mayBlock = false;
// Auch den internen Status des Decoders verwerfen.
decoder = Encoding.GetDecoder ();
}
int cbEncoded;
int parse_start;
// Der Puffer ist leer, fülle ihn erneut
private int ReadBuffer()
{
pos = 0;
pos_input = 0;
cbEncoded = 0;
// weitermachen, bis der Decoder uns einige Zeichen liefert
decoded_count = 0;
parse_start = 0;
Tun
{
cbEncoded = base_stream.Read (input_buffer, 0, buffer_size);
if (cbEncoded == 0)
return 0;
mayBlock = (cbEncoded < buffer_size);
if (do_checks > 0)
{
Kodierung alt = Kodierung;
parse_start = DoChecks (cbEncoded);
if (alt != Kodierung)
{
decoder = Encoding.GetDecoder ();
}
do_checks = 0;
cbEncoded -= parse_start;
}
decoded_count += decoder.GetChars (input_buffer, parse_start, cbEncoded, decoded_buffer, 0);
parse_start = 0;
} while (decoded_count == 0);
return decoded_count;
}
public override int Peek()
{
if (base_stream == null)
throw new ObjectDisposedException („StreamReader“, „Kann nicht aus einem geschlossenen StreamReader lesen“);
if (pos >= decoded_count && (mayBlock || ReadBuffer () == 0))
return -1;
return decoded_buffer [pos];
}
public override int Read()
{
throw new Exception("Dynamic Reader konnte nicht lesen!");
}
public override int Read ([In, Out] char[] dest_buffer, int index, int count)
{
throw new Exception("Dynamic Reader konnte nicht lesen!");
}
bool FoundCR_input;
int FindNextInputEOL()
{
char c = '