C#如何筛选出自己想要的数据

在判断头帧以后 下位机发过的数据 偶尔会发过来9个字节的数据 如果我按照10个字节的数据进行截取 后面就截取不到对的数据,我该如何把中间9个字节的数据 筛选掉 只要10个字节的数据 如果有思路请直接贴代码并注释上 注释,如果按照需求可以正常运行 我会采纳

img

img

    private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            // 检查是否允许跨线程调用控件,这里为了简化代码直接禁用了该功能
            CheckForIllegalCrossThreadCalls = false;
            // 读取所有可用字节数
            int bytesToRead = serialPort1.BytesToRead;
            //用于存储接收到的数据
            byte[] receiveBuffer = new byte[bytesToRead];
            //从串口读取数据
            serialPort1.Read(receiveBuffer, 0, bytesToRead);
            buffer.AddRange(receiveBuffer);
            //将接收到的数据转换为字符串格式
            if (buffer.Count >0 && buffer[0] == 0x68)
            {   
                //如果缓冲区的长度大于等于8,则表示已经接收到完整的数据帧
                if (buffer.Count >= 10)
                {   //截取需要的10个字节数据
                    byte[] data = buffer.GetRange(0,10).ToArray();
                    //将接收到的数据转换为字符串格式
                    receivedString = BitConverter.ToString(data).Replace("-", "");
                    textBox1.AppendText(receivedString);
                    //将字符串追加至文本框中
                    string timeStr = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                    using (StreamWriter sw = new StreamWriter(@"C:\Users\guoxiaoru\Desktop\ACVB.txt", true))
                    {
                        sw.WriteLine(timeStr + "   " + receivedString, "\n");
                        sw.Close();
                    }
                    //清空缓冲区
                    buffer.Clear();
                }
            }
            //清空缓冲区
            else { buffer.Clear(); }
        }

参考gpt和自己的思路,在判断头帧以后,可以先判断缓冲区的长度是否为9,如果是,则将缓冲区清空;如果不是,则按照10个字节的数据进行截取。可以参考如下代码:


private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// 检查是否允许跨线程调用控件,这里为了简化代码直接禁用了该功能
CheckForIllegalCrossThreadCalls = false;
// 读取所有可用字节数
int bytesToRead = serialPort1.BytesToRead;
//用于存储接收到的数据
byte[] receiveBuffer = new byte[bytesToRead];
//从串口读取数据
serialPort1.Read(receiveBuffer, 0, bytesToRead);
buffer.AddRange(receiveBuffer);
//将接收到的数据转换为字符串格式
if (buffer.Count > 0 && buffer[0] == 0x68)
{
//如果缓冲区的长度等于9,则表示中间有9个字节的无用数据
if (buffer.Count == 9)
{
//清空缓冲区
buffer.Clear();
}
//如果缓冲区的长度大于等于10,则表示已经接收到完整的数据帧
else if (buffer.Count >= 10)
{ //截取需要的10个字节数据
byte[] data = buffer.GetRange(0,10).ToArray();
//将接收到的数据转换为字符串格式
receivedString = BitConverter.ToString(data).Replace("-", "");
textBox1.AppendText(receivedString);
//将字符串追加至文本框中
string timeStr = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
using (StreamWriter sw = new StreamWriter(@"C:\Users\guoxiaoru\Desktop\ACVB.txt", true))
{
sw.WriteLine(timeStr + " " + receivedString, "\n");
sw.Close();
}
//清空缓冲区
buffer.Clear();
}
}
//清空缓冲区
else { buffer.Clear(); }
}

参考GPT和自己的思路,首先,你需要判断接收到的数据是否满足你的要求,即长度是否为10个字节。如果不满足,你可以忽略这些数据。这可以使用以下代码来实现:

if (data.Length != 10) {
    // 数据长度不为10,忽略此数据
    return;
}

// 数据长度为10,进行后续处理
// TODO: 处理数据

如果你需要在数据中截取特定的位置,你可以使用 Array.Copy() 方法。以下是一个例子,它从数组 data 的第二个字节开始复制八个字节,并将结果存储在 buffer 数组中:

byte[] buffer = new byte[8];
Array.Copy(data, 1, buffer, 0, 8);

