Как правильно остановить чтение файла?

161
28 декабря 2019, 23:40

Для упрощения считывания данных из бинарного файла, унаследовал класс BinaryReader, и описал несколько вспомогательных методов:

public class SegmentReader : BinaryReader
{
    public bool EndOfFile => BaseStream.Position + 1 >= BaseStream.Length - _endOffset;
    public string ReadString(int stringLength, Encoding encoding = null)
    {
        encoding = encoding ?? Encoding.Unicode;
        byte[] dataBytes = ReadBytes(encoding.IsSingleByte ? stringLength : stringLength * 2);
        return Encoding.Unicode.GetString(dataBytes).Replace("\n", "\\n");
    }
    public ushort[] ReadUInts16(int count)
    {
        Debug.Assert(BaseStream.CanSeek, "BaseStream.CanSeek");
        ushort[] result = new ushort[count];
        CheckEndOfFileAndThrow();
        for (int i = 0; i < count; i++)
        {
            result[i] = ReadUInt16();
        }
        CheckEndOfFileAndThrow();
        return result;
    }
    public ushort[] PeekUInts16(int count)
    {
        Debug.Assert(BaseStream.CanSeek, "BaseStream.CanSeek");
        ushort[] result = new ushort[count];
        long position = BaseStream.Position;
        CheckEndOfFileAndThrow();
        for (int i = 0; i < count; i++)
        {
            result[i] = ReadUInt16();
        }
        BaseStream.Position = position;
        CheckEndOfFileAndThrow();
        return result;
    }
    public byte[] PeekBytes(int count)
    {
        Debug.Assert(BaseStream.CanSeek, "BaseStream.CanSeek");
        long position = BaseStream.Position;
        CheckEndOfFileAndThrow();
        byte[] result = ReadBytes(count);
        BaseStream.Position = position;
        CheckEndOfFileAndThrow();
        return result;
    }
    public decimal PeekDecimal()
    {
        Debug.Assert(BaseStream.CanSeek, "BaseStream.CanSeek");
        long position = BaseStream.Position;
        CheckEndOfFileAndThrow();
        decimal result = ReadDecimal();
        BaseStream.Position = position;
        CheckEndOfFileAndThrow();
        return result;
    }
    public double PeekDouble()
    {
        Debug.Assert(BaseStream.CanSeek, "BaseStream.CanSeek");
        long position = BaseStream.Position;
        CheckEndOfFileAndThrow();
        double result = ReadDouble();
        BaseStream.Position = position;
        CheckEndOfFileAndThrow();
        return result;
    }
    public float PeekSingle()
    {
        Debug.Assert(BaseStream.CanSeek, "BaseStream.CanSeek");
        long position = BaseStream.Position;
        CheckEndOfFileAndThrow();
        float result = ReadSingle();
        BaseStream.Position = position;
        CheckEndOfFileAndThrow();
        return result;
    }
    public bool PeekBoolean()
    {
        Debug.Assert(BaseStream.CanSeek, "BaseStream.CanSeek");
        long position = BaseStream.Position;
        CheckEndOfFileAndThrow();
        bool result = ReadBoolean();
        BaseStream.Position = position;
        CheckEndOfFileAndThrow();
        return result;
    }
    public sbyte PeekSByte()
    {
        Debug.Assert(BaseStream.CanSeek, "BaseStream.CanSeek");
        long position = BaseStream.Position;
        CheckEndOfFileAndThrow();
        sbyte result = ReadSByte();
        BaseStream.Position = position;
        CheckEndOfFileAndThrow();
        return result;
    }
    public byte PeekByte()
    {
        Debug.Assert(BaseStream.CanSeek, "BaseStream.CanSeek");
        long position = BaseStream.Position;
        CheckEndOfFileAndThrow();
        byte result = ReadByte();
        BaseStream.Position = position;
        CheckEndOfFileAndThrow();
        return result;
    }
    public ulong PeekUInt64()
    {
        Debug.Assert(BaseStream.CanSeek, "BaseStream.CanSeek");
        long position = BaseStream.Position;
        CheckEndOfFileAndThrow();
        ulong result = ReadUInt64();
        BaseStream.Position = position;
        CheckEndOfFileAndThrow();
        return result;
    }
    public long PeekInt64()
    {
        Debug.Assert(BaseStream.CanSeek, "BaseStream.CanSeek");
        long position = BaseStream.Position;
        CheckEndOfFileAndThrow();
        long result = ReadInt64();
        BaseStream.Position = position;
        CheckEndOfFileAndThrow();
        return result;
    }
    public short PeekInt16()
    {
        Debug.Assert(BaseStream.CanSeek, "BaseStream.CanSeek");
        long position = BaseStream.Position;
        CheckEndOfFileAndThrow();
        short result = ReadInt16();
        BaseStream.Position = position;
        CheckEndOfFileAndThrow();
        return result;
    }
    public ushort PeekUInt16()
    {
        Debug.Assert(BaseStream.CanSeek, "BaseStream.CanSeek");
        long position = BaseStream.Position;
        CheckEndOfFileAndThrow();
        ushort result = ReadUInt16();
        BaseStream.Position = position;
        CheckEndOfFileAndThrow();
        return result;
    }
    public uint PeekUInt32()
    {
        Debug.Assert(BaseStream.CanSeek, "BaseStream.CanSeek");
        long position = BaseStream.Position;
        uint result = ReadUInt32();
        BaseStream.Position = position;
        CheckEndOfFileAndThrow();
        return result;
    }
    public bool CheckNextPeekIsNotEnd<T>()
    {
        return BaseStream.Position + Marshal.SizeOf<T>() < BaseStream.Length;
    }
    public int PeekInt32()
    {
        Debug.Assert(BaseStream.CanSeek, "BaseStream.CanSeek");
        long position = BaseStream.Position;
        int result = ReadInt32();
        BaseStream.Position = position;
        CheckEndOfFileAndThrow();
        return result;
    }
    public override bool ReadBoolean()
    {
        bool result = base.ReadBoolean();
        CheckEndOfFileAndThrow(1);
        return result;
    }
    private void CheckEndOfFileAndThrow(int readBytes = 0)
    {
        if (!EndOfFile)
        {
            if (readBytes > 0)
            {
                BaseStream.Seek(-readBytes, SeekOrigin.Current);
            }
            return;
        }
        if (readBytes > 0)
        {
            BaseStream.Seek(-readBytes, SeekOrigin.Current);
        }
        throw new EndOfStreamException(nameof(BaseStream));
    }
    #region Constructors
    private readonly int _endOffset;
    public SegmentReader(Stream input, int skipBytesOffset, int endOffset = 0) :
        base(input)
    {
        input.Seek(skipBytesOffset, SeekOrigin.Begin);
        _endOffset = endOffset;
    }
    public SegmentReader(Stream input, Encoding encoding) :
        base(input, encoding)
    {
    }
    public SegmentReader(Stream input, Encoding encoding, bool leaveOpen) :
        base(input, encoding, leaveOpen)
    {
    }
    #endregion
}

