عنوان URL الأصلي: http://www.blogwind.com/Wuvist/42999.shtml
في إطار عمل .Net، يجب تحديد الترميز الذي يستخدمه StreamReader في المُنشئ ولا يمكن تغييره على الإطلاق في منتصف الطريق.
في الظروف العادية، لن يسبب هذا أي مشاكل. بشكل عام، إذا تمت قراءة ملف من قرص ثابت، فإن التشفير داخل ملف واحد يكون موحدًا بشكل عام. حتى إذا وجدت خطأ في القراءة، يمكنك إغلاق StreamReader وإعادة القراءة باستخدام الترميز الجديد.
لقد واجهت مؤخرًا الحاجة إلى تعديل الترميز، ولم يقم برنامجي بإيقاف إمكانية إعادة القراءة. نظرًا لأن BaseStream الخاص بـ StreamReader الذي أستخدمه هو تدفق شبكي، فلا يمكنني إغلاقه... لكن الأشياء التي تم تمريرها بواسطة تدفق الشبكة قد تحتوي على ترميزات مختلفة... GB2312، وBig5، وUTF8، وISO-8859-1، وما إلى ذلك. .. على الرغم من أنه يتم الحصول على معلومات التشفير أولاً، ثم تتم قراءة المحتوى المحدد، ومع ذلك، بمجرد أن يكون تشفير Stream Reader المستخدم في البداية خاطئًا، لا يمكن استرداد المحتوى المقروء أبدًا... سيتم فقدان الكلمات...
أنا
.لا يمكن الحصول عليها مرة أخرى... بعد تشفير المعلومات، أعد إنشاء قارئ دفق جديد، لأنه تم تخزين المحتوى المحدد مؤقتًا بواسطة قارئ الدفق الأصلي...
الحل الوحيد هو تنفيذ قارئ دفق يمكنه تغيير سمة التشفير الحالي...
اكتب كل شيء من البداية كان الأمر غير عملي للغاية في البداية، لقد حصلت أولاً على الكود المصدري لـ mono وأجريت تعديلات من كود تنفيذ Stream Reader الخاص بـ mono.
يعد Stream Reader في الواقع بسيطًا جدًا. فهو يحتوي على مخزنين مؤقتين بالداخل، أحدهما هو المخزن المؤقت للإدخال والآخر هو المخزن المؤقت الذي تم فك تشفيره. يُستخدم الأول لتخزين البيانات الأصلية المقروءة من الدفق الأساسي مؤقتًا، ويستخدم الأخير لتخزين الأشياء مؤقتًا تم فك ترميزه وفقًا للبيانات الأصلية... ...طالما أنك تفهم طريقة ReadBuffer في تنفيذ mono، فليس من الصعب جدًا تعديل CurrentEncoding ديناميكيًا...
بروتوكول الشبكة الذي أحتاج إلى التعامل معه هو بروتوكول خطي ...لقد قمت فقط باستدعاء طريقة Readline الخاصة بـ StreamReader في البرنامج، ولكن لم يتم استخدام طريقتي القراءة بالكامل، مما يجعل من الأسهل بالنسبة لي أيضًا تعديل الترميز ديناميكيًا...
ما أفعله هو أنه في كل مرة أقوم فيها استدعاء Readline، لا أقوم فقط بتحريك المؤشر (pos) للمخزن المؤقت الذي تم فك تشفيره، ولكن أيضًا تحريك مؤشر جديد في المخزن المؤقت للإدخال (pos_input)، وتحتاج طريقة Readline إلى استدعاء FindNextEOL لتحريك المؤشر إلى ابحث عن رمز السطر الجديد... أقوم بإضافة سطر آخر إلى طريقة FindNextEOL:
كثافة العمليات FindNextEOL()
{
FindNextInputEOL();
....
الوظيفة الجديدة FindNextInputEOL هي نسخة طبق الأصل كاملة من FindNextEOL، باستثناء أن الأول يعالج المخزن المؤقت للإدخال، بينما يعالج الأخير المخزن المؤقت الذي تم فك تشفيره...
وبهذه الطريقة، يمكنني معرفة أنه بعد كل قراءة، يحتوي المخزن المؤقت للإدخال على لم يتم فك تشفيرها بواسطة الطبقة العليا. ما هي البيانات الأصلية المقروءة...
ثم أضف طريقة Set إلى سمة CurrentEncoding:
تعيين
{
الترميز = القيمة؛
وحدة فك التشفير = encoding.GetDecoder();
decoded_count = pos + decoder.GetChars (input_buffer, pos_input, cbEncoded, pos_input, decoded_buffer, pos);
}
عند تعيين ترميز جديد، يقوم البرنامج بإعادة فك تشفير البيانات الأصلية التي لم تتم قراءتها وفقًا لمؤشر المخزن المؤقت للإدخال (pos_input)، ويستبدل المحتوى الموجود في المخزن المؤقت الذي تم فك تشفيره.
وبعد ذلك يتم الأمر... لا تحتاج حتى إلى إجراء أي تعديلات على طريقة Readline... باستثناء وضع المتغير cbEncoded في العمومي...
إلا أن هذا التعديل يجعل طريقتي القراءة غير صالحتين للاستخدام تمامًا ... بمجرد الاتصال ... سيؤدي ذلك إلى عدم مزامنة المؤشرين الموجودين في المخزن المؤقت للإدخال والمخزن المؤقت الذي تم فك تشفيره ... الكود الكامل مرفق أدناه. آمل أن يساعدني شخص ما في معرفة طريقتي القراءة ... شكرا مقدما ...
/
// System.IO.StreamReader.cs
//
// مؤلف:
// ديتمار ماورير ( [email protected] )
// ميغيل دي إيكازا ( [email protected] )
//
// (ج) شركة 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 MinimBufferSize = 128
؛
// المخزن المؤقت للإدخال
//
بايت [] input_buffer
؛
// المخزن المؤقت الذي تم فك تشفيره من المخزن المؤقت للإدخال أعلاه
//
شار [] decoded_buffer
؛
// البايتات التي تم فك تشفيرها في decoded_buffer.
//
int decoded_count
؛
// الموضع الحالي في decoded_buffer
//
نقطة البيع
//
// الموضع الحالي في input_buffer
//
int pos_input
;
// حجم المخزن المؤقت الذي نستخدمه
//
int buffer_size;
int do_checks;
ترميز الترميز؛
وحدة فك ترميز
الدفق؛
bool mayBlock;
StringBuilder line_builder؛
فئة خاصة NullStreamReader: DynamicStreamReader
{
التجاوز العام int Peek ()
{
العودة -1؛
}
التجاوز العام قراءة int ()
{
العودة -1؛
}
التجاوز العام int Read ([In، Out] char[] buffer، int Index، int count)
{
العودة 0؛
}
سلسلة التجاوز العامة ReadLine ()
{
عودة فارغة؛
}
سلسلة التجاوز العامة ReadToEnd ()
{
إرجاع String.Empty;
}
تجاوز عام للدفق BaseStream
{
الحصول على { عودة Stream.Null }
}
تجاوز عام ترميز CurrentEncoding
{
الحصول على {إرجاع Encoding.Unicode}؛
}
}
عام جديد ثابت للقراءة فقط DynamicStreamReader Null = (DynamicStreamReader)(new NullStreamReader());
DynamicStreamReader () الداخلي {}
DynamicStreamReader العام (دفق الدفق)
: هذا (الدفق، Encoding.UTF8، صحيح، DefaultBufferSize) { }
public DynamicStreamReader(Streamstream, bool Detect_encoding_from_bytemarks)
: هذا (الدفق، Encoding.UTF8، Detect_encoding_from_bytemarks، DefaultBufferSize) { }
public DynamicStreamReader (دفق الدفق، ترميز التشفير)
: هذا (دفق، ترميز، صحيح، DefaultBufferSize) { }
public DynamicStreamReader(دفق الدفق، ترميز التشفير، كشف منطقي_encoding_from_bytemarks)
: هذا (الدفق، التشفير، Detect_encoding_from_bytemarks، DefaultBufferSize) { }
DynamicStreamReader العام (دفق الدفق، ترميز التشفير، كشف منطقي_encoding_from_bytemarks، int buffer_size)
{
التهيئة (الدفق، الترميز، Detect_encoding_from_bytemarks، buffer_size)؛
}
DynamicStreamReader العام (مسار السلسلة)
: هذا (المسار، Encoding.UTF8، صحيح، DefaultFileBufferSize) { }
public DynamicStreamReader(string path, bool Detect_encoding_from_bytemarks)
: هذا (المسار، Encoding.UTF8، Detect_encoding_from_bytemarks، DefaultFileBufferSize) { }
public DynamicStreamReader (مسار السلسلة، ترميز التشفير)
: هذا (المسار، الترميز، صحيح، DefaultFileBufferSize) { }
public DynamicStreamReader(string path, Encoding encoding, bool Detect_encoding_from_bytemarks)
: هذا (المسار، الترميز، Detect_encoding_from_bytemarks، DefaultFileBufferSize) { }
DynamicStreamReader العام (مسار السلسلة، ترميز التشفير، كشف منطقي عن التشفير_من_bytemarks، حجم المخزن المؤقت int)
{
إذا (فارغة == المسار)
رمي ArgumentNullException("path");
إذا (String.Empty == المسار)
throw new ArgumentException("المسار الفارغ غير مسموح به");
إذا (path.IndexOfAny (Path.InvalidPathChars) != -1)
طرح ArgumentException الجديد ("يحتوي المسار على أحرف غير صالحة")؛
إذا (خالية == ترميز)
طرح ArgumentNullException الجديد ("التشفير")؛
إذا (حجم المخزن المؤقت <= 0)
طرح ArgumentOutOfRangeException الجديد ("buffer_size"، "يجب أن يكون الحد الأدنى لحجم المخزن المؤقت موجبًا")
؛
إذا (DirName != String.Empty && !Directory.Exists(DirName))
طرح DirectoryNotFoundException جديد ("الدليل '" + DirName + "' غير موجود.");
إذا (!File.Exists(path))
throw new FileNotFoundException("لم يتم العثور على الملف.", المسار
= (Stream) File.OpenRead (path);
التهيئة (الدفق، الترميز، Detect_encoding_from_bytemarks، buffer_size)؛
}
تهيئة الفراغ الداخلي (دفق الدفق، ترميز التشفير، كشف منطقي عن التشفير_من_bytemarks، حجم المخزن المؤقت int)
{
إذا (خالية == تيار)
طرح ArgumentNullException الجديد ("الدفق")؛
إذا (خالية == ترميز)
طرح ArgumentNullException الجديد ("التشفير")؛
إذا (!stream.CanRead)
طرح ArgumentException جديد ("لا يمكن قراءة الدفق")؛
إذا (حجم المخزن المؤقت <= 0)
طرح ArgumentOutOfRangeException الجديد ("buffer_size"، "يجب أن يكون الحد الأدنى لحجم المخزن المؤقت موجبًا")
؛
buffer_size = minorBufferSize
base_stream =stream;
input_buffer = بايت جديد [buffer_size]؛
this.buffer_size = buffer_size;
this.encoding = encoding;
وحدة فك التشفير = encoding.GetDecoder ()؛
بايت [] ديباجة = encoding.GetPreamble ()؛
do_checks = Detect_encoding_from_bytemarks ?
do_checks += (الديباجة. الطول == 0) ?
decoded_buffer = حرف جديد [encoding.GetMaxCharCount (buffer_size)]؛
decoded_count = 0;
نقاط البيع = 0؛
pos_input =0;
}
البث الافتراضي العام BaseStream
{
يحصل
{
إرجاع Base_stream؛
}
}
التشفير الظاهري العام CurrentEncoding
{
يحصل
{
إذا (الترميز == فارغ)
طرح استثناء جديد ()؛
ترميز الإرجاع؛
}
تعيين
{
الترميز = القيمة؛
وحدة فك التشفير = encoding.GetDecoder();
decoded_count = pos + decoder.GetChars (input_buffer، pos_input، cbEncoded - pos_input، decoded_buffer، pos)؛
//DiscardBufferedData();
}
}
تجاوز عام باطل إغلاق ()
{
التخلص(صحيح);
}
التخلص من الفراغ المحمي (التخلص المنطقي)
{
إذا (التخلص من && base_stream != null)
base_stream.Close ()؛
input_buffer = null;
decoded_buffer = null;
الترميز = فارغ؛
وحدة فك الترميز = فارغة؛
base_stream = null;
قاعدة.التخلص (التخلص)؛
}
//
// يوفر الكشف التلقائي عن الترميز، بالإضافة إلى تخطيه
// علامات البايت في بداية الدفق.
//
كثافة العمليات DoChecks (عدد العمليات)
{
إذا ((do_checks & 2) == 2)
{
البايت [] الديباجة = encoding.GetPreamble ()؛
int c = preable.Length;
إذا (العد >= ج)
{
كثافة العمليات أنا؛
لـ (i = 0; i < c; i++)
إذا (input_buffer [i] != الديباجة [i])
استراحة؛
إذا (ط == ج)
العودة أنا؛
}
}
إذا ((do_checks & 1) == 1)
{
إذا (العد <2)
إرجاع 0
إذا (input_buffer [0] == 0xfe && input_buffer [1] == 0xff)
{
this.encoding = Encoding.BigEndianUnicode;
العودة 2؛
}
إذا (input_buffer [0] == 0xff && input_buffer [1] == 0xfe)
{
this.encoding = Encoding.Unicode;
العودة 2؛
}
إذا (العد <3)
إرجاع 0
إذا (input_buffer [0] == 0xef && input_buffer [1] == 0xbb && input_buffer [2] == 0xbf)
{
this.encoding = Encoding.UTF8;
العودة 3؛
}
}
العودة 0؛
}
الفراغ العام DiscardBufferedData ()
{
نقاط البيع = decoded_count = 0؛
mayBlock = false;
// تجاهل الحالة الداخلية لجهاز فك التشفير أيضًا.
وحدة فك التشفير = encoding.GetDecoder ()؛
}
int cbEncoded;
int parse_start;
// المخزن المؤقت فارغ، املأه مرة أخرى
الباحث الخاص ReadBuffer ()
{
نقاط البيع = 0؛
pos_input = 0;
cbEncoded = 0;
// استمر في التكرار حتى تعطينا وحدة فك التشفير بعض الأحرف
decoded_count = 0;
parse_start = 0;
يفعل
{
cbEncoded = base_stream.Read (input_buffer, 0, buffer_size);
إذا (cbEncoded == 0)
return 0
mayBlock = (cbEncoded < buffer_size);
إذا (do_checks > 0)
{
الترميز القديم = الترميز؛
parse_start = DoChecks (cbEncoded);
إذا (قديم != ترميز)
{
وحدة فك التشفير = 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)
;
}
التجاوز العام int Peek ()
{
إذا (base_stream == فارغ)
طرح ObjectDisposeException الجديد ("StreamReader"، "لا يمكن القراءة من StreamReader مغلق")؛
إذا (pos >= decoded_count && (mayBlock || ReadBuffer () == 0))
إرجاع -1؛
إرجاع decoded_buffer [pos]؛
}
التجاوز العام قراءة int ()
{
رمي استثناء جديد("القارئ الديناميكي لا يستطيع القراءة!");
}
التجاوز العام int Read ([In، Out] char[] dest_buffer، int Index، int count)
{
رمي استثناء جديد("القارئ الديناميكي لا يستطيع القراءة!");
}
bool FoundCR_input;
كثافة العمليات FindNextInputEOL()
{
شار ج = '