在这个例子中,data 是接收到的数据数组,1 是要开始复制的索引位置,buffer 是用来存储结果的数组,0 是存储结果的数组中的起始索引位置,8 是要复制的字节数。你可以根据你的需要调整这些值。

如果你需要在每个数据帧中跳过中间的9个字节,你可以使用一个循环来接收数据,并检查每个数据帧的长度。以下是一个示例代码:

while (true) {
    // 接收数据帧
    byte[] data = ReceiveData();

    // 检查数据帧长度
    if (data.Length != 10) {
        // 数据长度不为10,忽略此数据
        continue;
    }

    // 处理数据帧
    // TODO: 处理数据帧
}

在这个例子中,ReceiveData() 方法用来接收数据帧。如果数据帧长度不为10,则跳过此数据帧并继续等待下一个数据帧。如果数据帧长度为10,则进行处理。你可以根据你的需要进行调整。

该回答引用ChatGPT
这里介绍LINQ+Lambda表达式进行数据筛选的方式

这里是第一种方式,还是使用了if条件语句去判断,根据选择的条件去筛选出我所需要的数据

 public GxAnaly SelectDay(string ID, DateTime Day)
        {
            try
            {
                DBEntities db = new DBEntities();
                var temp = (from pt in db.Tb_Analysis
                           where pt.ID == Sn
                           select pt).ToList();
               // return temp.Any() ? LoadFromModel(temp.First()) : null;
                if (temp.Count == )
                {
                    return null;
                }
                else
                {
                    List<Analy> gxDetail = new List<Analy>();
                    List<Analy> Detail = new List<Analy>();
                    temp.ForEach(e => gxDetail.Add(LoadFromModel(e)));
                    foreach (Analy Detail in gxDetail)
                    {
                        if (Detail.OtTime.Day == Day.Day&&Detail.OtTime.Month==Day.Month&&Detail.OtTime.Year==Day.Year)
                        {
                            Detail.Add(Detail);
                        }
                    }
                    if (Detail.Count == )
                    {
                        return null;
                    }
                    else
                    {
                        return Detail.FirstOrDefault();
                    }
                }
            }
            catch (Exception ex)
            {
                return null;
                Logger.Error(ex.Message + ex.StackTrace);
            }
        }

第二种方式,直接用Lambda自带的筛选属性

using(var db=new MyDbContext())
 
{
 
var s= db.Students.ToList().First(s=>s.ID=);
 
}
 
//这里写的是进行全表查询,不建议使用

//根据相应的条件进行查询,不会去查询全表,特别在数据量庞大的时候,建议加个判断
using(var db=new MyDbContext())
 
{
 
var list =db.Students.AsQueryable();
 
if(********){list=list.Where(s=>s.ID=);}
 
if(******){list=list.Where(...)}
 
}

下面展示一个多个Lambda表达式进行表操作的方法

    public static class PredicateBuilder
    {
 
        public static Expression<Func<T, bool>> True<T>() { return f => true; }
        public static Expression<Func<T, bool>> False<T>() { return f => false; }
        public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
        {
            // build parameter map (from parameters of second to parameters of first)
            var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f);
 
            // replace parameters in the second lambda expression with parameters from the first
            var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
 
            // apply composition of lambda expression bodies to parameters from the first expression
            return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
        }
 
        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
        {
            return first.Compose(second, Expression.And);
        }

完美的动态拼接Lambda表达式如下:

using (var db = new MyDbContext())
            {
                var predicate = PredicateBuilder.True<Student>();
                predicate=predicate.And(s => s.ID > );
                predicate=predicate.Or(s => s.ID < );
                var result = db.Students.Where(predicate).ToList();
            }

下面是一种多Lambda表达式操作的一个范例,这里部分引用来自https://blog.csdn.net/leewhoee/article/details/8968023%EF%BC%8C%E8%87%AA%E5%B7%B1%E6%87%92%E5%BE%97%E5%8E%BB%E6%95%B2%E4%BA%86%EF%BC%8C%E9%A1%BA%E6%89%8B%E5%B0%B1%E5%80%9F%E6%9D%A5%E7%94%A8%E4%BA%86

