Точки, уже сделанные в отношении BaseStream являются действительными и важными. Однако есть ситуации, когда вы хотите прочитать текст и узнать, где вы находитесь в тексте. По-прежнему полезно написать это как класс, чтобы упростить повторное использование.
Я попытался написать такой класс сейчас. Кажется, что он работает правильно, но он довольно медленный. Это должно быть хорошо, когда производительность не имеет решающего значения (это не , что медленно, см. Ниже).
Я использую ту же логику для отслеживания позиции в тексте, независимо от того, читаете ли вы символ за один раз, по одному буфере за раз или по одной строке за раз. Хотя я уверен, что это можно сделать, чтобы сделать это лучше, отказавшись от этого, это значительно упростило реализацию ... и, я надеюсь, следовать коду.
Проведено очень простое сравнение производительности метода ReadLine (которое, по моему мнению, является самой слабой точкой этой реализации) для StreamReader, а разница почти на порядок. Я получил 22 МБ/с, используя мой класс StreamReaderEx, но почти в 9 раз больше, используя StreamReader напрямую (на моем ноутбуке, оборудованном SSD). Хотя это может быть интересно, я не знаю, как сделать правильный тест для чтения; возможно, используя 2 идентичных файла, каждый из которых больше, чем буфер диска, и читает их поочередно ..? По крайней мере, мой простой тест дает согласованные результаты, когда я запускаю его несколько раз, и независимо от того, какой класс сначала читает тестовый файл.
Символ NewLine по умолчанию имеет значение Environment.NewLine, но может быть настроен на любую строку длиной 1 или 2. Читатель рассматривает только этот символ как новую строку, что может быть недостатком. По крайней мере, я знаю, что Visual Studio подсказала мне много раз, что файл, который я открываю, имеет «непоследовательные строки».
Обратите внимание, что я не включил класс Guard; это простой класс утилиты, и он должен быть obvoius из контекста, как его заменить. Вы даже можете удалить его, но вы потеряете некоторые проверки аргументов, и, следовательно, полученный код будет дальше от «правильного». Например, Guard.NotNull (s, «s») просто проверяет, что s не является нулевым, выбрасывая ArgumentNullException (с именем аргумента «s», следовательно, второй параметр), если это так.
Хватит болтовни, вот код:
public class StreamReaderEx : StreamReader
{
// NewLine characters (magic value -1: "not used").
int newLine1, newLine2;
// The last character read was the first character of the NewLine symbol AND we are using a two-character symbol.
bool insideNewLine;
// StringBuilder used for ReadLine implementation.
StringBuilder lineBuilder = new StringBuilder();
public StreamReaderEx(string path, string newLine = "\r\n") : base(path)
{
init(newLine);
}
public StreamReaderEx(Stream s, string newLine = "\r\n") : base(s)
{
init(newLine);
}
public string NewLine
{
get { return "" + (char)newLine1 + (char)newLine2; }
private set
{
Guard.NotNull(value, "value");
Guard.Range(value.Length, 1, 2, "Only 1 to 2 character NewLine symbols are supported.");
newLine1 = value[0];
newLine2 = (value.Length == 2 ? value[1] : -1);
}
}
public int LineNumber { get; private set; }
public int LinePosition { get; private set; }
public override int Read()
{
int next = base.Read();
trackTextPosition(next);
return next;
}
public override int Read(char[] buffer, int index, int count)
{
int n = base.Read(buffer, index, count);
for (int i = 0; i
Это также не учитывает использование 'Seek'. –