从实时数据流中搜索数据

2 篇文章 0 订阅

项目需要从实时单向数据流中读取和筛选数据,即当遇到标志数据时,执行某些操作。所有数据只能读一次,不能回溯。我们的场景是监听串口,然后根据监听结果,读取后续数据。

找了万能的度娘,关于数据搜索技术的,都是基于可回溯数据的,不合适。

自己写了个方法,测试了一下,效果还可以。先上代码:

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;
        }

对这个方法做了简单测试:

文件大小纯读取文件耗时搜索靠后数据耗时
100K3ms22ms
12M856ms4872ms
1000M881ms4956ms

为什么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)

以上是本篇的全部内容。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值