using (var db = new SHTrackerDbContext())
            {
 
                var predicate = PredicateBuilder.True<Course>();
                settings = DecorateSettings(settings);
 
                Expression<Func<Course, bool>> checkCourse = c => db.Students.Any(s => s.CourseID == c.ID);
                if (!string.IsNullOrEmpty(settings.Quater_Year))
                {
 
                    checkCourse =
                        c => db.Students.Any(s => s.CourseID == c.ID && db.Student2CBOs.Any(
                            s2c => s2c.StudentID == s.ID && s2c.Quater_Year.Equals(settings.Quater_Year)));
                }
                if (settings.QuaterYearArray != null)
                {
                    checkCourse =
                        c => db.Students.Any(s => s.CourseID == c.ID && db.Student2CBOs.Any(
                            s2c =>
                            s2c.StudentID == s.ID && settings.QuaterYearArray.Any(qy => qy.Equals(s2c.Quater_Year))));
                }
 
                if (!string.IsNullOrEmpty(settings.DPU_ID))
                {
                    checkCourse =
                        checkCourse.And(
                            c => db.Students.Any(s => s.CourseID == c.ID && s.DPU_ID.Equals(settings.DPU_ID)));
                }
                predicate = predicate.And(checkCourse);
 
                if (settings.IsCheckInstructorName)
                {
                    predicate = predicate.And(c => c.InstructorName.Equals(settings.InstructorName));
                }
                if (!string.IsNullOrEmpty(settings.Term))
                {
                    predicate = predicate.And(c => c.TermDescription.Equals(settings.Term));
                }
                if (settings.TermArray != null)
                {
                    predicate = predicate.And(c => settings.TermArray.Any(t => t.Equals(c.TermDescription)));
                }
                if (settings.CourseType != CourseType.All)
                {
                    predicate = predicate.And(c => c.Type == (int) settings.CourseType);
                }
                var cc =
                    new CourseCollection(
                        db.Courses.AsNoTracking()
                          .Where(predicate)
                          .OrderByDescending(m => m.ID)
                          .Skip((pageIndex - )*pageSize)
                          .Take(pageSize)
                          .ToList(),
                        db.Courses.AsNoTracking().Where(predicate).Count())
                        {
                            PageIndex = pageIndex,
                            PageSize = pageSize,
                            Settings = DecorateSettings(settings)
                        };
 
                return cc;
            }

这个用linq中对数组进行 skip 和 take 操作,再 ToArray()

哥哥在你的代码中,当接收到数据后,你首先检查了数据帧头是否正确,然后判断缓冲区中接收到的数据长度是否大于等于10。如果是这种情况,你就从缓冲区中提取十个字节的数据,并将其转换为字符串格式。如果缓冲区中接收到的数据长度小于10,则不进行任何处理并清空缓冲区。

现在假设你想要过滤掉那些长度为9字节的数据,并且只处理长度为10字节的数据。可以在读取完整个缓冲区之前,对缓冲区中的数据进行分析,然后只提取长度为10字节的数据。

以下是修改后的代码示例:

private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    // 检查是否允许跨线程调用控件,这里为了简化代码直接禁用了该功能
    CheckForIllegalCrossThreadCalls = false;
    // 读取所有可用字节数
    int bytesToRead = serialPort1.BytesToRead;
    //用于存储接收到的数据
    byte[] receiveBuffer = new byte[bytesToRead];
    //从串口读取数据
    serialPort1.Read(receiveBuffer, 0, bytesToRead);
    buffer.AddRange(receiveBuffer);

    // 如果缓冲区的长度小于10,则等待下一个数据包
    if (buffer.Count < 10) return;

    // 找到数据包的起始位置
    int startIndex = buffer.IndexOf(0x68);

    // 如果在缓冲区中没有找到数据包起始位置,则清空缓冲区
    if (startIndex == -1)
    {
        buffer.Clear();
        return;
    }

    // 如果数据包起始位置不在缓冲区的开头,则删除该起始位置之前的所有数据
    if (startIndex != 0)
    {
        buffer.RemoveRange(0, startIndex);
    }

    // 如果缓冲区的长度小于10,则等待下一个数据包
    if (buffer.Count < 10) return;

    // 如果已经接收到完整的数据包,则提取该数据包并处理
    byte[] data = buffer.GetRange(0, 10).ToArray();

    // 检查数据包长度是否为10,如果不是,则清除数据包并等待下一个数据包
    if (data.Length != 10)
    {
        buffer.Clear();
        return;
    }

    // 将接收到的数据转换为字符串格式
    receivedString = BitConverter.ToString(data).Replace("-", "");
    textBox1.AppendText(receivedString);

    //将字符串追加至文本框中
    string timeStr = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
    using (StreamWriter sw = new StreamWriter(@"C:\Users\guoxiaoru\Desktop\ACVB.txt", true))
    {
        sw.WriteLine(timeStr + "   " + receivedString, "\n");
        sw.Close();
    }

    // 清空缓冲区
    buffer.Clear();
}

