當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
