之前用python(2.7版本)開發(fā)公司網(wǎng)絡(luò)協(xié)議的解析工具,直接從網(wǎng)絡(luò)抓包pcap文件中解析出協(xié)議交換流程.
公司協(xié)議中二進(jìn)制和Json兩種格式類型共存.針對二進(jìn)制內(nèi)容九杂,python的struct這庫用起來還是蠻順手的.針對Json協(xié)議部分,首選就是本文的Json庫.對于Json庫的詳細(xì)介紹,可以網(wǎng)上搜一把,資料還是很全的.(
這里討論的是json庫的loads方法.由于struct庫獲取過來的數(shù)據(jù),都是以pythonh中的字符串類型的保存的,所以我采用json的loads方法(load是從文件中加載).于是乎, 問題就出現(xiàn)了, 我使用時loads拋出了一個ValueError異常.查看下程序堆棧信息,可以發(fā)現(xiàn)loads方法的調(diào)用流程是:
loads()->_default_decoder.decode(s)
其中decode的庫源代碼如下
def decode(self, s, _w=WHITESPACE.match):
"""Return the Python representation of ``s`` (a ``str`` or ``unicode``
instance containing a JSON document)
"""
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
end = _w(s, end).end()
if end != len(s):
raise ValueError(errmsg("Extra data", s, end, len(s)))
return obj
其中WHITESPACE是一個正則表示式對象,其定義代碼如下:
WHITESPACE = re.compile(r'[ \t\n\r]*', FLAGS)
就是匹配空白字符的一個正則表達(dá)式對象,所以
idx=_w(s, 0).end()
就是把字符串前面的空白字符都跳過了soga.
PS:正則表示式對象的match方法的第二個入?yún)⑧胰Γ砥ヅ涞钠鹗嘉恢?br> 然后self.raw_decode完成了字符串內(nèi)容的解析(相關(guān)代碼可以自己研究哦),返回了一個obj對象(這貨就是我們需要的)和一個end( json格式中‘}’的位置+1,end返回一個開區(qū)間的結(jié)束位置) ,end現(xiàn)在代表json處理了的字符串的長度
end = _w(s, end).end()
_w這老兄又把end加上了json字符串后面空白字符串的長度(如果有的話).
主題來了:
if end != len(s):
raise ValueError(errmsg("Extra data", s, end, len(s)))
如果json處理的字符串長度和我們給的長度不一致,那就game over,拋出一個ValueError異常了.什么情況下會導(dǎo)致長度不統(tǒng)一呢,根據(jù)json庫的算法,只要你在后面的空白字符再跟上其他非空白字符,那就能得到夢寐以求的ValueError.
由于我要解析的json字符串,后面都會帶上c字符結(jié)尾符0,所以這就是真相.
我的解決方法是,對到手的json字符串先預(yù)處理下:
strJson = "".join([ string.strip().rsplit("}" , 1)[0] , "}"] )
主要就是用rsplit函數(shù)切掉最右邊“}”后面的字符串.
PS:我是感覺end!=len(s)這個判斷不是很好,不過stackoverflow上面一些家伙的解釋,是json庫不支持解釋"{}{}"這種類型的json字符串,從這方法考慮,倒也說的通.