这个代码做了以下这几个事:

  1. 在缓冲区中寻找起始位置并删除起始位置之前的所有数据。
  2. 提取长度为10字节的数据包并检查数据包长度是否为10字节。
  3. 将有效的数据转换为字符串并输出。

哥哥请看看

可以使用一个循环,每次读取10个字节,如果读取到9个字节就再读取一次,直到读取到10个字节为止。
示例代码:

data = b''  # 存储接收到的数据
while True:
    # 读取10个字节
    chunk = ser.read(10)
    # 如果读取到9个字节
    if len(chunk) == 9:
        # 再读取一次
        chunk += ser.read(1)
    # 如果读取到10个字节
    if len(chunk) == 10:
        data += chunk
        # 处理数据
        process_data(data)
        # 清空数据
        data = b''

在这个示例代码中,ser 是一个串口对象,process_data() 是处理数据的函数。每次读取到10个字节的数据后,将数据添加到 data 变量中,并调用 process_data() 函数处理数据。处理完数据后,清空 data 变量,等待下一次读取数据。如果读取到9个字节的数据,就再读取一次,直到读取到10个字节为止。

问题描述:
在接收下位机发过来的数据时,偶尔会出现9个字节的数据,而按照10个字节的数据进行截取会导致后面的数据截取不到。如何筛选掉中间的9个字节数据,只保留10个字节的数据。
解决方案:
1. 判断数据帧头
首先,在接收数据时需要判断数据帧头,只有当数据帧头为0x68时才进行后续操作。
if (buffer.Count > 0 && buffer[0] == 0x68)
{
// ...
}
2. 判断数据长度
接着,需要判断接收到的数据长度是否为10个字节,如果不是则需要继续等待数据的到来。
if (buffer.Count >= 10)
{
// ...
}
else
{
return;
}
3. 筛选数据
如果接收到的数据长度为10个字节,则需要筛选掉中间的9个字节数据,只保留前面和后面的数据。
byte[] data = buffer.ToArray();
byte[] newData = new byte[10];
Array.Copy(data, 0, newData, 0, 6);
Array.Copy(data, 9, newData, 6, 4);
4. 转换数据格式
最后,将筛选后的数据转换为字符串格式,并进行后续操作。
receivedString = BitConverter.ToString(newData).Replace("-", "");
textBox1.AppendText(receivedString);
完整代码:
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// 检查是否允许跨线程调用控件,这里为了简化代码直接禁用了该功能
CheckForIllegalCrossThreadCalls = false;
// 读取所有可用字节数
int bytesToRead = serialPort1.BytesToRead;
//用于存储接收到的数据
byte[] receiveBuffer = new byte[bytesToRead];
//从串口读取数据
serialPort1.Read(receiveBuffer, 0, bytesToRead);
buffer.AddRange(receiveBuffer);
//将接收到的数据转换为字符串格式
if (buffer.Count > 0 && buffer[0] == 0x68)
{
//如果缓冲区的长度大于等于10,则表示已经接收到完整的数据帧
if (buffer.Count >= 10)
{
//筛选需要的10个字节数据
byte[] data = buffer.ToArray();
byte[] newData = new byte[10];
Array.Copy(data, 0, newData, 0, 6);
Array.Copy(data, 9, newData, 6, 4);
//将接收到的数据转换为字符串格式
receivedString = BitConverter.ToString(newData).Replace("-", "");
textBox1.AppendText(receivedString);
//将字符串追加至文本框中
string timeStr = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
using (StreamWriter sw = new StreamWriter(@"C:\\Users\\guoxiaoru\\Desktop\\ACVB.txt", true))
{
sw.WriteLine(timeStr + " " + receivedString, "\
");
sw.Close();
}
//清空缓冲区
buffer.Clear();
}
}
//清空缓冲区
else { buffer.Clear(); }
}

你这一点一点的问,真不如找个人直接远程把你的需求问清楚了,一块弄出来