URL ดั้งเดิม: http://www.blogwind.com/Wuvist/42999.shtml
ในกรอบงาน .Net การเข้ารหัสที่ใช้โดย StreamReader จะต้องระบุในตัวสร้างและไม่สามารถเปลี่ยนแปลงได้เลยกลางคัน
ภายใต้สถานการณ์ปกติ สิ่งนี้จะไม่ทำให้เกิดปัญหาใดๆ โดยทั่วไป หากไฟล์ถูกอ่านจากฮาร์ดดิสก์ การเข้ารหัสภายในไฟล์เดียวโดยทั่วไปจะเหมือนกัน แม้ว่าคุณจะพบข้อผิดพลาดในการอ่าน คุณสามารถปิด StreamReader และเริ่มอ่านใหม่โดยใช้การเข้ารหัสใหม่ได้
ฉันเพิ่งพบความจำเป็นในการแก้ไขการเข้ารหัส และโปรแกรมของฉันก็ไม่ได้ปิดโอกาสในการอ่านซ้ำ เนื่องจาก BaseStream ของ StreamReader ที่ฉันใช้คือ Network Stream ฉันจึงไม่สามารถปิดได้... แต่สิ่งที่ส่งผ่าน Network Stream อาจมีการเข้ารหัสที่แตกต่างกัน... GB2312, Big5, UTF8, ISO-8859-1 ฯลฯ .. แม้ว่าจะได้รับข้อมูลการเข้ารหัสก่อน จากนั้นจึงอ่านเนื้อหาเฉพาะ อย่างไรก็ตาม เมื่อการเข้ารหัส Stream Reader ที่ใช้ตอนต้นไม่ถูกต้อง เนื้อหาที่อ่านจะไม่สามารถกู้คืนได้...คำจะหายไป...
ฉัน ไม่สามารถรับได้อีก ... หลังจากเข้ารหัสข้อมูลแล้ว ให้สร้าง Stream Reader ใหม่ขึ้นมาใหม่ เนื่องจากเนื้อหาเฉพาะถูกบัฟเฟอร์โดย Stream Reader ดั้งเดิม...
วิธีแก้ไขเดียวคือการใช้ Stream Reader ที่สามารถเปลี่ยน แอตทริบิวต์ CurrentEncoding...
เขียนทั้งหมดตั้งแต่เริ่มต้น ในตอนแรกฉันได้รับซอร์สโค้ดของ mono และทำการแก้ไขจากโค้ดการใช้งาน Stream Reader ของ mono
จริงๆ แล้ว Stream Reader นั้นง่ายมาก มันมีบัฟเฟอร์สองตัวอยู่ข้างใน อันหนึ่งคือบัฟเฟอร์อินพุตและอีกอันคือบัฟเฟอร์ที่ถอดรหัสแล้ว อันแรกใช้เพื่อแคชข้อมูลต้นฉบับที่อ่านจากสตรีมพื้นฐาน และอันหลังใช้เพื่อแคชสิ่งต่าง ๆ ถอดรหัสตามข้อมูลต้นฉบับ ... ... ตราบใดที่คุณเข้าใจวิธี ReadBuffer ในการใช้งานโมโนการแก้ไข CurrentEncoding แบบไดนามิกก็ไม่ยากเกินไป ...
โปรโตคอลเครือข่ายที่ฉันต้องจัดการคือโปรโตคอลแบบบรรทัด ...ฉันเรียกเฉพาะวิธี Readline ของ StreamReader ในโปรแกรมเท่านั้น แต่ไม่ได้ใช้ Readline ทั้งสองวิธีเลย ซึ่งทำให้ฉันแก้ไขการเข้ารหัสแบบไดนามิกได้ง่ายขึ้นมาก...
สิ่งที่ฉันทำคือทุกครั้งที่ฉัน เรียก Readline ฉันไม่เพียงแต่ย้ายเคอร์เซอร์ (pos) ของบัฟเฟอร์ที่ถอดรหัสแล้ว แต่ยังย้ายเคอร์เซอร์ใหม่ในบัฟเฟอร์อินพุตด้วย (pos_input) วิธีการนั้นง่ายมาก วิธี Readline จำเป็นต้องเรียก FindNextEOL เพื่อย้ายเคอร์เซอร์ไป ค้นหาสัญลักษณ์ขึ้นบรรทัดใหม่... ฉันเพิ่มอีกหนึ่งบรรทัดในวิธี FindNextEOL:
int ค้นหา NextEOL()
-
ค้นหาถัดไปอินพุตEOL();
....
ฟังก์ชันใหม่ FindNextInputEOL เป็นแบบจำลองที่สมบูรณ์ของ FindNextEOL ยกเว้นว่าฟังก์ชันแรกจะประมวลผลบัฟเฟอร์อินพุต ในขณะที่ฟังก์ชันหลังจะประมวลผลบัฟเฟอร์ที่ถอดรหัส...
ด้วยวิธีนี้ ฉันสามารถรู้ได้ว่าหลังจากแต่ละ Readline บัฟเฟอร์อินพุตจะมี ไม่ได้ถูกถอดรหัสโดยชั้นบน ข้อมูลต้นฉบับที่อ่านคืออะไร...
จากนั้น เพิ่มเมธอด Set ให้กับแอตทริบิวต์ CurrentEncoding:
ชุด
-
การเข้ารหัส=ค่า;
ตัวถอดรหัส = การเข้ารหัส GetDecoder ();
decoded_count = pos + ตัวถอดรหัส GetChars (input_buffer, pos_input, cbEncoded, pos_input, decoded_buffer, pos);
}
เมื่อตั้งค่าการเข้ารหัสใหม่ โปรแกรมจะถอดรหัสข้อมูลต้นฉบับที่ยังไม่ได้อ่านอีกครั้งตามเคอร์เซอร์ของบัฟเฟอร์อินพุต (pos_input) และแทนที่เนื้อหาในบัฟเฟอร์ที่ถอดรหัส
จากนั้น สิ่งนั้นก็เสร็จสิ้น... คุณไม่จำเป็นต้องแก้ไขเมธอด Readline ใดๆ ด้วยซ้ำ... ยกเว้นการวางตัวแปร cbEncoded ลงในโกลบอล...
อย่างไรก็ตาม การปรับเปลี่ยนนี้ทำให้ Read ทั้งสองวิธีใช้งานไม่ได้โดยสิ้นเชิง ... เมื่อเรียกแล้ว... จะทำให้เคอร์เซอร์สองตัวในบัฟเฟอร์อินพุตและบัฟเฟอร์ที่ถอดรหัสไม่ซิงค์กัน... รหัสทั้งหมดแนบมาด้านล่าง ฉันหวังว่าจะมีคนช่วยฉันหาวิธีการอ่านทั้งสองวิธีได้ ...ขอบคุณล่วงหน้าครับ…
-
// System.IO.StreamReader.cs
-
// ผู้เขียน:
// ดีทมาร์ เมาเรอร์ ( [email protected] )
// มิเกล เด อิกาซา ( [email protected] )
-
// (C) Ximian, Inc. http://www.ximian.com
// ลิขสิทธิ์ (C) 2004 โนเวลล์ ( http://www.novell.com )
//
//
// ลิขสิทธิ์ (C) 2004 Novell, Inc ( http://www.novell.com )
-
// อนุญาตให้บุคคลใด ๆ ที่ได้รับอนุญาตโดยไม่คิดค่าใช้จ่าย
// สำเนาของซอฟต์แวร์นี้และไฟล์เอกสารที่เกี่ยวข้อง (ไฟล์
// "ซอฟต์แวร์") เพื่อจัดการกับซอฟต์แวร์โดยไม่มีข้อจำกัด รวมถึง
// โดยไม่จำกัดสิทธิ์ในการใช้ คัดลอก แก้ไข ผสาน เผยแพร่
// แจกจ่าย ให้อนุญาตช่วง และ/หรือขายสำเนาของซอฟต์แวร์ และให้กับ
// อนุญาตให้บุคคลที่ได้รับซอฟต์แวร์ดังกล่าวสามารถดำเนินการดังกล่าวได้ ทั้งนี้ขึ้นอยู่กับ
// เงื่อนไขดังต่อไปนี้:
-
// ประกาศลิขสิทธิ์ข้างต้นและประกาศอนุญาตนี้จะต้องเป็น
// รวมอยู่ในสำเนาทั้งหมดหรือส่วนสำคัญของซอฟต์แวร์
-
// ซอฟต์แวร์นี้มีให้ "ตามที่เป็น" โดยไม่มีการรับประกันใดๆ
// โดยชัดแจ้งหรือโดยนัย รวมถึงแต่ไม่จำกัดเพียงการรับประกันของ
// ความสามารถในการค้าขาย ความเหมาะสมสำหรับวัตถุประสงค์เฉพาะ และ
// การไม่ละเมิด ไม่ว่าในกรณีใด ๆ ผู้เขียนหรือผู้ถือลิขสิทธิ์จะไม่
// ต้องรับผิดต่อการเรียกร้องค่าเสียหายหรือความรับผิดอื่น ๆ ไม่ว่าในการดำเนินการใด ๆ
// สัญญา การละเมิด หรืออย่างอื่น ที่เกิดขึ้นจาก ออกจาก หรือเกี่ยวข้องกัน
// กับซอฟต์แวร์หรือการใช้งานหรือข้อตกลงอื่น ๆ ในซอฟต์แวร์
//
ใช้ระบบ;
ใช้ System.Text;
ใช้ System.Runtime.InteropServices
System.IO
-
[สามารถซีเรียลไลซ์ได้]
DynamicStreamReader คลาสสาธารณะ: TextReader
{
const int DefaultBufferSize = 1,024;
const int DefaultFileBufferSize = 4096;
const int MinimumBufferSize = 128;
//
//บัฟเฟอร์อินพุต
-
ไบต์ [] input_buffer;
//
// บัฟเฟอร์ที่ถอดรหัสจากบัฟเฟอร์อินพุตด้านบน
-
ถ่าน [] decoded_buffer;
//
// ถอดรหัสไบต์ใน decoded_buffer
-
int decoded_count;
//
// ตำแหน่งปัจจุบันใน decoded_buffer
-
ตำแหน่งภายใน
//
// ตำแหน่งปัจจุบันใน input_buffer
-
int pos_input;
//
// ขนาดบัฟเฟอร์ที่เราใช้
-
int buffer_size;
int do_checks;
การเข้ารหัสการเข้ารหัส;
ตัวถอดรหัส
สตรีม base_stream;
บูลเมย์บล็อค;
StringBuilder line_builder;
คลาสส่วนตัว NullStreamReader : DynamicStreamReader
-
การแทนที่สาธารณะ int Peek ()
-
กลับ -1;
}
แทนที่สาธารณะ int อ่าน ()
-
กลับ -1;
}
แทนที่สาธารณะ int อ่าน (บัฟเฟอร์ [In, Out] ถ่าน [], ดัชนี int, จำนวน int)
-
กลับ 0;
}
สตริงการแทนที่สาธารณะ ReadLine ()
-
กลับเป็นโมฆะ;
}
สตริงการแทนที่สาธารณะ ReadToEnd ()
-
กลับ String.Empty;
}
แทนที่สาธารณะ Stream BaseStream
-
รับ { กลับ Stream.Null;
}
การเข้ารหัสแทนที่สาธารณะ CurrentEncoding
-
รับ { ส่งคืนการเข้ารหัส Unicode;
-
}
สาธารณะ ใหม่ DynamicStreamReader แบบอ่านอย่างเดียวแบบคงที่ Null = (DynamicStreamReader)(ใหม่ NullStreamReader());
DynamicStreamReader ภายใน () {}
DynamicStreamReader สาธารณะ (สตรีมสตรีม)
: นี่ (สตรีม, Encoding.UTF8, จริง, DefaultBufferSize) { }
สาธารณะ DynamicStreamReader (สตรีมสตรีม, bool detector_encoding_from_bytemarks)
: นี้ (สตรีม, Encoding.UTF8, detector_encoding_from_bytemarks, DefaultBufferSize) { }
DynamicStreamReader สาธารณะ (สตรีมสตรีม, การเข้ารหัสการเข้ารหัส)
: นี้ (สตรีม, การเข้ารหัส, จริง, DefaultBufferSize) { }
DynamicStreamReader สาธารณะ (สตรีมสตรีม, การเข้ารหัสการเข้ารหัส, bool detector_encoding_from_bytemarks)
: นี่ (สตรีม, การเข้ารหัส, detector_encoding_from_bytemarks, DefaultBufferSize) { }
DynamicStreamReader สาธารณะ (สตรีมสตรีม, การเข้ารหัสการเข้ารหัส, bool detector_encoding_from_bytemarks, int buffer_size)
-
เริ่มต้น (สตรีม, การเข้ารหัส, detector_encoding_from_bytemarks, buffer_size);
}
DynamicStreamReader สาธารณะ (เส้นทางสตริง)
: นี่ (เส้นทาง, Encoding.UTF8, จริง, DefaultFileBufferSize) { }
สาธารณะ DynamicStreamReader(เส้นทางสตริง, bool detector_encoding_from_bytemarks)
: นี้ (เส้นทาง, Encoding.UTF8, ตรวจพบ_encoding_from_bytemarks, DefaultFileBufferSize) { }
สาธารณะ DynamicStreamReader (เส้นทางสตริง, การเข้ารหัสการเข้ารหัส)
: นี่ (เส้นทาง, การเข้ารหัส, จริง, DefaultFileBufferSize) { }
สาธารณะ DynamicStreamReader (เส้นทางสตริง, การเข้ารหัสการเข้ารหัส, bool detector_encoding_from_bytemarks)
: นี่ (พาธ, การเข้ารหัส, detector_encoding_from_bytemarks, DefaultFileBufferSize) { }
DynamicStreamReader สาธารณะ (เส้นทางสตริง, การเข้ารหัสการเข้ารหัส, บูล detector_encoding_from_bytemarks, int buffer_size)
-
ถ้า (null == เส้นทาง)
โยน ArgumentNullException ใหม่ ("path");
ถ้า (String.Empty == path)
โยน ArgumentException ใหม่ ("ไม่อนุญาตให้ใช้เส้นทางว่าง");
ถ้า (path.IndexOfAny (Path.InvalidPathChars) != -1)
โยน ArgumentException ใหม่ ("เส้นทางมีอักขระที่ไม่ถูกต้อง");
ถ้า (null == การเข้ารหัส)
โยน ArgumentNullException ใหม่ ("การเข้ารหัส");
ถ้า (buffer_size <= 0)
โยน ArgumentOutOfRangeException ใหม่ ("buffer_size", "ขนาดต่ำสุดของบัฟเฟอร์ต้องเป็นค่าบวก");
string DirName = Path.GetDirectoryName(path);
ถ้า (DirName != String.Empty && !Directory.Exists(DirName))
โยน DirectoryNotFoundException ใหม่ ("ไดเรกทอรี '" + DirName + "' ไม่พบ");
ถ้า (!File.Exists(path))
โยน FileNotFoundException ใหม่ ("ไม่พบไฟล์", path);
สตรีมสตรีม = (สตรีม) File.OpenRead (เส้นทาง);
เริ่มต้น (สตรีม, การเข้ารหัส, detector_encoding_from_bytemarks, buffer_size);
}
โมฆะภายในเริ่มต้น (กระแสสตรีม, การเข้ารหัสการเข้ารหัส, บูล detector_encoding_from_bytemarks, int buffer_size)
-
ถ้า (null == สตรีม)
โยน ArgumentNullException ใหม่ ("สตรีม");
ถ้า (null == การเข้ารหัส)
โยน ArgumentNullException ใหม่ ("การเข้ารหัส");
ถ้า (!stream.CanRead)
โยน ArgumentException ใหม่ ("ไม่สามารถอ่านสตรีม");
ถ้า (buffer_size <= 0)
โยน ArgumentOutOfRangeException ใหม่ ("buffer_size", "ขนาดต่ำสุดของบัฟเฟอร์ต้องเป็นค่าบวก");
ถ้า (buffer_size < MinimumBufferSize)
buffer_size = MinimumBufferSize;
base_stream = สตรีม;
input_buffer = ไบต์ใหม่ [buffer_size];
this.buffer_size = ขนาดบัฟเฟอร์;
this.encoding = การเข้ารหัส;
decoder = encoding.GetDecoder ();
ไบต์ [] คำนำ = encoding.GetPreamble ();
do_checks = detector_encoding_from_bytemarks ? 1 : 0;
do_checks += (คำนำความยาว == 0) ? 0 : 2;
decoded_buffer = ถ่านใหม่ [encoding.GetMaxCharCount (buffer_size)];
ถอดรหัส_นับ = 0;
ตำแหน่ง = 0;
pos_input =0;
}
BaseStream สตรีมเสมือนสาธารณะ
-
รับ
-
กลับ base_stream;
-
}
การเข้ารหัสเสมือนสาธารณะ CurrentEncoding
-
รับ
-
ถ้า (การเข้ารหัส == null)
โยนข้อยกเว้นใหม่ ();
การเข้ารหัสกลับ;
-
ชุด
-
การเข้ารหัส=ค่า;
ตัวถอดรหัส = การเข้ารหัส GetDecoder ();
decoded_count = pos + ตัวถอดรหัส GetChars (input_buffer, pos_input, cbEncoded - pos_input, decoded_buffer, pos);
//DiscardBufferedData();
-
}
สาธารณะแทนที่เป็นโมฆะปิด ()
-
กำจัด(จริง);
}
การป้องกันแทนที่ void Dispose (การกำจัดบูล)
-
ถ้า (ทิ้ง && base_stream != null)
base_stream.ปิด ();
input_buffer = โมฆะ;
decoded_buffer = โมฆะ;
การเข้ารหัส = null;
ตัวถอดรหัส = โมฆะ;
base_stream = โมฆะ;
ฐานทิ้ง (ทิ้ง);
}
//
// ให้การตรวจจับการเข้ารหัสอัตโนมัติรวมถึงการข้ามไป
// เครื่องหมายไบต์ที่จุดเริ่มต้นของสตรีม
-
int DoChecks (จำนวน int)
-
ถ้า ((do_checks & 2) == 2)
-
ไบต์ [] คำนำ = การเข้ารหัส GetPreamble ();
int c = คำนำความยาว;
ถ้า (นับ >= c)
-
ฉัน;
สำหรับ (i = 0; i <c; i++)
ถ้า (input_buffer [i] != คำนำ [i])
พัง;
ถ้า (i == c)
ส่งคืนฉัน;
-
}
ถ้า ((do_checks & 1) == 1)
-
ถ้า (นับ < 2)
กลับ 0;
ถ้า (input_buffer [0] == 0xfe && input_buffer [1] == 0xff)
-
this.encoding = การเข้ารหัส BigEndianUnicode;
กลับ 2;
}
ถ้า (input_buffer [0] == 0xff && input_buffer [1] == 0xfe)
-
this.encoding = การเข้ารหัส Unicode;
กลับ 2;
}
ถ้า (นับ < 3)
กลับ 0;
ถ้า (input_buffer [0] == 0xef && input_buffer [1] == 0xbb && input_buffer [2] == 0xbf)
-
this.encoding = การเข้ารหัส UTF8;
กลับ 3;
-
}
กลับ 0;
}
โมฆะสาธารณะ DiscardBufferedData ()
-
POS = ถอดรหัส_นับ = 0;
mayBlock = เท็จ;
// ละทิ้งสถานะภายในของตัวถอดรหัสด้วย
ตัวถอดรหัส = การเข้ารหัส GetDecoder ();
-
int cbเข้ารหัส;
int parse_start;
//บัฟเฟอร์ว่างเปล่า ให้เติมใหม่อีกครั้ง
int ReadBuffer ส่วนตัว ()
-
ตำแหน่ง = 0;
pos_input = 0;
cbEncoded = 0;
// วนซ้ำต่อไปจนกว่าตัวถอดรหัสจะให้ตัวอักษรแก่เรา
ถอดรหัส_นับ = 0;
parse_start = 0;
ทำ
-
cbEncoded = base_stream.Read (input_buffer, 0, buffer_size);
ถ้า (cbEncoded == 0)
กลับ 0;
mayBlock = (cbEncoded < buffer_size);
ถ้า (do_checks > 0)
-
การเข้ารหัสแบบเก่า = การเข้ารหัส;
parse_start = DoChecks (cbเข้ารหัส);
ถ้า (เก่า != การเข้ารหัส)
-
ตัวถอดรหัส = การเข้ารหัส GetDecoder ();
-
do_check = 0;
cbEncoded -= parse_start;
-
decoded_count += decoder.GetChars (input_buffer, parse_start, cbEncoded, decoded_buffer, 0);
parse_start = 0;
} ในขณะที่ (decoded_count == 0)
;
}
แทนที่สาธารณะ int Peek ()
-
ถ้า (base_stream == null)
โยน ObjectDisposedException ใหม่ ("StreamReader", "ไม่สามารถอ่านจาก StreamReader ที่ปิดได้");
ถ้า (pos >= decoded_count && (mayBlock || ReadBuffer () == 0))
กลับ -1;
กลับ decoded_buffer [pos];
}
แทนที่สาธารณะ int อ่าน ()
-
โยนข้อยกเว้นใหม่ ("Dynamic Reader ไม่สามารถอ่านได้!");
}
แทนที่สาธารณะ int อ่าน ([เข้า, ออก] ถ่าน [] dest_buffer, ดัชนี int, นับ int)
-
โยนข้อยกเว้นใหม่ ("Dynamic Reader ไม่สามารถอ่านได้!");
}
บูลพบCR_input;
int ค้นหา NextInputEOL()
-
ถ่าน c = '