Далее, описал класс-контейнер, который может держать в себе Id сегмента, и список сообщений:

public class ContainerSegment<T>
{
    public ContainerSegment(T id)
    {
        Id = id;
    }
    public T Id { get; }
    public List<string> SegmentMessages { get; } = new List<string>();
    public byte[] GetBytes()
    {
        StringBuilder builder = new StringBuilder();
        builder.AppendFormat("ID={0}\n", Id);
        foreach (string segmentMessage in SegmentMessages)
        {
            builder.AppendLine(segmentMessage);
        }
        builder.AppendFormat("\n\n");
        return Encoding.Unicode.GetBytes(builder.ToString());
    }
    public override string ToString()
    {
        return $"Segment ID={Id}, Messages={SegmentMessages.Count}";
    }
}

Далее, начинаю читать данные из файла:

public static class Program
{
    private static void Main(string[] args)
    {
        const string filePath = "D:\\datas\\data12\\bin\\Table\\tb_Cinema_String.res";
        List<ContainerSegment<ushort>> cinemaSegments = new List<ContainerSegment<ushort>>();
        using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.None))
        {
            using (SegmentReader segmentReader = new SegmentReader(fileStream, 4, 32))
            {
                while(!segmentReader.EndOfFile && segmentReader.PeekUInt16() >= 10103 && segmentReader.CheckNextPeekIsNotEnd<ushort>())
                {
                    ContainerSegment<ushort> segment = new ContainerSegment<ushort>(segmentReader.ReadUInt16());
                    do
                    {
segment.SegmentMessages.Add(segmentReader.ReadString(segmentReader.ReadUInt16()));
                    } while (segmentReader.PeekUInt16() < 10103 && !segmentReader.EndOfFile && segmentReader.CheckNextPeekIsNotEnd<ushort>());
                }
            }
        }
        using (FileStream fileStream =
            File.Open(filePath.Replace(".res", ".txt"), FileMode.OpenOrCreate, FileAccess.Write))
        {
            foreach (ContainerSegment<ushort> cinemaSegment in cinemaSegments)
            {
                byte[] bufferBytes = cinemaSegment.GetBytes();
                fileStream.Write(bufferBytes, 0, bufferBytes.Length);
            }
        }
    }
}

