Исходный URL: http://www.blogwind.com/Wuvist/42999.shtml .
В среде .Net кодировка, используемая StreamReader, должна быть указана в конструкторе и не может быть изменена в процессе работы.
В обычных условиях это не вызовет никаких проблем. Обычно, если файл читается с жесткого диска, кодировка внутри одного файла обычно одинакова. Даже если вы обнаружите ошибку чтения, вы можете закрыть StreamReader и возобновить чтение, используя новую кодировку.
Недавно я столкнулся с необходимостью изменить кодировку, а у меня в программе не отключили возможность перечитывания. Поскольку BaseStream StreamReader, который я использую, является сетевым потоком, я не могу его закрыть... но данные, передаваемые сетевым потоком, могут содержать разные кодировки... GB2312, Big5, UTF8, ISO-8859-1 и т. д. .. Хотя сначала получается информация о кодировке, а затем считывается конкретный контент. Однако, если кодировка Stream Reader, использованная вначале, неверна, прочитанный контент никогда не сможет быть восстановлен... слова будут потеряны...
Я. не могу получить его снова... После кодирования информации заново создайте новый Stream Reader, поскольку определенный контент был буферизован исходным Stream Reader...
Единственное решение - реализовать Stream Reader, который может изменить Атрибут CurrentEncoding...
Написать все с нуля. Поначалу это было очень непрактично. Сначала я получил исходный код mono и внес изменения в код реализации Mono Stream Reader.
Stream Reader на самом деле очень прост. Он имеет внутри два буфера: один — входной, а другой — декодированный. Первый используется для кэширования исходных данных, считанных из базового потока, а второй — для кэширования данных. декодируется в соответствии с исходными данными... ...Пока вы понимаете метод ReadBuffer в реализации mono, не так уж сложно динамически изменять CurrentEncoding...
Сетевой протокол, с которым мне нужно иметь дело, является линейным протоколом. ...Я вызывал в программе только метод Readline StreamReader, но полностью. Два метода Read не используются, что также значительно облегчает мне динамическое изменение кодировки...
Что я делаю, так это то, что каждый раз, когда я вызывая Readline, я не только перемещаю курсор (pos) декодированного буфера, но и перемещаю новый курсор во входном буфере (pos_input), метод очень простой. Методу Readline необходимо вызвать FindNextEOL, чтобы переместить курсор. найдите символ новой строки... Я добавляю еще одну строку в метод FindNextEOL:
int FindNextEOL()
{
НайтиСледующийВводEOL();
....
Новая функция FindNextInputEOL является полной копией FindNextEOL, за исключением того, что первая обрабатывает входной буфер, а вторая обрабатывает декодированный буфер...
Таким образом, я могу знать, что после каждой строки чтения входной буфер имеет не были декодированы верхним слоем. Какие исходные данные читаются...
Затем добавьте метод Set в атрибут CurrentEncoding:
набор
{
кодировка = значение;
декодер = кодирование.GetDecoder();
decoded_count = pos + decoder.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) Novell, 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 = 1024;
const int DefaultFileBufferSize = 4096;
const int МинимальныйBufferSize = 128
//
//Входной буфер
//
байт [] input_buffer;
//
// Декодированный буфер из вышеуказанного входного буфера
//
символ [] decoded_buffer;
//
// Декодированные байты в decoded_buffer.
//
int decoded_count;
//
// Текущая позиция в decoded_buffer
//
интервал позиции
//
// Текущая позиция во входном_буфере
//
интервал pos_input
;
// Размер буфера, который мы используем
//
интервал буфера_размер;
интервал do_checks;
Кодирование кодирование;
Декодер декодера
потока base_stream;
bool mayBlock;
StringBuilder line_builder;
частный класс NullStreamReader: DynamicStreamReader;
{
публичное переопределение int Peek()
{
вернуть -1;
}
публичное переопределение int Read ()
{
вернуть -1;
}
public override int Read (буфер [In, Out] char[], int index, int count)
{
вернуть 0;
}
публичная строка переопределения ReadLine ()
{
вернуть ноль;
}
публичная строка переопределения ReadToEnd ()
{
вернуть String.Empty;
}
публичное переопределение Stream BaseStream
{
получить {вернуть Stream.Null};
}
публичное переопределение кодировки CurrentEncoding
{
получить {вернуть Encoding.Unicode};
}
}
Public new static только для чтения DynamicStreamReader Null = (DynamicStreamReader)(new NullStreamReader());
внутренний DynamicStreamReader() {}
public DynamicStreamReader (поток потока)
: this (поток, Encoding.UTF8, true, DefaultBufferSize) { }
public DynamicStreamReader (поток потока, boolDetect_encoding_from_bytemarks)
: this (поток, Encoding.UTF8, define_encoding_from_bytemarks, DefaultBufferSize) { }
public DynamicStreamReader (поток потока, кодировка кодировки)
: this (поток, кодировка, правда, DefaultBufferSize) { }
public DynamicStreamReader (поток потока, кодировка кодирования, boolDetect_encoding_from_bytemarks)
: this (поток, кодирование,Detect_encoding_from_bytemarks, DefaultBufferSize) { }
public DynamicStreamReader (поток потока, кодировка кодировки, boolDetect_encoding_from_bytemarks, intuffer_size)
{
Инициализировать (поток, кодирование, обнаружение_кодирования_из_байтовых знаков, размер_буфера);
}
Public DynamicStreamReader (строковый путь)
: this (path, Encoding.UTF8, true, DefaultFileBufferSize) { }
public DynamicStreamReader (строковый путь, boolDetect_encoding_from_bytemarks)
: this (path, Encoding.UTF8, define_encoding_from_bytemarks, DefaultFileBufferSize) { }
public DynamicStreamReader (строковый путь, кодировка кодировки)
: this (путь, кодировка, правда, DefaultFileBufferSize) { }
public DynamicStreamReader (строковый путь, кодировка кодировки, boolDetect_encoding_from_bytemarks)
: this (путь, кодировка,Detect_encoding_from_bytemarks, DefaultFileBufferSize) { }
public DynamicStreamReader (строковый путь, кодировка кодировки, boolDetect_encoding_from_bytemarks, intuffer_size)
{
если (ноль == путь)
выдать новое ArgumentNullException("путь");
если (String.Empty == путь)
throw new ArgumentException("Пустой путь не разрешен");
если (path.IndexOfAny (Path.InvalidPathChars) != -1)
throw new ArgumentException("путь содержит недопустимые символы");
если (ноль == кодировка)
выдать новое исключение ArgumentNullException («кодирование»);
если (размер_буфера <= 0)
throw new ArgumentOutOfRangeException ("buffer_size", "Минимальный размер буфера должен быть положительным");
string DirName = Path.GetDirectoryName(path);
if (DirName != String.Empty && !Directory.Exists(DirName))
выдать новое исключение DirectoryNotFoundException («Каталог '» + Имя каталога + «' не найден.»);
если (!File.Exists(путь))
throw new FileNotFoundException("Файл не найден", путь
Stream поток = (Stream) File.OpenRead (путь);
Инициализировать (поток, кодирование, обнаружение_кодирования_из_байтовых знаков, размер_буфера);
}
Внутренняя пустота Инициализация (поток потока, кодировка кодировки, boolDetect_encoding_from_bytemarks, intuffer_size)
{
если (нуль == поток)
выдать новое исключение ArgumentNullException («поток»);
если (ноль == кодировка)
выдать новое исключение ArgumentNullException («кодирование»);
если (!stream.CanRead)
выдать новое ArgumentException («Невозможно прочитать поток»);
если (размер_буфера <= 0)
выдать новое исключение ArgumentOutOfRangeException ("buffer_size", "Минимальный размер буфера должен быть положительным")
;
buffer_size = МинимальныйBufferSize
= поток;
input_buffer = новый байт [buffer_size];
this.buffer_size = buffer_size;
this.encoding = кодировка;
декодер = кодировка.GetDecoder ();
байт [] преамбула = кодировка.GetPreamble ();
do_checks = обнаружить_кодирование_из_байтовых знаков? 1: 0;
do_checks += (preamble.Length == 0) ? 0: 2;
decoded_buffer = новый символ [encoding.GetMaxCharCount (buffer_size)];
decoded_count = 0;
поз = 0;
pos_input = 0;
}
общедоступный виртуальный поток BaseStream
{
получать
{
вернуть базовый_поток;
}
}
общедоступное виртуальное кодирование CurrentEncoding
{
получать
{
если (кодировка == ноль)
выбросить новое исключение ();
возвратная кодировка;
}
набор
{
кодировка = значение;
декодер = кодирование.GetDecoder();
decoded_count = pos + decoder.GetChars (input_buffer, pos_input, cbEncoded - pos_input, decoded_buffer, pos);
//DiscardBufferedData();
}
}
публичное переопределение void Close ()
{
Утилизировать (правда);
}
protected override void Dispose (удаление bool)
{
if (распоряжение && base_stream != null)
base_stream.Close ();
input_buffer = ноль;
decoded_buffer = ноль;
кодировка = ноль;
декодер = ноль;
base_stream = ноль;
base.Dispose (удаление);
}
//
// Обеспечивает автоопределение кодировки, а также пропуск
// байтовые метки в начале потока.
//
int DoChecks (количество целых чисел)
{
если ((do_checks & 2) == 2)
{
байт [] преамбула = кодировка.GetPreamble ();
int c = преамбула.Длина;
если (счет >= с)
{
интервал я;
для (я = 0; я <с; я++)
if (input_buffer [i] != преамбула [i])
сломать,
если (я == с)
вернуть я;
}
}
if ((do_checks & 1) == 1)
{
если (количество < 2)
вернуть 0;
если (input_buffer [0] == 0xfe && input_buffer [1] == 0xff)
{
this.encoding = Encoding.BigEndianUnicode;
возврат 2;
}
if (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;
}
public void DiscardBufferedData ()
{
pos = decoded_count = 0;
майБлок = ложь;
// Также отбрасываем внутреннее состояние декодера.
декодер = кодирование.GetDecoder ();
}
интервал cbEncoded;
интервал parse_start;
//буфер пуст, заполняем его еще раз
частный int ReadBuffer()
{
поз = 0;
pos_input = 0;
cbEncoded = 0
// продолжаем цикл, пока декодер не выдаст нам несколько символов
decoded_count = 0;
parse_start = 0;
делать
{
cbEncoded = base_stream.Read (input_buffer, 0, buffer_size);
если (cbEncoded == 0)
вернуть 0
mayBlock = (cbEncoded <uffer_size);
если (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);
parse_start = 0;
} Пока (decoded_count == 0);
возврат decoded_count;
}
публичное переопределение int Peek ()
{
если (base_stream == ноль)
выдать новое исключение ObjectDisposeException («StreamReader», «Невозможно прочитать из закрытого StreamReader»);
if (pos >= decoded_count && (mayBlock || ReadBuffer () == 0))
вернуть -1;
вернуть декодированный_буфер [поз];
}
публичное переопределение int Read ()
{
throw new Exception("Динамическому чтению не удалось прочитать!");
}
public override int Read ([In, Out] char[] dest_buffer, int index, int count)
{
throw new Exception("Динамическому чтению не удалось прочитать!");
}
Bool FoundCR_input;
int FindNextInputEOL()
{
символ c = '