元の URL: http://www.blogwind.com/Wuvist/42999.shtml
.Net Framework では、StreamReader で使用するエンコーディングをコンストラクターで指定する必要があり、途中で変更することはできません。
通常の状況では、これによって問題が発生することはありません。一般に、ファイルがハードディスクから読み取られる場合、単一ファイル内のエンコードは通常均一です。読み取りエラーが見つかった場合でも、StreamReader を閉じて、新しいエンコーディングを使用して読み取りを再開できます。
最近、エンコードを変更する必要が生じましたが、プログラムによって再読み込みの機会が無効になることはありませんでした。私が使用している StreamReader の BaseStream はネットワーク ストリームなので、閉じることができません...しかし、ネットワーク ストリームによって渡されるものには、異なるエンコーディングが含まれている可能性があります... GB2312、Big5、UTF8、ISO-8859-1 など。 .. 最初にエンコード情報を取得してから、特定のコンテンツを読み込みますが、最初に使用された Stream Reader のエンコードが間違っていると、読み込んだコンテンツを復元することはできません...言葉が失われます
...再度取得できません...情報をエンコードした後、新しいストリーム リーダーを再作成します。特定のコンテンツは元のストリーム リーダーによってバッファリングされているためです...
唯一の解決策は、ストリーム リーダーを変更できるストリーム リーダーを実装することです。 CurrentEncoding 属性...
すべてを最初から作成する 最初は、mono のソース コードを入手し、mono の Stream Reader 実装コードから修正を加えました。
ストリーム リーダーは実際には非常に単純です。内部には 2 つのバッファがあり、1 つは入力バッファ、もう 1 つはベース ストリームから読み取られた元のデータをキャッシュするために使用され、後者はデータをキャッシュするために使用されます。元のデータに従ってデコードされます... ...mono の実装の ReadBuffer メソッドを理解している限り、CurrentEncoding を動的に変更することはそれほど難しくありません...
扱う必要があるネットワーク プロトコルは回線プロトコルです...プログラム内で StreamReader の Readline メソッドを呼び出しただけですが、Read の 2 つのメソッドは完全に使用されていないため、エンコーディングを動的に変更するのがはるかに簡単になります...
私がしていることは、毎回Readline を呼び出すと、デコードされたバッファのカーソル (pos) を移動するだけでなく、入力バッファ (pos_input) 内の新しいカーソルも移動します。このメソッドは、カーソルを移動するために FindNextEOL を呼び出す必要があります。改行記号を見つけます... FindNextEOL メソッドにもう 1 行追加します。
int FindNextEOL()
{
FindNextInputEOL();
....
新しい関数 FindNextInputEOL は FindNextEOL の完全なレプリカですが、前者が入力バッファを処理するのに対し、後者はデコードされたバッファを処理する点
が異なります。このようにして、各 Readline の後に入力バッファが上位層によってデコードされていません。読み取られた元のデータは何ですか...
次に、Set メソッドを CurrentEncoding 属性に追加します。
セット
{
エンコーディング=値;
デコーダー = エンコーディング.GetDecoder();
decoded_count = pos + decoder.GetChars (input_buffer, pos_input, cbEncoded, pos_input, decoded_buffer, pos);
、
プログラムは入力バッファー (pos_input) のカーソルに従って、読み込まれていない元のデータを再デコードし、デコードされたバッファーの内容を置き換えます。
これで、作業は完了です... Readline メソッドに変更を加える必要さえありません... cbEncoded 変数をグローバルに置くことを除いて... ただし
、この変更により、Read の 2 つのメソッドが完全に使用できなくなります... 一度呼び出されると、入力バッファとデコードされたバッファの 2 つのカーソルが同期しなくなります... 完全なコードを以下に添付します。誰かが Read の 2 つのメソッドを理解するのを手伝ってくれることを願っています。 ... 前もって感謝します …
/
// System.IO.StreamReader.cs
//
// 著者:
// ディートマール・マウラー ( [email protected] )
// ミゲル・デ・イカザ ( [email protected] )
//
// (C)Ximian, Inc. http://www.ximian.com
// 著作権 (C) 2004 Novell ( http://www.novell.com )
//
//
// 著作権 (C) 2004 Novell, Inc ( http://www.novell.com )
//
// ここで、以下を取得する人には無償で許可が与えられます。
// このソフトウェアと関連ドキュメント ファイルのコピー (
//「ソフトウェア」)、以下を含むソフトウェアを制限なく取り扱うこと。
// 制限なく、使用、コピー、変更、マージ、公開、
// ソフトウェアのコピーを配布、サブライセンス、および/または販売すること、および
// ソフトウェアが提供される人に、以下の条件に従って提供することを許可します。
// 以下の条件:
//
// 上記の著作権表示とこの許可表示は、
// ソフトウェアのすべてのコピーまたは実質的な部分に含まれます。
//
// ソフトウェアは「現状のまま」提供され、いかなる種類の保証もありません。
// 明示的または黙示的、保証を含みますがこれに限定されません。
// 商品性、特定の目的への適合性、および
// 非侵害。いかなる場合でも、著者または著作権者は権利を侵害しません。
// 行為中であるかどうかにかかわらず、あらゆる請求、損害賠償、またはその他の責任に対して責任を負います
// 契約に起因、起因、または関連して生じる、不法行為またはその他の契約
// ソフトウェア、またはソフトウェアの使用またはその他の取引について。
//
システムを使用します。
System.Text を使用します。
System.Runtime.InteropServices
名前空間 System.IO
を使用します。
{
【シリアライズ可能】
パブリック クラス DynamicStreamReader : TextReader
{
const int DefaultBufferSize = 1024;
const int DefaultFileBufferSize = 4096;
const int MinimumBufferSize = 128
;
//入力バッファ
//
バッファ
;
// 上記の入力バッファからデコードされたバッファ
//
char [] デコードバッファ;
//
// decoded_buffer 内のデコードされたバイト。
//
int decoded_count
;
// decoded_buffer 内の現在位置
//
int pos
;
// input_buffer 内の現在位置
//
int pos_input
;
// 使用しているバッファ サイズ
//
int バッファサイズ;
int do_checks;
エンコーディングエンコーディング;
デコーダ デコーダ
;
ブール MayBlock;
StringBuilder line_builder;
プライベート クラス NullStreamReader : DynamicStreamReader
{
パブリックオーバーライド int Peek ()
{
-1 を返します。
オーバーライド
int Read()
{
-1 を返します。
public override int Read ([In, Out] char[] バッファ、int インデックス、int カウント
)
{
0を返します。
オーバーライド
文字列ReadLine()
{
null を返します。
パブリック
オーバーライド文字列 ReadToEnd ()
{
String.Empty を返します。
パブリック
オーバーライドストリームBaseStream
{
get { Stream.Null を返す }
public
オーバーライド Encoding CurrentEncoding
{
get { return Encoding.Unicode }
}
public
new static readonly DynamicStreamReader Null = (DynamicStreamReader)(new NullStreamReader());
内部 DynamicStreamReader() {}
public DynamicStreamReader(ストリーム ストリーム)
: 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(ストリーム ストリーム, Encoding エンコーディング)
: this (ストリーム、エンコーディング、true、DefaultBufferSize) { }
public DynamicStreamReader(ストリーム ストリーム、エンコーディング エンコーディング、ブール detect_encoding_from_bytemarks)
: this (ストリーム、エンコーディング、detect_encoding_from_bytemarks、DefaultBufferSize) { }
public DynamicStreamReader(ストリームストリーム、エンコーディングencoding、bool detect_encoding_from_bytemarks、intbuffer_size)
{
初期化 (ストリーム、エンコーディング、detect_encoding_from_bytemarks、buffer_size);
public
DynamicStreamReader(文字列パス)
: this (path, Encoding.UTF8, true, DefaultFileBufferSize) { }
public DynamicStreamReader(string path, bool detect_encoding_from_bytemarks)
: this (path, Encoding.UTF8, detect_encoding_from_bytemarks, DefaultFileBufferSize) { }
public DynamicStreamReader(文字列パス, Encoding エンコーディング)
: this (パス、エンコーディング、true、DefaultFileBufferSize) { }
public DynamicStreamReader(string path, Encoding エンコーディング, bool detect_encoding_from_bytemarks)
: this (パス、エンコーディング、detect_encoding_from_bytemarks、DefaultFileBufferSize) { }
public DynamicStreamReader(文字列パス、エンコーディングencoding、bool detect_encoding_from_bytemarks、intbuffer_size)
{
if (null == パス)
throw new ArgumentNullException("パス");
if (String.Empty == パス)
throw new ArgumentException("空のパスは許可されません");
if (path.IndexOfAny (Path.InvalidPathChars) != -1)
throw new ArgumentException("パスに無効な文字が含まれています");
if (null == エンコーディング)
新しい ArgumentNullException (「エンコーディング」) をスローします。
if (buffer_size <= 0)
throw new ArgumentOutOfRangeException ("buffer_size", "バッファの最小サイズは正の値である必要があります");
string DirName = Path.GetDirectoryName(path);
if (DirName != String.Empty && !Directory.Exists(DirName))
throw new DirectoryNotFoundException ("ディレクトリ '" + DirName + "' が見つかりません。");
if (!File.Exists(パス))
throw new FileNotFoundException("ファイルが見つかりません。", パス);
ストリーム stream = (ストリーム) File.OpenRead (パス);
初期化 (ストリーム、エンコーディング、detect_encoding_from_bytemarks、buffer_size);
}
内部 void 初期化 (ストリーム ストリーム、エンコーディング エンコーディング、ブール型 detect_encoding_from_bytemarks、int バッファサイズ)
{
if (null == ストリーム)
新しい ArgumentNullException (「ストリーム」) をスローします。
if (null == エンコーディング)
新しい ArgumentNullException (「エンコーディング」) をスローします。
if (!stream.CanRead)
throw new ArgumentException (「ストリームを読み取れません」);
if (buffer_size <= 0)
throw new ArgumentOutOfRangeException ("buffer_size", "バッファの最小サイズは正の値である必要があります")
if (buffer_size < MinimumBufferSize);
バッファサイズ = 最小バッファ
サイズ = ストリーム;
input_buffer = 新しいバイト [buffer_size];
this.buffer_size = バッファサイズ;
this.encoding = エンコーディング;
デコーダ = エンコーディング.GetDecoder ();
バイト [] プリアンブル = エンコーディング.GetPreamble ();
do_checks = detect_encoding_from_bytemarks ? 1:0;
do_checks += (プリアンブル.長さ == 0) 0 : 2;
decoded_buffer = 新しい文字 [encoding.GetMaxCharCount (buffer_size)];
デコード数 = 0;
位置 = 0;
pos_input =0;
パブリック
仮想ストリームBaseStream
{
得る
{
ベースストリームを返す;
}
パブリック
仮想エンコーディング CurrentEncoding
{
得る
{
if (エンコーディング == null)
新しい例外をスローします ();
エンコーディングを返します。
}
セット
{
エンコーディング=値;
デコーダー = エンコーディング.GetDecoder();
decoded_count = pos + decoder.GetChars (input_buffer, pos_input, cbEncoded - pos_input, decoded_buffer, pos);
//DiscardBufferedData();
}
オーバーライド
void Close()
{
破棄(true);
protected
オーバーライド void Dispose (bool 破棄)
{
if (&&base_stream != null を破棄)
Base_stream.Close();
入力バッファ = null;
デコードされたバッファ = null;
エンコーディング = null;
デコーダ = null;
ベースストリーム = null;
Base.Dispose (処分);
}
//
// エンコーディングの自動検出とスキップを提供します。
// ストリームの先頭のバイト マーク。
//
int DoChecks (int カウント)
{
if ((do_checks & 2) == 2)
{
byte [] プリアンブル = エンコーディング.GetPreamble ();
int c = プリアンブル.長さ;
if (カウント >= c)
{
int i;
for (i = 0; i < c; i++)
if (入力バッファ [i] != プリアンブル [i])
ブレーク;
if (i == c)
私を返します。
}
if
((do_checks & 1) == 1)
{
if (カウント < 2)
0 を返す;
if (入力バッファ [0] == 0xfe && 入力バッファ [1] == 0xff)
{
this.encoding = エンコーディング.BigEndianUnicode;
2を返します。
if
(入力バッファ [0] == 0xff && 入力バッファ [1] == 0xfe)
{
this.encoding = エンコーディング.Unicode;
2を返します。
if
(カウント < 3)
0 を返す;
if (入力バッファ [0] == 0xef && 入力バッファ [1] == 0xbb && 入力バッファ [2] == 0xbf)
{
this.encoding = エンコーディング.UTF8;
3 を返します。
}
0を返します
。
public void DiscardBufferedData(
)
{
pos = デコードカウント = 0;
MayBlock = false;
// デコーダの内部状態も破棄します。
デコーダー = エンコーディング.GetDecoder();
}
int cbEncoded;
int parse_start;
// バッファが空なので、再度バッファを埋めます
private int ReadBuffer ()
{
位置 = 0;
pos_input = 0;
cbEncoded = 0;
// デコーダがいくつかの文字を取得するまでループを続けます。
デコード数 = 0;
解析開始 = 0;
する
{
cbEncoded = Base_stream.Read (入力バッファ, 0, バッファサイズ);
if (cbEncoded == 0)
0 を返します。mayBlock
= (cbEncoded < バッファサイズ);
if (do_checks > 0)
{
古いエンコーディング = エンコーディング;
parse_start = DoChecks (cbEncoded);
if (古い != エンコーディング)
{
デコーダー = エンコーディング.GetDecoder();
}
do_checks = 0;
cbEncoded -= parse_start;
}
decoded_count += decoder.GetChars (input_buffer, parse_start, cbEncoded, decoded_buffer, 0);
解析開始 = 0;
while (デコードされたカウント == 0)
;
public オーバーライド int Peek (
)
{
if (base_stream == null)
throw new ObjectDissolvedException ("StreamReader"、"閉じた StreamReader から読み取れません");
if (pos >= decoded_count && (mayBlock || ReadBuffer () == 0))
-1 を返す;
デコードされたバッファを返す [pos];
オーバーライド
int Read()
{
throw new Exception("ダイナミック リーダーが読み取れませんでした!");
public override int Read ([In, Out] char[] dest_buffer, int インデックス, int count
)
{
throw new Exception("ダイナミック リーダーが読み取れませんでした!");
ブール
値が見つかりましたCR_input;
int FindNextInputEOL()
{
char c = '