项目需要从实时单向数据流中读取和筛选数据,即当遇到标志数据时,执行某些操作。所有数据只能读一次,不能回溯。我们的场景是监听串口,然后根据监听结果,读取后续数据。
找了万能的度娘,关于数据搜索技术的,都是基于可回溯数据的,不合适。
自己写了个方法,测试了一下,效果还可以。先上代码:
public static bool Search(System.IO.Stream stream, byte[] findBytes, int timeout)
{
var bytesLength = findBytes.Length;
var bytesLastIndex = bytesLength - 1;
var buff = new byte[bytesLength];
var pos = 0;
var matched = false;
var expired = false;
var expires = DateTime.Now.AddMilliseconds(timeout);
var timer = new System.Timers.Timer(10);
timer.Elapsed += (sender, e) =>
{
expired = DateTime.Now > expires;
};
timer.Start();
while (!expired)
{
// 从数据流中读取一个字符
byte b = 0;
try
{
var n = stream.ReadByte();
// 若没有读到新数据,则继续尝试,直到超时
if (n < 0) continue;
else b = (byte)n;
}
catch
{
continue;
}
// 把字符写入缓冲区的最后面
if (pos < bytesLength)
{
buff[pos++] = b;
}
else
{
// 如果缓冲区已满,则把缓冲区左移一个字符
Array.Copy(buff, 1, buff, 0, bytesLastIndex);
buff[bytesLastIndex] = b;
}
// 从后往前比较缓冲区的字符和目标的字符
var matchCount = 0;
for (var i = bytesLastIndex; i >= 0; i--)
{
if (buff[i] == findBytes[i]) matchCount++;
else break;
}
// 找到则退出
if (matchCount == bytesLength)
{
matched = true;
break;
}
}
timer.Stop();
timer.Close();
return matched;
}
代码逻辑说明:
1) 循环从数据流中读取一个个字节(若读取不到,可能是因为数据流中暂时没有数据,继续尝试,直到超时);
2)把字节加入缓冲区,缓冲区和待匹配数据一样大。当缓冲区满时,左移一个字节;
3)比较待匹配数据和缓冲区;
对于非实时数据流,则不用超时处理,使用一下代码:
public static bool Search(System.IO.Stream stream, byte[] findBytes)
{
var bytesLength = findBytes.Length;
var bytesLastIndex = bytesLength - 1;
var buff = new byte[bytesLength];
var pos = 0;
var matched = false;
while (true)
{
// 从数据流中读取一个字符
byte b = 0;
try
{
var n = stream.ReadByte();
if (n < 0) break;
else b = (byte)n;
}
catch
{
break;
}
// 把字符写入缓冲区的最后面
if (pos < bytesLength)
{
buff[pos++] = b;
}
else
{
// 如果缓冲区已满,则把缓冲区左移一个字符
Array.Copy(buff, 1, buff, 0, bytesLastIndex);
buff[bytesLastIndex] = b;
}
// 从后往前比较缓冲区的字符和目标的字符
var matchCount = 0;
for (var i = bytesLastIndex; i >= 0; i--)
{
if (buff[i] == findBytes[i]) matchCount++;
else break;
}
// 找到则退出
if (matchCount == bytesLength)
{
matched = true;
break;
}
}
return matched;
}
对这个方法做了简单测试:
文件大小 | 纯读取文件耗时 | 搜索靠后数据耗时 |
100K | 3ms | 22ms |
12M | 856ms | 4872ms |
1000M | 881ms | 4956ms |
为什么12M和1000M的文件的测试结果那么接近,没去考察。
记录一个小技巧:
第一个代码里处理超时,一开始我用的是:
var expires = DateTime.Now.AddMilliseconds(timeout);
while (DateTime.Now < expires)
然后测试的时候发现, while (DateTime.Now < expires)这行语句要消耗代码运行时间的 95% 以上。然后改成了下面这个样子(可能不是最优化的):
var expired = false;
var expires = DateTime.Now.AddMilliseconds(timeout);
var timer = new System.Timers.Timer(10);
timer.Elapsed += (sender, e) =>
{
expired = DateTime.Now > expires;
};
timer.Start();
while (!expired)
以上是本篇的全部内容。