处理SIM800L模块的中文短信内容

当SIM800L模块工作在文本模式(AT+CMGF=1),使用AT+CMGR=1读取的非中文短信会直接返回内容,中文短信会显示16进制值,比如:

+CMGL: 1,"REC UNREAD","10655000531001147525","","20/03/15,16:01:31+32"
30104F174FE160A054C965C56E3830115C0A656C76844F1A5458FF0C60A876849A8C8BC17801662FFF1A003100370031003900370038FF0C67096548671F003100305206949FFF0C8BF75728987597624E2D586B51996B649A8C8BC17801FF0C8FDB884C540E7EED64CD4F5C3002

// 短信内容为:【众信悠哉旅游】尊敬的会员,您的验证码是:171978,有效期10分钟,请在页面中填写此验证码,进行后续操作。

  

当AT+CSDH=1时,会返回更详细的资料:

+CMGR: <stat>,<oa>[,<alpha>],<scts>[,<tooa>,<fo>,<pid>,<dcs>,<sca>,<tosca>,<length>]<CR><LF><data>

当AT+CSDH=0时(模块默认值),只会返回:

+CMGR: <stat>,<oa><CR><LF><data>

  

STM32单片机将短信内容通过SIM800L发送到我们服务器后端时, 如果是中文短信需要将16进制值转换成中文字符串。

如何在JAVA中转换? 根据【参考资料1】中我们得知,可以用DatatypeConverter类中的parseHexBinary方法:

String input = "30104F174FE160A054C965C56E383011";
byte[] bytes = DatatypeConverter.parseHexBinary(input);
String result = new String(bytes);
System.out.println(result);

  

然后我就测试了,发现乱码。。。

查看String类的构造方法【参考资料2】发现可以输入一个字节数组和一个指定的字符集解码,然后我尝试了"UTF-8","GBK",都不行,仍然是乱码。

然后我就去查了String构造方法中的Charset参数【参考资料3】,发现Charset类中这几类标准字符集:

Charset    Description

US-ASCII	   Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the Unicode character set
ISO-8859-1  	ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1
UTF-8	    Eight-bit UCS Transformation Format
UTF-16BE	  Sixteen-bit UCS Transformation Format, big-endian byte order
UTF-16LE	  Sixteen-bit UCS Transformation Format, little-endian byte order
UTF-16	    Sixteen-bit UCS Transformation Format, byte order identified by an optional byte-order mark

  

然后就想了,怎么确定SIM800L模块返回的16进制值用的是哪种编码呢?

反推吧,我用中文字符串生成这六种编码方式的字节数组,然后将字节数组转换成字符串。

发现采用的就是 "UTF-16BE",然后我将前面的代码加入指定字符集后测试成功:

String input = "30104F174FE160A054C965C56E383011";
byte[] bytes = DatatypeConverter.parseHexBinary(input);
String result = new String(bytes,"UTF-16BE");
System.out.println(result);

  

然后我就想看看他们三是啥关系:UTF-16BE UTF-16LE UTF-16,根据【参考资料4

UTF-16BE:utf-16 big-endian 大端序,也称大尾序

UTF-16LE:utf-16 little-endian 小端序,也称小尾序

UTF-16:  基于不同的平台默认使用以上两种尾序,比如苹果Mac系统中 UTF-16 = UTF-16BE;Windows和Linux中 UTF-16 = UTF-16LE。

上两张直观的图【来源

 

看到UTF-16 和UCS-2的关系才想起来,SIM800的中文编码就是采用的UCS-2

UTF-16可看成是UCS-2的父集。在没有辅助平面字符(surrogate code points)前,UTF-16与UCS-2所指的是同一的意思。但当引入辅助平面字符后,就称为UTF-16了。现在若有软件声称自己支持UCS-2编码,那其实是暗指它不能支持在UTF-16中超过2字节的字集。对于小于0x10000的UCS码,UTF-16编码就等于UCS码。

然后我就回过头去测试了一下 "UTF-16"字符集 发现也OK

 

 

根据【这里】的资料说明,Java平台中UTF-16默认使用大端序,所以指定UTF-16字符集也可以成功解码。

 

 

 

 

参考资料1:https://www.baeldung.com/java-base64-encode-and-decode

参考资料2:https://docs.oracle.com/javase/7/docs/api/java/lang/String.html

参考资料3:https://docs.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html

参考资料4:https://zh.wikipedia.org/wiki/UTF-16

参考资料5:https://zh.wikipedia.org/wiki/%E5%AD%97%E8%8A%82%E5%BA%8F#%E5%A4%A7%E7%AB%AF%E5%BA%8F

参考资料6:https://xiaogd.net/%E7%BD%91%E9%A1%B5%E4%B8%AD%E7%9A%84%E7%BC%96%E7%A0%81%E4%B8%8E%E4%B9%B1%E7%A0%81%EF%BC%884%EF%BC%89/

posted @ 2020-03-16 22:50  1x11  阅读(1708)  评论(0编辑  收藏  举报