“startIndex 不能大于字符串长度。Arg_ParamName_Name”
下面是程序,主要是substring截取的开始字符超范围
private void Form1_Load(object sender, EventArgs e)
{
try
{
string[] str = SerialPort.GetPortNames(); //获取连接到电脑的串口号并存进数组
if (str.Length > 0)
{
serialPort1.PortName = "com5";
serialPort1.BaudRate = 9600;
serialPort1.DataBits = 8;
serialPort1.Parity = Parity.None;
serialPort1.StopBits =StopBits.Two;
serialPort1.Encoding = Encoding.BigEndianUnicode;
serialPort1.Open();
}
else
{
MessageBox.Show("当前无串口连接!");
}
}
catch
{
MessageBox.Show("无串口设备!/r/n请检查是否连接设备!/r/n请检查设备驱动!");
}
}
//向低阻仪发送
private void timer1_Tick(object sender, EventArgs e)
{
char c1 = (char)259; //01H 03H
char c2 = (char)0001; //00H 01H
char c3 = (char)0014; //00H 0EH
char c4 = (char)38350; //95H CEH
string OutData = c1.ToString() + c2.ToString() + c3.ToString() + c4.ToString();
serialPort1.Write(OutData);
}
//触发事件,读取低阻仪返回数据
List<byte> sp_buffer = new List<byte>(4096); //串口缓存区
int sp_buffer_max = 4096; //串口缓存区最大缓存字节数
byte[] data = new byte[9192]; //用来存放缓冲区的数据流
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
int Byte_len = serialPort1.BytesToRead; //读取缓存的数据长度
byte[] Rc_byte = new byte[Byte_len];
serialPort1.Read(Rc_byte, 0, Byte_len); //将缓存数据存储进字节数组里面
if (sp_buffer.Count > sp_buffer_max) //缓存超过字节数 先丢弃前面的字节
sp_buffer.RemoveRange(0, sp_buffer_max); //丢弃前面的字节0到sp_buffer_max
sp_buffer.AddRange(Rc_byte); //存入缓存区
if (sp_buffer.Count > 4)
{
sp_buffer.CopyTo(0, data, 0, sp_buffer.Count);
this.Invoke(new EventHandler(DisplayText));
}
}
string data3;
string data4;
string data5;
string data6;
string data7;
private void DisplayText(object sender,EventArgs e)
{
short data2;
data7 = data.ToString();
char[] data1 = data7.ToCharArray();
for(int i=0;i<data1 .Length;i++)
{
data2 = (short)data1[i];
data3 = Convert.ToString(data2, 16);
}
data3 = data3.Substring(14, 2) + data3.Substring(12,2) + data3.Substring(10,2) + data3.Substring(8,2);
data4 = data3.Substring(28, 2) + data3.Substring(26,2) + data3.Substring(24,2) + data3.Substring(22,2);
data5 = data3.Substring(42, 2) + data3.Substring(40,2) + data3.Substring(38,2) + data3.Substring(36,2);
data6 = data3.Substring(56, 2) + data3.Substring(54,2) + data3.Substring(52,2) + data3.Substring(50,2);
textBox1.Text = DataChenge(data3).ToString();
textBox2.Text = DataChenge(data4).ToString();
textBox3.Text = DataChenge(data5).ToString();
textBox4.Text = DataChenge(data6).ToString();
}
private float DataChenge(string s)
{
MatchCollection matches = Regex.Matches(s, @"[0-9A-Fa-f]{2}");
byte[] bytes = new byte[matches.Count];
for (int i = 0; i < bytes.Length; i++)
bytes[i] = byte.Parse(matches[i].Value, System.Globalization.NumberStyles.AllowHexSpecifier);
float m = BitConverter.ToSingle(bytes.Reverse().ToArray(), 0);
return m;
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
}
private void textBox3_TextChanged(object sender, EventArgs e)
{
}
private void textBox4_TextChanged(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
serialPort1.Close();
Close();
}
private void Form1_FormClosing(object sender,FormClosingEventArgs e)
{
if (serialPort1.IsOpen) serialPort1.Close();
}
}
}
下面是我用串口助手回读的信息,红框内是需要转成十进制小数显示的数据
还有一个问题,我怎么能够不要前两个接收的数据,只要第3个接收数据?
你可以先检查字符串的长度是否足够,再调用Substring。比如:
if (data3.Length >= 16)
data3 = data3.Substring(14, 2) + data3.Substring(12,2) + data3.Substring(10,2) + data3.Substring(8,2);
if (data4.Length >= 32)
data4 = data4.Substring(28, 2) + data4.Substring(26,2) + data4.Substring(24,2) + data4.Substring(22,2);
// 同样对data5和data6也做如此处理
第二个问题是只想获取第三个接收的数据,可以通过设置一个计数器来实现。你可以在类内部设置一个private int类型的变量,每次接收到数据时递增,当该变量值为3时,处理并显示数据,否则不处理。例如:
private int receiveCount = 0; // 定义类内部变量
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
// 处理数据的代码...
// 判断是不是第三次接收数据
receiveCount++;
if (receiveCount == 3)
{
// 处理并显示数据
}
}
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
public partial class Form1 : Form
{
private int receiveCount = 0; // 添加计数器
private List<byte> sp_buffer = new List<byte>(4096); //串口缓存区
private int sp_buffer_max = 4096; //串口缓存区最大缓存字节数
private byte[] data = new byte[9192]; //用来存放缓冲区的数据流
private void Form1_Load(object sender, EventArgs e)
{
// ...
}
private void timer1_Tick(object sender, EventArgs e)
{
// ...
}
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// ...
// 判断是不是第三次接收数据
receiveCount++;
if (receiveCount == 3)
{
// 处理并显示数据
if (sp_buffer.Count > 4)
{
sp_buffer.CopyTo(0, data, 0, sp_buffer.Count);
this.Invoke(new EventHandler(DisplayText));
}
}
}
private void DisplayText(object sender, EventArgs e)
{
short data2;
string data7 = data.ToString();
char[] data1 = data7.ToCharArray();
string data3 = "", data4 = "", data5 = "", data6 = "";
for (int i = 0; i < data1.Length; i++)
{
data2 = (short)data1[i];
string hexData = Convert.ToString(data2, 16);
if (hexData.Length >= 16)
{
data3 = hexData.Substring(14, 2) + hexData.Substring(12, 2) + hexData.Substring(10, 2) + hexData.Substring(8, 2);
}
if (hexData.Length >= 32)
{
data4 = hexData.Substring(28, 2) + hexData.Substring(26, 2) + hexData.Substring(24, 2) + hexData.Substring(22, 2);
}
if (hexData.Length >= 48)
{
data5 = hexData.Substring(42, 2) + hexData.Substring(40, 2) + hexData.Substring(38, 2) + hexData.Substring(36, 2);
}
if (hexData.Length >= 64)
{
data6 = hexData.Substring(56, 2) + hexData.Substring(54, 2) + hexData.Substring(52, 2) + hexData.Substring(50, 2);
}
}
textBox1.Text = DataChenge(data3).ToString();
textBox2.Text = DataChenge(data4).ToString();
textBox3.Text = DataChenge(data5).ToString();
textBox4.Text = DataChenge(data6).ToString();
}
private float DataChenge(string s)
{
// ...
}
// ...
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (serialPort1.IsOpen) serialPort1.Close();
}
}
关于第一个问题,可以在 substring 前添加一个判断,如果开始截取的索引大于字符串长度,就将其修改为字符串长度减一。修改后的代码如下:
if (data3.Length > 56)
{
data3 = data3.Substring(Math.Min(56, data3.Length - 1), 2) +
data3.Substring(Math.Min(54, data3.Length - 1), 2) +
data3.Substring(Math.Min(52, data3.Length - 1), 2) +
data3.Substring(Math.Min(50, data3.Length - 1), 2);
}
关于第二个问题,可以在 if (sp_buffer.Count > 4) 判断语句中,将 sp_buffer 数组复制到 data 数组中,并从 data 中获取第三个数据进行处理,代码如下:
if (sp_buffer.Count > 4)
{
sp_buffer.CopyTo(0, data, 0, sp_buffer.Count);
int offset = 0;
while (offset < sp_buffer.Count - 4)
{
if (data[offset] == 0x01 && data[offset + 1] == 0x03)
{
byte len = data[offset + 2];
if (offset + 4 + len <= sp_buffer.Count)
{
byte[] buffer = data.Skip(offset + 3).Take(len + 1).ToArray();
string hexData = BitConverter.ToString(buffer);
textBox5.Text = hexData;
float value = DataChange(hexData.Substring(4, 8));
textBox6.Text = value.ToString();
break;
}
}
offset++;
}
}
其中,offset 表示当前处理到的位置,从 0 开始循环,每次循环判断当前位置的数据是否符合要求,若符合,则取出该数据进行处理并退出循环。具体的实现方式可以自行修改。
private void DisplayText(object sender, EventArgs e)
{
short data2;
data7 = Encoding.ASCII.GetString(data, 0, sp_buffer.Count); // 将字节数组转换为字符串
if (data7.Length >= 58)
{
data3 = data7.Substring(14, 8); // 提取第一个接收数据
data4 = data7.Substring(28, 8); // 提取第二个接收数据
data5 = data7.Substring(42, 8); // 提取第三个接收数据
data6 = data7.Substring(56, 8); // 提取第四个接收数据
textBox1.Text = DataChenge(data3).ToString();
textBox2.Text = DataChenge(data4).ToString();
textBox3.Text = DataChenge(data5).ToString();
textBox4.Text = DataChenge(data6).ToString();
}
}
private float DataChenge(string s)
{
MatchCollection matches = Regex.Matches(s, @"[0-9A-Fa-f]{2}");
byte[] bytes = new byte[matches.Count];
for (int i = 0; i < bytes.Length; i++)
bytes[i] = byte.Parse(matches[i].Value, System.Globalization.NumberStyles.AllowHexSpecifier);
float m = BitConverter.ToSingle(bytes.Reverse().ToArray(), 0);
return m;
}
关于“startIndex 不能大于字符串长度。Arg_ParamName_Name”的错误,是由于在使用Substring()方法的时候截取的位置超出了字符串的长度。可以在调用Substring()方法之前,先用if语句判断一下字符串是否足够长。
关于只要第三个接收数据的问题,您可以在serialPort1_DataReceived()方法中,在获取到完整的第三个数据后,就不再处理后面的数据了。可以设置一个计数器,每次接收到数据就加1,当计数器等于3时,就不再处理后面的数据了。
以下是修改后的代码片段:
int count = 0; //计数器
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
int Byte_len = serialPort1.BytesToRead;
byte[] Rc_byte = new byte[Byte_len];
serialPort1.Read(Rc_byte, 0, Byte_len);
if (sp_buffer.Count > sp_buffer_max)
sp_buffer.RemoveRange(0, sp_buffer_max);
sp_buffer.AddRange(Rc_byte);
if (sp_buffer.Count > 4)
{
sp_buffer.CopyTo(0, data, 0, sp_buffer.Count);
count++; //计数器加1
if(count == 3) //只处理第三个数据
{
this.Invoke(new EventHandler(DisplayText));
return; //不再处理后面的数据
}
}
}
古老的问题,因为串口为流式传输,所以他没有固定长度
所以任何试图使用固定长度判定方式的代码都会必然产生该类问题
比如 if(buffer[0]==0xff)----------------------buffer[0]未必每次都是0xff
比如题主的 data3.Substring(Math.Min(56, data3.Length - 1), 2) , data未必每次都会那么长
所以请正确理解流,水流
假设你有3杯水(红,绿,蓝)依次倒入一个水管,然后你在另一端打开水龙头来看,你看到了什么??
你看到的时连续不断的水,而不是3杯水,如果你不停的开关龙头(从缓冲区接收数据),你又会看到什么?
你会看到的是 一段一段的水流,只有第一次你能保证 buffer[0]==0xff是对的,其他时间都是巧合
同样道理我们不能保证“龙头只开闭了3次并且龙头开闭的时间正好是3杯水交界“
所以你需要做的第一步是另外在放个“颜色判定”,自己去判定颜色的交界(封包协议边界)
你把你的读到数据后处理方法改进一下,不要转字符串再操作,直接操作二进制byte[],示例:
static void ReadDataSample()
{
byte[] bytes = { 0x01, 0x03, 0x1C, 0x01, 0x58, 0x2F, 0x00, 0x42, 0x6D, 0x50, 0x02, 0x00, 0x3C, 0x1C, 0x46, 0x55, 0x46, 0x03, 0x4A, 0x4D, 0xD1, 0x41, 0x6D, 0x50, 0x04, 0xD8 };
MemoryStream stream = new MemoryStream(bytes);
BinaryReader reader = new BinaryReader(stream);
reader.ReadBytes(4); //跳过4个字节
float val1 = reader.ReadSingle(); //读一个float(4字节)
reader.ReadBytes(3); //跳过3个字节
float val2 = reader.ReadSingle(); //读一个float(4字节)
reader.ReadBytes(3); //跳过3个字节
float val3 = reader.ReadSingle(); //读一个float(4字节)
Console.WriteLine(val1);
Console.WriteLine(val2);
Console.WriteLine(val3);
/* 输出结果:
32.04623
9999
26.16274
*/
}
首先呢看你有两个问题:
1:只需要检查startIndex是否超出substring(startIndex, endIndex)字符串的长度,如果超出则不执行substring操作。
例子:
data3 = data3.Substring(14, 2);
// 这里检查14是否超出data3的长度,如果没有才执行Substring
if (14 < data3.Length) {
data3 = data3.Substring(14, 2);
}
2:定义一个计数变量count,初始化为0。
每当接收到数据并处理的时候,count++。
如果count < 3,则不显示在TextBox中,只有当count == 3时才显示。
count继续++,下次接收到数据时继续判断,只有count为3的倍数时才显示,以此达到丢弃前两个数据的效果。
例子:
int count = 0;
private void DisplayText(object sender,EventArgs e)
{
...
// 判断count是否为3的倍数,只有是才显示在TextBox中
if (count % 3 == 0) {
textBox1.Text = DataChenge(data3).ToString();
...
}
count++; // count继续++
}