Для упрощения считывания данных из бинарного файла, унаследовал класс 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);
}
}
}
}
Виртуальный выделенный сервер (VDS) становится отличным выбором
Вопрос такой, есть вот такой сайт, на котором есть кнопка "Выгрузить в календарь"По ее нажатию высвечивается модальное окно с кнопкой "Выгрузить"