Ищите через FileStream, затем используйте StreamReader для чтения оттуда

Итак, я хочу иметь возможность искать точку в файловом потоке, а затем читать вперед с помощью StreamReader. Затем снова выполните поиск вперед и используйте StreamReader для чтения другого фрагмента данных.

const int BufferSize = 4096;
var buffer = new char[BufferSize];

var endpoints = new List<long>();

using (var fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{ 
    var fileLength = fileStream.Length;

    var seekPositionCount = fileLength / concurrentReads;

    long currentOffset = 0;
    for (var i = 0; i < concurrentReads; i++)
    {
        var seekPosition = seekPositionCount + currentOffset;

        // seek the file forward
        fileStream.Seek(seekPosition, SeekOrigin.Current);

        // setting true at the end is very important, keeps the underlying fileStream open.
        using (var streamReader = new StreamReader(fileStream, Encoding.UTF8, true, BufferSize, true))
        {
            // this also seeks the file forward the amount in the buffer...
            int bytesRead;
            var totalBytesRead = 0;
            while ((bytesRead = await streamReader.ReadAsync(buffer, 0, buffer.Length)) > 0)
            {
                totalBytesRead += bytesRead;

                var found = false;

                var gotR = false;

                for (var j = 0; j < buffer.Length; j++)
                {
                    if (buffer[j] == '\r')
                    {
                        gotR = true;
                        continue;
                    }

                    if (buffer[j] == '\n' && gotR)
                    {
                        // so we add the total bytes read, minus the current buffer amount read, then add how far into the buffer we actually read.
                        seekPosition += totalBytesRead - BufferSize + j;
                        endpoints.Add(seekPosition);
                        found = true;
                        break;
                    }
                }

                if (found) break;
            }
        }
        
        // we need to seek to the position we got to in the StreamReader (but not going by how much was read).
        fileStream.Seek(seekPosition, SeekOrigin.Current);

        currentOffset += seekPosition;
    }
}

return endpoints;

Однако я добираюсь до двух записей в endpoints, и он выходит.

(bytesRead = await streamReader.ReadAsync(buffer, 0, buffer.Length)) > 0

Я думал, что аргументы, которые вы передаете ReadAsync, относятся исключительно к буферу, поэтому я думал, что аргумент index должен был сказать: заполнить buffer в index.

Я не могу понять из справочного источника, как это используется значение.

Я предположил (и не могу найти доказательств для подтверждения), что когда вы открываете StreamReader, он использует базовый Stream в качестве руководства, поэтому, когда вы попросите прочитать несколько байтов, он начнется с позиции, в которой находится базовый Stream. ...

Но результаты того, что я делаю, не показывают этого, они, кажется, показывают, что StreamReader каждый раз начинается в начале Stream - однако я не могу найти доказательства, подтверждающие, что это так. это либо...

Ищу

Верно ли мое понимание поиска в том смысле, что если я назову поиск

fileStream.Seek(seekPosition, SeekOrigin.Current);

Если файл находится в 300, я хочу найти 600, указанная выше переменная seekPosition должна быть 600??

ReferenceSource сказал бы иначе:

else if (origin == SeekOrigin.Current) {
    // Don't call FlushRead here, which would have caused an infinite
    // loop.  Simply adjust the seek origin.  This isn't necessary
    // if we're seeking relative to the beginning or end of the stream.
    offset -= (_readLen - _readPos);
}

person Callum Linington    schedule 15.07.2016    source источник
comment
StreamReader сохраняет свой собственный буфер, вам придется вызвать его метод DiscardBufferedData(), чтобы принудительно выполнить повторную синхронизацию с FileStream.   -  person Hans Passant    schedule 15.07.2016
comment
Где о, после каждого прочитанного?   -  person Callum Linington    schedule 15.07.2016
comment
@HansPassant ааа исходники говорят   -  person Callum Linington    schedule 15.07.2016
comment
@HansPassant это интересно, потому что там написано, что если вам нужно перечитать, но если вы постоянно ищете вперед, то почему это будет неэффективно...   -  person Callum Linington    schedule 15.07.2016
comment
Найден аналогичный вопрос   -  person Callum Linington    schedule 15.07.2016


Ответы (1)


Итак, благодаря Хансу Пассанту, я получил ответ:

var buffer = new char[BufferSize];

var endpoints = new List<long>();

using (var fileStream = this.CreateMultipleReadAccessFileStream(fileName))
{
    var fileLength = fileStream.Length;

    var seekPositionCount = fileLength / concurrentReads;

    long currentOffset = 0;
    for (var i = 0; i < concurrentReads; i++)
    {
        var seekPosition = seekPositionCount + currentOffset;

        // seek the file forward
        // fileStream.Seek(seekPosition, SeekOrigin.Current);

        // setting true at the end is very important, keeps the underlying fileStream open.
        using (var streamReader = this.CreateTemporaryStreamReader(fileStream))
        {
            // this is poor on performance, hence why you split the file here and read in new threads.
            streamReader.DiscardBufferedData();
            // you have to advance the fileStream here, because of the previous line
            streamReader.BaseStream.Seek(seekPosition, SeekOrigin.Begin);
            // this also seeks the file forward the amount in the buffer...
            int bytesRead;
            var totalBytesRead = 0;
            while ((bytesRead = await streamReader.ReadAsync(buffer, 0, buffer.Length)) > 0)
            {
                totalBytesRead += bytesRead;

                var found = false;

                var gotR = false;

                for (var j = 0; j < buffer.Length; j++)
                {
                    if (buffer[j] == '\r')
                    {
                        gotR = true;
                        continue;
                    }

                    if (buffer[j] == '\n' && gotR)
                    {
                        // so we add the total bytes read, minus the current buffer amount read, then add how far into the buffer we actually read.
                        seekPosition += totalBytesRead - BufferSize + j;
                        endpoints.Add(seekPosition);
                        found = true;
                        break;
                    }
                    // if we have found new line then move the position to 
                }

                if (found) break;
            }
        }

        currentOffset = seekPosition;
    }
}

return endpoints;

Обратите внимание на новую часть, а не делайте это дважды:

fileStream.Seek(seekPosition, SeekOrigin.Current);

Теперь я использую SeekOrigin.Begin и StreamReader для продвижения основного базового потока:

// this is poor on performance, hence why you split the file here and read in new threads.
streamReader.DiscardBufferedData();
// you have to advance the fileStream here, because of the previous line
streamReader.BaseStream.Seek(seekPosition, SeekOrigin.Begin);

DiscardBufferedData будет означать, что я всегда использую базовую позицию потока.

person Callum Linington    schedule 15.07.2016