經常采集數據,肯定會碰到解析字符串,包括整個頁面的html,或者json以及一些不標准的json格式。。。
以前用json序列化,有時候需要實體類,有的時候沒有,比較麻煩,聽說可以用JsonMapper,解析為字典格式。不過沒用過,習慣了用最原始的方法來解析字符串,所以這里分享幾個解析的案例。也許會有點作用。
解析字符串最常用的應該是Splite和Replace了。分割,然后替換一些引號之類的。最后組合。特別是采集的時候,經常會把html頁面中某一段要提取出來,可能很多人用正則表達式,可是不會啊,也不願意去學。那只好用這些原始的方法了,時間久了,也積累一些經驗或者函數。看看幾個例子。
1.不規則非json字符串
先看看這個例子,字符串是連在一起,沒有換行的,為了方便觀察,換行了,程序是原始在一起的:
[11101630,1532,14,'0','0',3,'2015,4,23,16,05,48','4',1,2,0,0], [11101631,1532,14,'0','0',3,'2015,4,23,16,09,48','0',,,0,0], [11101632,1532,14,'0','0',3,'2015,4,23,16,03,10','1',2,2,0,0]
先來分析一下這個字符串的特點,才能找到思路哦:
1.每一組數據都是在[]括號對中,每一組數據用,號分割,所以最終要形成一個數組來訪問哦。
2.每一組的數據基本都是用 , 號分割,字符串類型還有單引號 ;
3.第7個數組是一個整體,也使用,號分割,整體是字符串有引號;
4.第2組數據有空值,直接用,號分割,所以splite的時候不能去掉空值,否則數組長度不一樣,定位就亂了。
既然分析都完了,那思路呢?
1.組直接分割使用 ], 標記,然后每一組要Repalce掉 [ 和 ] 。主要是最前和最后;
2.組內分割,使用 ,號標記分割,出來之前要把單引號給 替換掉 ;不然也是作為字符串,引號也包括進去了;
3.至於那個 數組 的處理,不能過於想復雜,分割之后,直接在最后增加1個元素,將固定位置7-12的組合起來;這樣也許方便點;
4.由於空值有占位,所以每一組的長度是固定的。所以處理的時候直接根據自己想要的位置來組合。
下面看看代碼了,C#版本,相對與一行代碼,仔細看,Linq很是一個神器,真的是神奇。。。說多了都是淚,為啥就沒早點學呢:
String str = @"[11101630,1532,14,'0','0',3,'2015,4,23,16,05,48','4',1,2,0,0],[11101631,1532,14,'0','0',3,'2015,4,23,16,09,48','0',,,0,0],[11101632,1532,14,'0','0',3,'2015,4,23,16,03,10','1',2,2,0,0]"; var result = str.Split(new string[] { "]," }, StringSplitOptions.None) //先整體分割組 .Select(n => n.Replace("[", "") //以下是組內分割,並去掉其他干擾字符 .Replace("]", "") .Replace("\'", "") .Split(',').ToList()) .Select(n => //對中間一個整體單獨提取,進行組合,單獨增加一個元素 { n.Add(String.Format("{0},{1},{2},{3},{4},{5}", n[6], n[7], n[8], n[9], n[10], n[11]));return n; }).ToList();
看看結果怎么樣:
2.鍵值對字符串分割函數
由於json數據格式都是鍵值對字符串,所以這里特意分享一個經常用到的分割函數,不用Json組件,那就用簡單的方法做一個。這個函數來源於Newlife.Core,是X組件的重要部分。源碼部分不過多解釋,就是按規則將鍵值對直接分割保持在字典中,使用方法大家可以自己實驗一下,或者參考下面的案例,都有用到這個方法。代碼如下,為了方便使用,寫成了擴展方法:
public static class StringHelper { /// <summary>拆分字符串成為名值字典</summary> /// <param name="str">要分割字符串的</param> /// <param name="nameValueSeparator">鍵值對的分隔符</param> /// <param name="separators">分割字符</param> /// <returns>鍵值對字典</returns> public static IDictionary<String, String> SplitAsDictionary(this String str, String nameValueSeparator = "=", params String[] separators) { var dic = new Dictionary<String, String>(); if (String.IsNullOrWhiteSpace(str)) return dic; if (String.IsNullOrEmpty(nameValueSeparator)) nameValueSeparator = "="; if (separators == null || separators.Length < 1) separators = new String[] { ",", ";" }; String[] ss = str.Split(separators, StringSplitOptions.RemoveEmptyEntries); if (ss == null || ss.Length < 1) return null; foreach (var item in ss) { Int32 p = item.IndexOf(nameValueSeparator); // 在前后都不行 if (p <= 0 || p >= item.Length - 1) continue; String key = item.Substring(0, p).Trim(); dic[key] = item.Substring(p + nameValueSeparator.Length).Trim(); } return dic; } }
上面默認的鍵值對分割符號為 = 號,根據實際情況進行修改,json格式里面一般是:冒號比較多。
3.復雜Json格式的字符串
上面的例子比較簡單,這次看一個稍微復雜點的,雖然可能用JsonMapper可以很輕易做到,但試一下最原始的方法吧。還是按照上面的思路,先分析字符串的特點:字符串是連在一起,沒有換行的,為了方便觀察,換行了,程序是原始在一起的:
{1074:['墨聯','墨聯','MEX D1','#098000','98'], 2100:['美乙','美乙','USL D2','#E89B10','98'], 1024:['阿甲','阿甲','ARG','#00CCFF','98'], 1052:['哥倫甲','哥倫甲','COLCMA','#888500','98'], 1028:['K聯賽','K聯賽','KORL','#F75000','98'], 1297:['球會友誼','球會友誼','CF','#5691D8','98'], 2085:['奧女甲','奧女甲','AFB','#D86220','97']}
還是先分析特點,這個格式應該是json類似的了,比較規則:
-
組與之間是使用 , 號分割;前后有{}括號對;觀察前后可以使用 ], 字符串將組分開;
-
鍵 是整數,鍵值是通過 : 號分割;
-
值是一個數組,有5個元素,通過 , 號分割
-
都有單引號,需要過濾掉;其他沒有特殊情況;
代碼解決過程:
string text = @"{1074:['墨聯','墨聯','MEX D1','#098000','98'],2100:['美乙','美乙','USL D2','#E89B10','98'],1024:['阿甲','阿甲','ARG','#00CCFF','98'],1052:['哥倫甲','哥倫甲','COLCMA','#888500','98'],1028:['K聯賽','K聯賽','KORL','#F75000','98'],1297:['球會友誼','球會友誼','CF','#5691D8','98'],2085:['奧女甲','奧女甲','AFB','#D86220','97']}"; var dic = text.Replace("\'", "").Split(new String[]{"],"}, StringSplitOptions.None) //先組分割 .Select(n => n.Replace("{", "").Replace("}", "") //將組里面的干擾字符過濾掉 .Replace("[", "").Replace("]", "") .SplitAsDictionary(":", "\"") //鍵值對處理,冒號分隔符 .ToDictionary(t => t.Key, t => t.Value.Split(',')//值再次進行分割,形成數組 )).ToArray();
看看結果如何:
4.標准的json格式
在實際的采集過,可能會碰到直接url,返回的就是json格式,比較標准。這里舉個例子,如下面這個頁面鏈接:
http://d.dacai.com/zhishu/CorrectCorp.html?matchId=1899040&matchStatusId=41
點擊開后,是這個樣子:
這個應該夠復雜了,我們分析一下,篇幅較大,源碼對照鏈接:
-
每一大組數據,是用,號分割;整體也是前后大括號對{},有4個大類,鍵分布是:IndBodans,IndEvenGoals,IndGoalss,IndHalfFulls。
-
每一組的子類中,包括的都是同一個類型的數據,每一個子數據組都有N多個小的鍵值對,如MatchId,CorpId等等,使用},可以分割
分析完成之后如何解決,思路是類似的,先整體分割,使用},進行,然后每一個之類作為鍵,對值再次進行分割。。。形成字典列表。其中的引號也是需要過濾的,至於數值類型,實際用到那個的時候,都是根據key去找,再進行Convert轉換就可以了。看看源代碼:
var dicList = doc.Split("{\"IndBodans\":[", "],\"IndEvenGoals\":[", //大類分割 "],\"IndGoalss\":[", "],\"IndHalfFulls\":[", "]}") .Select(n => n.Replace("\"", "").Split("},{")//對每個大類處理,先過濾,再分割為子類 .Select(k => k.Replace("{", "").Replace("}", "")//對子類過濾 .SplitAsDictionary(":", ","))//提取子類的鍵值對 .ToArray() ).ToList();//列表
看看解析的結果如何:
注意,有元素是空值的,可以不用管。至於那幾個鍵值對數組,最終屬於哪一個大類?雖然沒有直接標記出來,但是這個問題隨便加一個判斷就可以了,因為每一個類別的鍵名稱是不一樣的,看看是否包括對應的鍵就可以確定類別。
5.總結
雖然過程很簡單,也許有更簡單的辦好,直接和實體類映射,json反序列化更方便,但有時候沒有實體類,也挺麻煩。特別是這種變態復雜的,jsonmap直接處理不知道行不行。總之本文的目的是盡可能使用簡單原始的方法來解決問題,也是一個思路。雖然有時候我很懶,喜歡用開源組件,但有的時候一旦用上原始的了,也不喜歡改了。
字符串分析處理的過程雖然繁瑣,但基本都是比較簡單的,細致一點,都不是事。
最后把這個測試的源代碼發上來,大家別忘了點贊哦。。。