C#Windows表单-解析从串行端口接收的字符串

时间:2018-12-30 16:07:12

标签: c# winforms split serial-port string-parsing

我正在使用C#开发Windows窗体应用程序,在其中我从串行端口接收数据,现在我有以下代码(这只是解决我的问题的相关代码):

private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)     
{
      ReceivedData = serialPort1.ReadExisting();
      this.Invoke(new EventHandler(interp_string));       
}

private void interp_string(object sender, EventArgs e)      
{
      textReceive.Text += ReceivedData + "\n";
}

但是现在我需要将接收到的数据解析为小字符串。 ReceivedData变量是具有以下格式的多个字符串的组合:“值时间\ n”,其中值从0到1024,时间以秒为单位(并且始终在增加),并具有4个小数位。我需要将ReceivedData变量拆分为单独的值,这是将其绘制在图表中的相应时间。考虑到使用ReadExisting,可能会发生一个字符串仅被部分读取而其余字符串仅在下次触发DataReceived事件时读取的情况,但是我不介意是否丢失了一点数据,并不重要。

我已经尝试使用ReadLine代替ReadExisting,我设法拆分每个字符串并绘制数据,但是,鉴于该应用程序正在接收大量数据,因此每1毫秒一个字符串,该应用程序无法跟上并即使已经过了10秒钟,应用程序仍然从第二秒开始打印数据,并且我按了一个按钮以停止接收数据,但该应用程序长时间保持打印值,我认为这是存储在接收缓冲区中的值。更改为ReadExisting是我发现的唯一实时读取和打印所有内容的方法。

2 个答案:

答案 0 :(得分:0)

您始终可以在String类型中使用正则表达式,而不必单独分割每一行。 Regex .NETMatches method

让我们假设您拥有以下截获的数据:

1250 154873210
1250 15487556574
1250 15487444
1250 154871111
1250 154875454524545444
1250 154873210
1250 15487556574
1250 15487444
1250 154871111
1250 154875454524545444
1250 154873210
1250 15487556574
1250 15487444
1250 154871111
1250 154875454524545444
1250 154873210
1250 15487556574
1250 15487444
1250 154871111
89877 154875454524545444
001 154873210
877 15487556574
15647 15487444
540 154871111
12 154875454524545444

使用正则表达式,可以很容易地获得配对的“值/时间”(如上面的链接中所述)。

所以您的常规将是:

(?'Value'\d+)\s*(?'Time'\d+\.\d+)

Demo

C#示例:

        string ReceivedData = "1221 1111.1111\n1221 1111.1111";
        MatchCollection matches = Regex.Matches(ReceivedData, @"(?'Value'\d+)\s*(?'Time'\d+\.\d+)");
        Console.WriteLine("Matches found : "+matches.Count);
        foreach(Match Pairs in matches)
        {

            Console.WriteLine(String.Format("Match : Value -> {0} , Time -> {1}", Pairs.Groups["Value"], Pairs.Groups["Time"]));
        }

答案 1 :(得分:0)

DataReceived事件可能会在字符串中间触发,您可能还没有收到整个消息。您需要知道要查找的内容,通常是换行(LF)或回车(CR)。我使用StringBuilder来构建字符串,下面是一个示例,其中LF表示我收到了整个消息。我将字符附加到字符串中,直到我知道完整的消息为止。我快速清除了缓冲区,因为在评估时可以输入下一个字符串。对于这个简单的示例,我只是调用一个函数来评估我的字符串,但是您可能要在这里使用委托。

StringBuilder sb = new StringBuilder();
char LF = (char)10;

private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
    string Data = serialPort1.ReadExisting();

    foreach (char c in Data)
    {
        if (c == LF)
        {
            sb.Append(c);

            CurrentLine = sb.ToString();
            sb.Clear();

            //do something with your response 'CurrentLine'
            Eval_String(CurrentLine);
        }
        else
        {
            sb.Append(c);
        }
    }
}

一旦您收到完整的消息,您就可以根据需要对其进行评估。我不确定您的数据是什么样子,但是在我的示例中,我的消息以逗号分隔返回,因此我可以使用逗号作为分隔符将字符串拆分为字符串数组,并从消息中获取每个值。

public void Eval_String(string s)
{
    string[] eachParam;
    eachParam = s.Split(',');

    if (eachParam.Length == 5)
    {
        //do something
        Console.WriteLine(eachParam[2]);
    }
}

编辑:这是一个有关如何使用队列更新gui的示例。

StringBuilder sb = new StringBuilder();
char LF = (char)10;
Queue<string> q = new Queue<string>();

private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
    string Data = serialPort1.ReadExisting();

    foreach (char c in Data)
    {
        if (c == LF)
        {
            sb.Append(c);

            CurrentLine = sb.ToString();
            sb.Clear();

            q.Enqueue(currentLine);
        }
        else
        {
            sb.Append(c);
        }
    }
}

private void backgroundWorkerQ_DoWork(object sender, DoWorkEventArgs e)
{
    while (true)
    {
        if (backgroundWorkerQ.CancellationPending)
            break;

        if (q.Count > 0)
        {
            richTextBoxTerminal.AppendText(q.Dequeue());
        }

        Thread.Sleep(10);
    }
}

如果您从未与后台工作者一起工作过,则只需创建一个新的backgroundworker对象,将supportsCancellation设置为true,然后为其DoWork事件添加一个事件。然后,当您启动表单时,可以将其告知.RunWorkerAsync()

if (!backgroundWorkerQ.IsBusy)
{
    backgroundWorkerQ.RunWorkerAsync();
}

请参阅:https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.backgroundworker?view=netframework-4.7.2 还要注意,backgroundworker在它自己的线程中,您很可能需要BeginInvoke来更新您的文本框。