Чтение файла: заглянуть на несколько символов вперёд
От: Neco  
Дата: 07.12.10 06:01
Оценка:
Писал csv парсер и столкнулся с проблемой, когда стандартных библиотечных функций как будто бы не хватает.
Дело в том, что CsvParser это автомат, который читает файл посимвольно и переходит из одного состояния в другое. И проблема с тем, что в общем случае надо читать несколько символов наперёд, чтобы решить, что надо переходить в другое состояние (из-за кавычек и переносов строки). С кавычками хватает Peek у стандартного StreamReader'а. А с переносом строки сложнее. Самое простое, что придумал, это создать свой Reader, который перед каждым чтением буфера бэкапирует предыдущее состояние. А потом по специальному методу CancelLastReading он восстанавливает его. Но решение довольно ресурсозатратное.
Кто как решает эту проблему (если сталкивались)?
Пасиб.
P.S. Вот этот класс. Решение абсолютно черновое. Писал, лишь бы юнит тесты прогонялись.

    public class ExStreamReader : IDisposable {
        private Stream _stream;
        private Encoding _encoding;
        private Decoder _decoder;
        private char[] _buffer;
        private int _bufferSize;
        private int _bufferPosition;
        private char[] _newLine;
        private bool _endOfStream;
        private long _lastBaseStreamPosition;
        private int _lastBufferPosition;
        private int _lastBufferSize;
        private char[] _lastBuffer;

        public ExStreamReader(Stream stream, Encoding enconding)
            : this(stream, enconding, 1024) {
        }
        public ExStreamReader(Stream stream, Encoding enconding, int bufferSize) {
            _stream = stream;
            _endOfStream = _stream.Length == 0;
            _encoding = enconding;
            _decoder = _encoding.GetDecoder();
            NewLineSymbol = "\n";
            ByteBufferSize = bufferSize;
            _buffer = new char[ByteBufferSize];
        }


        public bool EndOfStream {
            get {
                return (_stream.Position == _stream.Length) && (_bufferPosition == _bufferSize);
            }
        }

        public int Read() {
            char[] ch = new char[1];
            int rez = Read(ch, 0, 1);
            if (rez > 0) {
                return ch[0];
            } else {
                return -1;
            }
        }
        public string ReadString(int charsToRead) {
            return new string(Read(charsToRead));
        }
        public char[] Read(int charsToRead) {
            char[] ch = new char[charsToRead];
            int rez = Read(ch, 0, charsToRead);
            if (rez != charsToRead) {
                char[] n_rez = new char[rez];
                Array.Copy(ch, n_rez, rez);
                return n_rez;
            } else {
                return ch;
            }
        }
        public int Read(char[] buffer, int startIndex, int charsToRead) {
            SaveInformationForCancelling();
            int charsReaded = 0;

            int destArrayStartIndex = startIndex;

            while (charsReaded < charsToRead && !EndOfStream) {
                if (_bufferPosition >= _bufferSize) {
                    ReadBuffer(_stream, _decoder, ref _buffer, ref _bufferPosition, ref _bufferSize, ref _endOfStream);
                }

                int rlCharsToRead = (charsToRead - charsReaded);
                if (rlCharsToRead > _bufferSize - _bufferPosition) {
                    rlCharsToRead = _bufferSize - _bufferPosition;
                }
                Array.Copy(_buffer, _bufferPosition, buffer, destArrayStartIndex, rlCharsToRead);
                charsReaded += rlCharsToRead;
                destArrayStartIndex += rlCharsToRead;
                _bufferPosition += rlCharsToRead;
            }
            return charsReaded;
        }
        private void SaveInformationForCancelling() {
            _lastBaseStreamPosition = _stream.Position;
            _lastBufferPosition = _bufferPosition;
            if (_lastBuffer == null || _lastBuffer.Length != _buffer.Length) {
                _lastBuffer = new char[_buffer.Length];
            }
            _buffer.CopyTo(_lastBuffer, 0);
            _lastBufferSize = _bufferSize;
        }
        public void CancelLastReading() {
            _stream.Position = _lastBaseStreamPosition;
            _bufferPosition = _lastBufferPosition;
            _bufferSize = _lastBufferSize;
            _buffer = new char[ByteBufferSize];
            if (_lastBuffer != null) {
                _lastBuffer.CopyTo(_buffer, 0);
            }
        }

        private void ReadBufferTailed(Stream stream, Decoder decoder, int tailStart, int tailSize, ref char[] buffer, ref int bufferPosition, ref int bufferSize, ref bool endOfStream) {
            var tail = new char[tailSize];
            Array.Copy(buffer, bufferPosition, tail, 0, tailSize);
            Array.Copy(tail, 0, buffer, 0, tailSize);
            ReadBuffer(stream, decoder, ref buffer, ref bufferPosition, ref bufferSize, tailSize, ByteBufferSize - tailSize, ref endOfStream);
            bufferPosition = 0;
            bufferSize = bufferSize + tailSize;
        }
        private void ReadBuffer(Stream stream, Decoder decoder, ref char[] buffer, ref int bufferPosition, ref int bufferSize, ref bool endOfStream) {
            ReadBuffer(stream, decoder, ref buffer, ref bufferPosition, ref bufferSize, 0, ByteBufferSize / 2, ref endOfStream);
        }
        private static void ReadBuffer(Stream stream, Decoder decoder, ref char[] buffer, ref int bufferPosition, ref int bufferSize, int startWritingFrom, int bytesToRead, ref bool endOfStream) {
            var b_buffer = new byte[bytesToRead];
            var cnt_read = stream.Read(b_buffer, 0, bytesToRead);
            if (cnt_read == 0) {
                bufferPosition = 0;
                bufferSize = 0;
                endOfStream = true;
            } else {
                bufferPosition = startWritingFrom;
                var chars_read = decoder.GetChars(b_buffer, 0, cnt_read, buffer, startWritingFrom);
                bufferSize = chars_read;
            }
        }


        private int _byteBufferSize;
        public int ByteBufferSize {
            get { return _byteBufferSize; }
            private set {
                if (value < 6) {
                    throw new ArgumentException("You cannot set buffer size shorter than maximum symbol size (6 bytes)");
                }
                _byteBufferSize = value;
            }
        }
        public string NewLineSymbol {
            get {
                return new string(_newLine);
            }
            set {
                _newLine = value.ToCharArray();
            }
        }
        public long Position {
            get {
                return _stream.Position - _bufferSize + _bufferPosition;
            }
        }

        public void Dispose() {

        }
    }
всю ночь не ем, весь день не сплю — устаю
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.