1. 初识EasyModbus与中文传输痛点
我在工业自动化项目里摸爬滚打十多年,Modbus协议就像吃饭喝水一样熟悉。最近接了个国内工厂的MES系统项目,需要从PLC读取中文的生产状态信息,结果踩了个大坑——EasyModbus库竟然不支持中文传输!测试时传输英文一切正常,但一发中文就乱码,甚至直接报错。
EasyModbus是个非常流行的开源库,用C#封装了Modbus TCP和RTU协议,让开发者不用从零写底层通信。但它的作者是西方人,可能没怎么处理过中文场景。库里面字符串转换默认用的ASCII编码,这玩意儿只能处理英文字符,遇到中文立马歇菜。
ASCII编码每个字符只占1个字节,而中文需要多字节编码。比如"你好"这两个字,用UTF-8编码后是6个字节(0xE4 0xBD 0xA0 0xE5 0xA5 0xBD),但ASCII编码根本没法表示。这就是问题的根源——编码方式不对,字节长度计算也错了。
2. 深入剖析编码问题本质
2.1 为什么ASCII编码处理不了中文?
ASCII编码是在1960年代制定的,那时候计算机主要在英语国家使用,所以只定义了128个字符(0-127),包括英文字母、数字和一些符号。每个ASCII字符固定占用1个字节,最高位为0。
中文汉字数量庞大,常用字就有几千个,必须用多字节编码。UTF-8是现在最通用的 Unicode 编码方式,兼容ASCII的同时支持全球所有语言。在UTF-8中,英文字符还是占1个字节,中文通常占3个字节,有些特殊字符可能占4个字节。
EasyModbus原代码是这样处理字符串的:
public static int[] ConvertStringToRegisters(string stringToConvert)
{
byte[] array = System.Text.Encoding.ASCII.GetBytes(stringToConvert);
// ... 后续处理
}
看到问题了吗?Encoding.ASCII.GetBytes()这行代码注定处理不了中文。任何中文字符都会被转换成问号(0x3F),信息就丢失了。
2.2 字节长度计算的坑
原代码还有个隐藏问题:
int[] returnarray = new int[stringToConvert.Length / 2 + stringToConvert.Length % 2];
这里用字符串的字符数(stringToConvert.Length)来计算需要的寄存器数量,对于中文来说完全不对。比如"你好"两个字符,按原代码会分配 (2/2 + 2%2) = 2个寄存器,但实际上UTF-8编码后需要6个字节,也就是3个寄存器(每个寄存器存2个字节)。
这就是为什么即使改了编码方式,如果不对字节长度做正确计算,还是会出问题。寄存器分配少了,字符串会被截断;分配多了,会读回多余的空字节。


634

被折叠的 条评论
为什么被折叠?



