我有一个二进制日志文件,其中包含来自传感器(Int16)的流数据。
每 6 秒添加 6000 个 Int16 类型的样本,直到传感器断开连接。
我需要定期轮询这个文件,从最后读取的位置继续。a)保持文件流和二进制读取器打开并在读取之间实例化是否更好 b)每次我需要读取时实例化文件流和二进制读取器(并保留一个外部变量来跟踪最后读取的位置)c)更好的东西?
编辑:到目前为止,一些很棒的建议需要补充一点,“服务器”应用程序是由外部源供应商提供的,无法修改。
我有一个二进制日志文件,其中包含来自传感器(Int16)的流数据。
每 6 秒添加 6000 个 Int16 类型的样本,直到传感器断开连接。
我需要定期轮询这个文件,从最后读取的位置继续。a)保持文件流和二进制读取器打开并在读取之间实例化是否更好 b)每次我需要读取时实例化文件流和二进制读取器(并保留一个外部变量来跟踪最后读取的位置)c)更好的东西?
编辑:到目前为止,一些很棒的建议需要补充一点,“服务器”应用程序是由外部源供应商提供的,无法修改。
如果它总是添加相同数量的数据,那么重新打开它可能是有意义的。您可能想在打开它之前找出长度,然后向下舍入到可用的“样本集”的总数,以防万一您在它仍在写入数据时捕获它。这可能意味着您阅读的内容少于您的阅读内容(如果在您检查长度和开始阅读之间完成写入),但您下次会赶上。
您需要确保使用适当的共享选项,以便作者在您阅读时仍然可以写作。(作者可能也必须考虑到这一点。)
你可以使用MemoryMappedFiles吗?
如果可以,将文件映射到内存中并在进程之间共享它,您将能够通过每次简单地增加指针的偏移量来读取数据。
如果你将它与一个事件结合起来,你可以在你的读者可以进入阅读信息时向他发出信号。无需阻止任何内容,因为阅读器将始终读取已写入的“旧”数据。
我建议使用管道,它们就像文件一样,除了直接在应用程序之间传输数据,即使应用程序在不同的 PC 上运行(尽管这实际上只是一个选项,如果你能够更改两个应用程序)。在“System.IO.Pipes”命名空间下查看它。
PS您将为此使用“命名”管道(“c”也支持管道,因此基本上任何半体面的编程语言都应该能够实现它们)
我认为(a)是最好的,因为:
您只需要设置正确的 FileShare 标志:
举个例子:
服务器:
using(var writer = new BinaryWriter(new FileStream(@"D:\testlog.log", FileMode.Append, FileAccess.Write, FileShare.Read)))
{
int n;
while(Int32.TryParse(Console.ReadLine(), out n))
{
writer.Write(n);
writer.Flush(); // write cached bytes to file
}
}
客户:
using (var reader = new BinaryReader(new FileStream(@"D:\testlog.log", FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
{
string s;
while (Console.ReadLine() != "exit")
{
// allocate buffer for new ints
Int32[] buffer = new Int32[(reader.BaseStream.Length - reader.BaseStream.Position) / sizeof(Int32)];
Console.WriteLine("Stream length: {0}", reader.BaseStream.Length);
Console.Write("Ints read: ");
for (int i = 0; i < buffer.Length; i++)
{
buffer[i] = reader.ReadInt32();
Console.Write((i == 0 ? "" : ", ") + buffer[i].ToString());
}
Console.WriteLine();
}
}
您还可以将数据流式传输到数据库而不是文件作为另一种选择,那么您不必担心文件锁定。
但是如果您坚持使用文件方法,您可能希望在每次从中读取数据时关闭文件;这在很大程度上取决于写入文件的过程有多复杂,以及它是否可以检测到文件锁定操作并做出适当的响应而不会严重崩溃。