【问题标题】:C# Windows form - parsing string received from serial portC# Windows 窗体 - 解析从串口接收到的字符串
【发布时间】:2019-05-27 11:57:47
【问题描述】:

我正在使用 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 变量是具有以下格式的多个字符串的组合:“value time \n”,其中 value 从 0 到 1024,时间以秒为单位(并且总是增加),并且有 4 个小数位。我需要将 ReceivedData 变量拆分为单独的值,并在相应的时间将其绘制在图表中。考虑到使用 ReadExisting,可能会发生一个字符串仅被部分读取,其余的只会在下次触发 DataReceived 事件时读取,但我不介意丢失一点数据,并不重要。

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

【问题讨论】:

  • 你走错了方向;在“每 1 毫秒一个字符串”尝试在 WinForms 文本字段中显示字符串是无用的,因为没有人可以读取它。您只需要存储您感兴趣的数据,并且只显示用户需要阅读的内容并丢弃其余的内容。
  • @DourHighArch 最终目标不是显示字符串并读取它,否则我不需要拆分字符串并识别值项及其对应的时间。我出于调试目的打印它。正如我在原始帖子中所说,最终目标是将它们绘制在图表中。

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


【解决方案1】:

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

如果您从未使用过后台工作者,则只需创建一个新的后台工作者对象,将 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 它来更新您的文本框。

【讨论】:

  • 感谢您的帮助,在您的示例中,我设法分离了这些值,问题是我需要在主线程中将这些值绘制在图表中,并且一旦我调用 EventHandler 它减慢一切,这与我在原帖最后一段中解释的问题相同。
  • 小修正,它不是调用一个减慢它的 EventHandler 是与 RichTextBox 或图表或类似的东西交互。如果我只是在控制台(在事件处理程序内)写它工作正常
  • 取决于您的数据输入的速度,通常在Eval_String 中,我将使用BeginInvoke 更新我的GUI,它工作正常。但是,如果您有大量数据快速通过,您可能需要使用Queue。我会创建一个支持取消的backgroundworker,并且有一段时间为真,它会检查队列中的任何内容并对其进行更新。我会用一个例子来编辑我的帖子。
【解决方案2】:

您始终可以在字符串类型中使用正则表达式,而不是单独拆分每一行。 Regex .NET , Matches 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"]));
        }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-22
    • 1970-01-01
    • 1970-01-01
    • 2015-10-17
    • 1970-01-01
    • 2015-03-23
    相关资源
    最近更新 更多