Но, при таком подходе, по непонятным мне причинам, оба цикла, читают и последние 32 байта из файла, что в свою очередь не правильно. Последние 32 байта, не несут смысловой нагрузки в специфичную структуру файла, и служат идентификатором для одной из программ.

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

Еще добавлю, что если слелать срез данных с помощью массива байт, и MemoryStream, то никаких багов не происходит:

public static class Program
{
    private static void Main(string[] args)
    {
        const string filePath = "D:\\datas\\data12\\bin\\Table\\tb_Cinema_String.res";
        List<ContainerSegment<ushort>> cinemaSegments = new List<ContainerSegment<ushort>>();
        byte[] fileDataBytes = null;
        using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.None))
        {
            long fileDataSize = fileStream.Length - 36;
            fileDataBytes = new byte[fileDataSize];
            fileStream.Seek(4, SeekOrigin.Begin);
            fileStream.Read(fileDataBytes, 0, (int) fileDataSize);
        }
        using (MemoryStream memoryStream = new MemoryStream(fileDataBytes))
        {
            memoryStream.Position = 0;
            using (SegmentReader segmentReader = new SegmentReader(memoryStream, 0))
            {
                while(!segmentReader.EndOfFile && segmentReader.PeekUInt16() >= 10103 && segmentReader.CheckNextPeekIsNotEnd<ushort>())
                {
                    ContainerSegment<ushort> segment = new ContainerSegment<ushort>(segmentReader.ReadUInt16());
                    do
                    {
                        segment.SegmentMessages.Add(segmentReader.ReadString(segmentReader.ReadUInt16()));
                    } while (segmentReader.PeekUInt16() < 10103 && !segmentReader.EndOfFile && segmentReader.CheckNextPeekIsNotEnd<ushort>());
                    cinemaSegments.Add(segment);
                }
            }
        }
        using (FileStream fileStream =
            File.Open(filePath.Replace(".res", ".txt"), FileMode.OpenOrCreate, FileAccess.Write))
        {
            foreach (ContainerSegment<ushort> cinemaSegment in cinemaSegments)
            {
                byte[] bufferBytes = cinemaSegment.GetBytes();
                fileStream.Write(bufferBytes, 0, bufferBytes.Length);
            }
        }
    }
}
READ ALSO
Рисовать пиксель при условии

Рисовать пиксель при условии

В textbox есть строка, состоящая из 1 и 0Код должен читать строку

138
Что это за оператор =&gt; в свойстве

Что это за оператор => в свойстве

Что это за оператор => в свойстве

125
Зачем создавать экземпляр интерфейса? [дубликат]

Зачем создавать экземпляр интерфейса? [дубликат]

Зачем создавать экземпляр интерфейса?

124
Как скачать файл на сервер средствами PHP?

Как скачать файл на сервер средствами PHP?

Вопрос такой, есть вот такой сайт, на котором есть кнопка "Выгрузить в календарь"По ее нажатию высвечивается модальное окно с кнопкой "Выгрузить"

159