Есть ли встроенный способ обработки нескольких файлов в одном потоке? - PullRequest
5 голосов
/ 10 февраля 2009

У меня есть список файлов, и мне нужно прочитать их каждый в определенном порядке в байт [] заданного размера. Само по себе это не проблема для отдельного файла, просто while ((got = fs.Read (piece, 0, pieceLength))> 0) отлично справляется со своей работой. Последний фрагмент файла может быть меньше, чем нужно, и это нормально.

Теперь есть один хитрый момент: если у меня есть несколько файлов, мне нужен один непрерывный поток, что означает, что если последний кусок файла меньше, чем pieceLength, то мне нужно прочитать (pieceLength-got) следующего файла, а затем продолжайте до конца последнего файла.

Таким образом, по сути, учитывая X-файлы, я всегда буду читать фрагменты, которые имеют длину, равную pieceLength, за исключением самого последнего фрагмента самого последнего файла, который может быть меньше.

Мне просто интересно, есть ли уже что-то встроенное в .net (3.5 SP1), которое бы помогло. Мой текущий подход заключается в создании класса, который принимает список файлов, а затем предоставляет функцию Read(byte[] buffer, long index, long length), аналогичную FileStream.Read (). Это должно быть довольно просто, потому что мне не нужно менять свой код вызова, который читает данные, но перед тем, как заново изобретать колесо, я просто хотел бы дважды проверить, что колесо еще не встроено в BCL.

Спасибо:)

Ответы [ 2 ]

7 голосов
/ 10 февраля 2009

Я не верю, что в фреймворке что-то есть, но я бы посоветовал сделать его немного более гибким - возьмите IEnumerable<Stream> в своем конструкторе и извлекайте из Stream себя. Затем, чтобы получить файловые потоки, вы можете (при условии C # 3.0) просто сделать:

Stream combined = new CombinationStream(files.Select(file => File.Open(file));

Часть «владение» здесь немного сложна - вышеприведенное позволит комбинированному потоку стать владельцем любого потока, из которого он читает, но вы можете не захотеть, чтобы он проходил через все остальные потоков и закройте их все, если это закрыто преждевременно.

4 голосов
/ 30 марта 2011

Вот то, что я придумал, основываясь на идее @jon skeet.

Это просто реализует Read, что было вполне достаточно для меня. (но мне не нужна помощь по реализации метода BeginRead / EndRead.) Вот полный код, содержащий как синхронизацию, так и асинхронность - чтение и BeginRead / EndRead

https://github.com/facebook-csharp-sdk/combination-stream/blob/master/src/CombinationStream-Net20/CombinationStream.cs

internal class CombinationStream : System.IO.Stream
{
    private readonly System.Collections.Generic.IList<System.IO.Stream> _streams;
    private int _currentStreamIndex;
    private System.IO.Stream _currentStream;
    private long _length = -1;
    private long _postion;

    public CombinationStream(System.Collections.Generic.IList<System.IO.Stream> streams)
    {
        if (streams == null)
        {
            throw new System.ArgumentNullException("streams");
        }

        _streams = streams;
        if (streams.Count > 0)
        {
            _currentStream = streams[_currentStreamIndex++];
        }
    }

    public override void Flush()
    {
        if (_currentStream != null)
        {
            _currentStream.Flush();
        }
    }

    public override long Seek(long offset, System.IO.SeekOrigin origin)
    {
        throw new System.InvalidOperationException("Stream is not seekable.");
    }

    public override void SetLength(long value)
    {
        this._length = value;
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        int result = 0;
        int buffPostion = offset;

        while (count > 0)
        {
            int bytesRead = _currentStream.Read(buffer, buffPostion, count);
            result += bytesRead;
            buffPostion += bytesRead;
            _postion += bytesRead;

            if (bytesRead <= count)
            {
                count -= bytesRead;
            }

            if (count > 0)
            {
                if (_currentStreamIndex >= _streams.Count)
                {
                    break;
                }

                _currentStream = _streams[_currentStreamIndex++];
            }
        }

        return result;
    }

    public override long Length
    {
        get
        {
            if (_length == -1)
            {
                _length = 0;
                foreach (var stream in _streams)
                {
                    _length += stream.Length;
                }
            }

            return _length;
        }
    }

    public override long Position
    {
        get { return this._postion; }
        set { throw new System.NotImplementedException(); }
    }
    public override void Write(byte[] buffer, int offset, int count)
    {
        throw new System.InvalidOperationException("Stream is not writable");
    }

    public override bool CanRead
    {
        get { return true; }
    }

    public override bool CanSeek
    {
        get { return false; }
    }

    public override bool CanWrite
    {
        get { return false; }
    }
}

Также доступно в виде пакета NuGet

Install-Package CombinationStream
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...