Чтение файла: заглянуть на несколько символов вперёд
От: 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() {

        }
    }
всю ночь не ем, весь день не сплю — устаю
Re: Чтение файла: заглянуть на несколько символов вперёд
От: Jolly Roger  
Дата: 07.12.10 07:30
Оценка: 2 (1)
Здравствуйте, Neco, Вы писали:

Да нет никакой проблемы, тем более — с CSV Переносов, кстати, тоже, в CSV строка файла == строка таблицы. Просто читаете целиком одну строку в свой буфер и ходите по нему сколько угодно. Но необходимости в таком хождении в общем случае тоже нет. Даже читая посимвольно из однонаправленного потока, складываете символы в буфер, пока не обнаружите конец лексемы, а потом уже смотрите, что за лексема. Для кавычек и т.п. просто заведите ещё одно состояние, да и всё.
"Нормальные герои всегда идут в обход!"
Re[2]: Чтение файла: заглянуть на несколько символов вперёд
От: _FRED_ Черногория
Дата: 07.12.10 07:44
Оценка: 1 (1)
Здравствуйте, Jolly Roger, Вы писали:

JR>Да нет никакой проблемы, тем более — с CSV Переносов, кстати, тоже, в CSV строка файла == строка таблицы.


Не правда ваша. Отквоченное поле может содержать переносы строк:

  • Fields with embedded line breaks must be enclosed within double-quote characters.
    1997,Ford,E350,"Go get one now
    they are going fast"

  • Help will always be given at Hogwarts to those who ask for it.
    Re[3]: Чтение файла: заглянуть на несколько символов вперёд
    От: Jolly Roger  
    Дата: 07.12.10 07:57
    Оценка: +2
    Здравствуйте, _FRED_, Вы писали:

    _FR>Не правда ваша.


    О как Ну и ладно, задачу всё равно это практически не усложняет.
    "Нормальные герои всегда идут в обход!"
    Re: Чтение файла: заглянуть на несколько символов вперёд
    От: matumba  
    Дата: 07.12.10 08:02
    Оценка: +2
    Здравствуйте, Neco, Вы писали:

    N>И проблема с тем, что в общем случае надо читать несколько символов наперёд


    Строго говоря, НЕ НАДО. Перепишите алгоритм, он наверняка хромает. У меня реализован разбор на конечном автомате — ничего "предзаглядывать" не понадобилось.
    Re[2]: Чтение файла: заглянуть на несколько символов вперёд
    От: Neco  
    Дата: 07.12.10 09:23
    Оценка:
    Здравствуйте, matumba, Вы писали:

    M>Строго говоря, НЕ НАДО. Перепишите алгоритм, он наверняка хромает. У меня реализован разбор на конечном автомате — ничего "предзаглядывать" не понадобилось.

    А как? Хоть намекните...
    Я так понимаю, что можно добавить новые состояния, когда парсер "съел" половину переноса строки и ожидает вторую. Но при этом надо запоминать уже съеденную часть, чтобы в случае, если перенос строки не состоялся поменять поведение — в общем какой-то головняк.
    Вот мой алгоритм:
            private string[] ReadCsvLine() {
                var rez = new List<string>();
                var state = ReadingState.ExpectDataStart;
                bool end_of_line = false;
                char[] extra_data = null;
    
                StringBuilder current_data = null;
                do {
                    if (_reader.EndOfStream) {
                        switch (state) {
                            case ReadingState.Data:
                            case ReadingState.ExpectDataStart: {
                                    AddValue(rez, current_data);
                                    break;
                                }
                            case ReadingState.QuotedData: {
                                    throw new CsvParserException("Unexpected end of line!");
                                }
                            case ReadingState.ExpectSeparator: {
                                    // do nothing
                                    break;
                                }
                            default: {
                                    throw new CsvParserException(string.Format("Unknown state [{0}]", state));
                                }
                        }
                        end_of_line = true;
                        break;
                    }
                    char cur = (char)_reader.Read();
                    switch (state) {
                        case ReadingState.ExpectDataStart: {
                                if (cur == '"') {
                                    current_data = new StringBuilder();
                                    state = ReadingState.QuotedData;
                                } else if (cur == ',') {
                                    AddValue(rez, current_data);
                                } else if (IsNewLine(cur, _reader)) {
                                    AddValue(rez, current_data);
                                    state = ReadingState.NoRead;
                                    end_of_line = true;
                                } else {
                                    current_data = new StringBuilder();
                                    current_data.Append(cur);
                                    state = ReadingState.Data;
                                }
                                break;
                            }
                        case ReadingState.Data: {
                                if (cur == '"') {
                                    throw new CsvParserException(string.Format("Unexpected quota inside data! Position: [{0}]", _reader.Position));
                                } else if (cur == ',') {
                                    AddValue(rez, current_data);
                                    current_data = null;
                                    state = ReadingState.ExpectDataStart;
                                } else if (IsNewLine(cur, _reader)) {
                                    AddValue(rez, current_data);
                                    current_data = null;
                                    state = ReadingState.NoRead;
                                    end_of_line = true;
                                } else {
                                    current_data.Append(cur);
                                    if (extra_data != null) {
                                        current_data.Append(extra_data);
                                    }
                                }
                                break;
                            }
                        case ReadingState.QuotedData: {
                                if (cur == '"') {
                                    if (NextSymbolIsQuota(_reader)) {
                                        current_data.Append(cur);
                                        _reader.Read();
                                    } else {
                                        AddValue(rez, current_data);
                                        current_data = null;
                                        state = ReadingState.ExpectSeparator;
                                    }
                                } else {
                                    current_data.Append(cur);
                                }
                                break;
                            }
                        case ReadingState.ExpectSeparator: {
                                if (cur == ',') {
                                    current_data = null;
                                    state = ReadingState.ExpectDataStart;
                                } else if (IsNewLine(cur, _reader)) {
                                    state = ReadingState.NoRead;
                                    end_of_line = true;
                                } else {
                                    throw new CsvParserException(string.Format("Expected separator! Position: [{0}]", _reader.Position));
                                }
                                break;
                            }
                        default: {
                                throw new CsvParserException(string.Format("Unknown state [{0}]", state));
                            }
                    }
                } while (!end_of_line);
                return rez.ToArray();
            }
            private void AddValue(List<string> data, StringBuilder sb) {
                if (sb != null) {
                    data.Add(sb.ToString());
                } else {
                    data.Add(null);
                }
            }
            private bool NextSymbolIsQuota(ExStreamReader reader) {
                if (reader.EndOfStream) {
                    return false;
                } else {
                    char nc = (char)reader.Read();
                    reader.CancelLastReading();
                    return nc == '"';
                }
            }
            private bool IsNewLine(char ch, ExStreamReader reader) {
                if (ch == _newLine[0]) {
    
                    bool is_new_line = true;
                    var buf = new char[_newLine.Length - 1];
                    int bytes_read = reader.Read(buf, 0, buf.Length);
                    for (int i = 0; i < bytes_read; i++) {
                        char val_from_buf = buf[i];
                        char val_from_nl = _newLine[i + 1];
                        if (val_from_buf != val_from_nl) {
                            is_new_line = false;
                            break;
                        }
                    }
                    if (is_new_line) {
                        return true;
                    } else {
                        reader.CancelLastReading();
                        return false;
                    }
                } else {
                    return false;
                }
            }
            private enum ReadingState {
                ExpectDataStart = 0, Data, QuotedData, ExpectSeparator, NoRead /* expected that reading will not be performed in this state */
            }


    строго говоря он даже не совсем мой. я его слизал откуда-то и доработал на предмет распознавания разных переносов строк и ещё какой-то мульки (уже не помню).
    всю ночь не ем, весь день не сплю — устаю
    Re[2]: Чтение файла: заглянуть на несколько символов вперёд
    От: Neco  
    Дата: 07.12.10 09:26
    Оценка:
    Здравствуйте, Jolly Roger, Вы писали:

    JR> Даже читая посимвольно из однонаправленного потока, складываете символы в буфер, пока не обнаружите конец лексемы, а потом уже смотрите, что за лексема.

    А кстати, может в этом и соль. Я ориентируюсь на начало лексемы, а возможно надо на конец. Надо обмозговать, когда время будет.
    всю ночь не ем, весь день не сплю — устаю
    Re: Чтение файла: заглянуть на несколько символов вперёд
    От: hardcase Пират http://nemerle.org
    Дата: 07.12.10 10:47
    Оценка: +1
    Здравствуйте, Neco, Вы писали:

    N>Писал csv парсер и столкнулся с проблемой, когда стандартных библиотечных функций как будто бы не хватает.


    Конечно нехватает. Парсеры писать вручную работа неблагодарная совсем.
    Почему бы не использовать готовые парсеры (я не верю что нет доступных csv-парсеров)? Или же не сгенерировать его тем же Coco/R-ом? Хотя я бы Nemerle.Peg задействовал
    /* иЗвиНите зА неРовнЫй поЧерК */
    Re[2]: Чтение файла: заглянуть на несколько символов вперёд
    От: Neco  
    Дата: 07.12.10 14:21
    Оценка:
    Здравствуйте, hardcase, Вы писали:

    H>Конечно нехватает. Парсеры писать вручную работа неблагодарная совсем.

    H>Почему бы не использовать готовые парсеры (я не верю что нет доступных csv-парсеров)?
    Всё начиналось с быстрого решения на MSJET. Потом оно оказалось неспособным что-то читать и пришлось уже думать над чем-то своим.

    H>Или же не сгенерировать его тем же Coco/R-ом? Хотя я бы Nemerle.Peg задействовал

    Знал бы, я б попробовал, конечно. Но сдаётся мне, что это поможет только по части ускорения написания (что не есть проблема). Мне-то судя по-всему надо правильно выделить состояния конечного автомата.
    Плюс нехватка времени на изучение новых горизнтов даёт о себе знать.

    В общем, сменил подглючноватый ExStreamReader на другую версию (сменив подход — теперь просто буферизую заранее прочитанные символы):

        public class ExStreamReader : IDisposable {
            private StreamReader _reader;
            private InternalBuffer _buffer;
    
            public ExStreamReader(Stream stream, Encoding enconding) {
                _reader = new StreamReader(stream, enconding);
                _buffer = new InternalBuffer(_reader);
            }
            public char Read() {
                if (_buffer.IsEmpty) {
                    return (char)_reader.Read();
                } else {
                    return _buffer.Read();
                }
            }
            public int PreRead(ref char[] buffer, int index, int count) {
                return _buffer.PreRead(ref buffer, index, count);
            }
            public void ConfirmPreRead() {
                _buffer.Clear();
            }
    
            public bool EndOfStream {
                get {
                    return _reader.EndOfStream;
                }
            }
            public long Position {
                get { return _reader.BaseStream.Position; }
            }
    
            public void Dispose() {
                _reader.Dispose();
            }
    
            private class InternalBuffer {
                private StreamReader _rdr;
                private Queue<char> _buf = new Queue<char>();
    
                public InternalBuffer(StreamReader rdr) {
                    _rdr = rdr;
                }
                public int PreRead(ref char[] buffer, int index, int count) {
                    int bytes = _rdr.Read(buffer, index, count);
                    for (int i = index; i < count + index; i++) {
                        _buf.Enqueue(buffer[i]);
                    }
                    return bytes;
                }
                public char Read() {
                    if (IsEmpty) {
                        throw new InvalidOperationException("You cannot read from InternalBuffer when it is empty");
                    }
                    return _buf.Dequeue();
                }
                public bool IsEmpty {
                    get {
                        return _buf.Count == 0;
                    }
                }
                public void Clear() {
                    _buf.Clear();
                }
            }
        }


    а сам парсер остался практически без изменений. Вроде работает и по скорости будет гораздо лучше, хотя исходный вопрос остаётся открытый.
    всю ночь не ем, весь день не сплю — устаю
    Re[3]: Чтение файла: заглянуть на несколько символов вперёд
    От: Ziaw Россия  
    Дата: 07.12.10 16:57
    Оценка: +1
    Здравствуйте, Neco, Вы писали:

    H>>Или же не сгенерировать его тем же Coco/R-ом? Хотя я бы Nemerle.Peg задействовал

    N>Знал бы, я б попробовал, конечно. Но сдаётся мне, что это поможет только по части ускорения написания (что не есть проблема). Мне-то судя по-всему надо правильно выделить состояния конечного автомата.
    N>Плюс нехватка времени на изучение новых горизнтов даёт о себе знать.

    Когда я набросал на скорую руку парсер джейсона на Peg, был удивлен не столько скоростью написания (благо я уже юзал другие генераторы парсеров и в скорости написания peg не несет особых преимуществ, кроме тесной интеграции с .net), сколько скоростью самого парсера, он оказался быстрее рукописного Newton.Json в полтора раза. А автор джейсона вобщем-то обращает внимание на вопросы производительности.

    Нехватка времени довольно смешная причина делать задачу вручную не пользуясь инструментами для этого созданными.
    Re[3]: Чтение файла: заглянуть на несколько символов вперёд
    От: Sinclair Россия https://github.com/evilguest/
    Дата: 07.12.10 17:01
    Оценка: 4 (1)
    Здравствуйте, Neco, Вы писали:

    N>Здравствуйте, matumba, Вы писали:


    M>>Строго говоря, НЕ НАДО. Перепишите алгоритм, он наверняка хромает. У меня реализован разбор на конечном автомате — ничего "предзаглядывать" не понадобилось.

    N>А как? Хоть намекните...
    N>Я так понимаю, что можно добавить новые состояния, когда парсер "съел" половину переноса строки и ожидает вторую.
    Зачем?
    Состояний у парсера очень немного.
    1. BeforeData
    2. WithinData
    3. WithinQuotedData
    4. RightAfterTheQuote

    Переводы строк лучше склеивать, т.к. некоторые источники ограничиваются CR без LF. А "пустых строк" в CSV не бывает, поэтому CR CR = CR LF = LF LF = CR LF CR LF = "один перевод строки".
    Схема переходов примитивна:


    1. BeforeData:
    CR, LF:; // nothing
    *     : data[colNum] += char; State = WithinData;
    2. WithinData:
    ','   : ++colNum; 
    '"'   : State = WithinQuotedData;
    CR, LF: ++lineNum; colNum = 0; State = BeforeData;
    *     : data[colNum] += char; 
    3. WithinQuotedData:
    '"'   : state = RightAfterTheQuote;
    *     : data[colNum] += char;
    4. RightAfterTheQuote:
    ','   : ++colNum; State = WithinData;
    '"'   : data[colNum] += '"'; State = WithinQuotedData;
    CR, LF: ++lineNum; colNum = 0; State = BeforeData; 
    *     : throw Exception
    Уйдемте отсюда, Румата! У вас слишком богатые погреба.
    Re[4]: Чтение файла: заглянуть на несколько символов вперёд
    От: VladD2 Российская Империя www.nemerle.org
    Дата: 07.12.10 17:29
    Оценка:
    Здравствуйте, Ziaw, Вы писали:

    Z>Нехватка времени довольно смешная причина делать задачу вручную не пользуясь инструментами для этого созданными.


    Ага. Я бы даже сказал — парадоксальная. Просто (видимо) народ настолько привык к тому, что внешние "тулы" очень трудно сконфигурированный и прикрутить к процессу сборки, что и отношение соответствующие.
    Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
    Re[4]: Чтение файла: заглянуть на несколько символов вперёд
    От: VladD2 Российская Империя www.nemerle.org
    Дата: 07.12.10 17:33
    Оценка: +1
    Здравствуйте, Sinclair, Вы писали:

    S>Зачем?

    S>Состояний у парсера очень немного.
    S>1. BeforeData
    S>2. WithinData
    S>3. WithinQuotedData
    S>4. RightAfterTheQuote

    А зачем такие задачи вообще вручную решать? Я вижу только одну причину — изучение ОК.
    Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
    Re: Чтение файла: заглянуть на несколько символов вперёд
    От: _nn_ www.nemerleweb.com
    Дата: 07.12.10 18:06
    Оценка: 8 (1)
    Здравствуйте, Neco, Вы писали:

    Возьмите готовый LinqToCSV
    http://rsdn.nemerleweb.com
    http://nemerleweb.com
    Re[5]: Чтение файла: заглянуть на несколько символов вперёд
    От: Sinclair Россия https://github.com/evilguest/
    Дата: 07.12.10 18:26
    Оценка:
    Здравствуйте, VladD2, Вы писали:

    VD>А зачем такие задачи вообще вручную решать? Я вижу только одну причину — изучение ОК.

    Ну ты же знаешь — я от переднего края отстал на годы. Для меня всё ещё написать свой парсер CSV с нуля, потом поматериться и отладить, потом поматериться и ускорить, потом поматериться и снова отладить — проще, чем даже взять готовый.
    А уж тем более чем взять готовый генератор парсеров.
    Можешь, кстати, показать, как правильные пацаны сейчас пишут парсер CSV? (Предположим, что все готовые парсеры — заведомое УГ).
    Уйдемте отсюда, Румата! У вас слишком богатые погреба.
    Re: Чтение файла: заглянуть на несколько символов вперёд
    От: fddima  
    Дата: 07.12.10 18:46
    Оценка:
    Здравствуйте, Neco, Вы писали:

    Если хочется иметь look-a-head, тем более для такого простого парсера — избавляемся от посимвольных чтений вообще, работаем только с буфером. Это не сложно, и на порядок быстрее.
    Re[6]: Чтение файла: заглянуть на несколько символов вперёд
    От: VladD2 Российская Империя www.nemerle.org
    Дата: 07.12.10 18:50
    Оценка: 3 (1)
    Здравствуйте, Sinclair, Вы писали:

    S>Ну ты же знаешь — я от переднего края отстал на годы. Для меня всё ещё написать свой парсер CSV с нуля, потом поматериться и отладить, потом поматериться и ускорить, потом поматериться и снова отладить — проще, чем даже взять готовый.


    Да Вы батенька не отсталис! Вы батенька стареете похожес.

    S>А уж тем более чем взять готовый генератор парсеров.

    S>Можешь, кстати, показать, как правильные пацаны сейчас пишут парсер CSV? (Предположим, что все готовые парсеры — заведомое УГ).

    Да я тоже уже постарел. В показательных выступлениях не участвую. Как грится — дорогу молодежи!

    Скажу только, что парсер CSV пишется с использованием того же PegGrammar-а где-то за час. Без опыта за 3.

    В качестве примера могу показать свой парсер XML-я с брэкджеком и шлюхами (со сплайсами и встроенными операторами foreach, when и unless) — здесь. Он правда по сложнее раз эдак в несколько, но за счет декларативности код грамматики влезает на один экран (сравнимый рукописный парсер на шарпе потянет строк эдак на 1000).

    Еще можно попросить Ziaw-а чтобы он выложил код своего парсера JSON-а
    Автор: Ziaw
    Дата: 07.12.10
    . Он правда (по его же словам), с ним не за час справился, а за три, но то в основном потому, что опыта мало было. Зато обошелся без вопросов в форумах.
    Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
    Re[2]: Чтение файла: заглянуть на несколько символов вперёд
    От: VladD2 Российская Империя www.nemerle.org
    Дата: 07.12.10 18:53
    Оценка:
    Здравствуйте, fddima, Вы писали:

    F>Если хочется иметь look-a-head, тем более для такого простого парсера — избавляемся от посимвольных чтений вообще, работаем только с буфером. Это не сложно, и на порядок быстрее.


    Я пропустил что-то новое в теории парсеров?

    Раньше принято было считать, что быстрее все будет при заглядывании вперед на 0-1 символ, а чем больше заглядывание, тем медленнее разбор (в плоть до экспоненты).
    Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
    Re[3]: Чтение файла: заглянуть на несколько символов вперёд
    От: matumba  
    Дата: 07.12.10 19:08
    Оценка:
    Здравствуйте, Neco, Вы писали:

    M>>Строго говоря, НЕ НАДО. Перепишите алгоритм, он наверняка хромает. У меня реализован разбор на конечном автомате — ничего "предзаглядывать" не понадобилось.

    N>А как? Хоть намекните...
    N>Я так понимаю, что можно добавить новые состояния, когда парсер "съел" половину переноса строки и ожидает вторую. Но при этом надо запоминать уже съеденную часть....

    Ты сам себе намекнул.

    Весь автомат укладывается в 3 состояния с сохранением промежуточного буфера:
    0: читаем и запоминаем поля (обрабатывая запятую), пока не попадём на кавычки. Кавычки -> 1
    1: Мы внутри строки — запоминаем символы или ждём очередную кавычку. Кавычки -> 2
    2: Здесь ждём либо кавычки, либо запятую, обрабатываем соответственно (т.к. кавычки могут быть удвоенными внутри строки и просто окончание строки).

    В конце проверяем состояние, если 1 — это ошибка. Не забываем про аккумулятор.

    Для большей наглядности нарисуй этот автомат — что и в каком состоянии может быть — он не тривиален, но без трюков.
    Re[3]: Чтение файла: заглянуть на несколько символов вперёд
    От: Sinclair Россия https://github.com/evilguest/
    Дата: 07.12.10 19:09
    Оценка: +1
    Здравствуйте, VladD2, Вы писали:

    VD>Раньше принято было считать, что быстрее все будет при заглядывании вперед на 0-1 символ, а чем больше заглядывание, тем медленнее разбор (в плоть до экспоненты).

    Тем более, что буферизация должна быть строго ортогональна алгоритму, собственно, разбора. Ещё не хватало свой BufferedStream каждый раз писать.
    Уйдемте отсюда, Румата! У вас слишком богатые погреба.
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.