1. 程式人生 > >Lua字串模式和捕獲 (轉)

Lua字串模式和捕獲 (轉)

已經拿Lua用了快兩年的時間了,但是每次用到字串的模式匹配的時候就總要去翻看Lua官網的說明,網上也沒有一個比較詳細的說明,也有好多朋友都向我詢問這塊的內容,其實這塊的難點有三:

  • 一個是對Lua的正則表示式不熟悉;
  • 另一個是對Lua中string庫提供的幾個函式的用法不熟悉;
  • 還有一點是Lua的string庫提出了一個新的概念,叫做捕獲,其實也不算什麼新概念,只是和函式呼叫雜糅在一起行為就不好理解罷了。

這裡我總結一下。

先從Lua內建string庫提供的幾個大家不熟悉的函式開始(基於Lua5.1,Lua5.2基本沒有變化)。

Lua內建字串庫用到模式的地方有4個函式,它們分別是:

  • string.find()

  • string.match()

  • string.gmatch()

  • string.gsub()

1、string.find(s, pattern, start, plain)

這個函式的功能是查詢字串 s 中的指定的模式 pattern。

如果找到了一個模式的匹配,就返回找到的模式在 s 中的起點和終點;否則返回 nil。這裡需要注意的是,它只會返回找到的第一個匹配的位置,所以找到了的返回值其實是兩個數字,匹配的起點、匹配的終點。 第三個引數是個數字,它是可選的,start 指定在 s 中查詢開始的位置,預設是 1,start可以是負數,-1 代表從最後一個字元開始,-2 代表倒數第二個字元開始。當然,最後都是到最後一個字元結束,所以如果你指定位置從最後一個字元開始,那麼就只會查詢這一個字元。
第四個引數是個 bool 值,它指明第二個引數 pattern 中是否使用特殊字元,如果第四個引數指明為 true,那麼就意味著第二個引數 pattern 中的那些特殊字元(這些字元有 ^$*+?.([%- ,定義在Lua原始碼 lstrlib.c 中)都被當作正常字元進行處理,也就是一個簡單的字串匹配,而不過所謂的模式匹配,也就是不動用正則表示式的匹配。相反,false 就意味著 pattern 採用特殊字元處理。這樣說也不太明瞭,舉個例子就明白了,不過要涉及到一個Lua模式中特殊的字元,如果這裡還是不明白,看了後面我關於Lua正則表示式的介紹應該就能明白。 比如:
1 2 3 local s = "am+df" print(string.find(s, "m+", 1, false))    -- 2    2 print(string.find(s, "m+", 1, true))    -- 2    3

其中字元 + 在 Lua 正則表示式中的意思是匹配在它之前的那個字元一次或者多次,也就是說 m+ 在正則表示式裡會去匹配 m, mm, mmm ……。所以當 string.find 第四個引數為 false 的時候,就只能在字串 s 中找到 m 這個字母是匹配的,那麼返回的結果就是 2    2。

而當第四個引數為 true 的時候, + 被當作正常字元,那麼查詢的匹配就是 m+ 這個字串,那麼找到的位置就是 2     3。
如果你不傳第四個引數,就跟 false 是一個意思。 上面把 find 函式做了一個簡單的介紹,但是這個函式的行為並非總是這樣,為什麼呢?這就是我文章開頭提到的Lua的捕獲也會被雜糅到這些string的庫函式裡。 沒有辦法,只得先介紹一下所謂的捕獲是個什麼概念。 上面 find 函式的第二個引數我們都明白是一個模式,可以理解為一般的正則匹配中的正則表示式,而Lua為這個模式增加了一個新的功能,也就是所謂的捕獲,在一個模式串中,我們可以用小括號()來標明一些我們想要儲存的匹配,而這個小括號中的內容依然是模式串,也就是說我們只不過是把模式中一些我們想要的特殊字元保留下來供後面使用。比如上面那個例子中的模式串是 m+ ,如果我想要把跟 m+ 匹配的字串捕獲出來,也就是儲存下來,我可以用一個小括號把它括起來,而 find 函式除了上面說到的行為外,也就是除了返回查詢到 pattern 的起止位置外,還會返回所有要求捕獲的字串,像這樣:
1 2 local s = "am+df" print(string.find(s, "(m+)", 1, false))    -- 2    2    m

如果你想要捕獲更多的內容,只需要用小括號把它括起來就好了,比如這樣:

1 2 3 local s = "am+df" print(string.find(s, "((m+))", 1, false))    -- 2    2    m    m print(string.find(s, "(((m+)))", 1, false))    -- 2    2    m    m    m

關於捕獲還有一點需要說明的,就是捕獲只會在模式能夠匹配成功的時候才會跟著 string 的函式進行返回,比如下面這個,我想捕獲字母 a ,但事實上這個模式根本無法匹配到,所以肯定是無法返回的:

1 2 local s = "am+df" print(string.find(s, "(m+)(a)", 1, false))    -- nil

另外捕獲返回的順序,是依照左小括號的位置來定的,比如上面那個捕獲了3個 m 的例子,第一個 m 其實是最外層的小括號捕獲到的。為什麼要提到捕獲的順序呢?因為我們可以使用 %n 來取得第n個捕獲的字串,至於取得對應的捕獲有什麼用處呢?這個在後面會介紹到。

一個空的捕獲,也就是小括號裡面什麼內容也沒有,它會返回當前字串的比較操作進行到的位置,比如
1 2 local s = ”am+df“ print(string.find(s, "()(m+)()", 1, false))    -- 2    2    2    m    3

有一點也必須要提一下,就是在Lua5.1的原始碼當中,捕獲字串的數量是有限制的,預設是32個,也就是說你新增的小括號不能無限加,最多加32個。如果捕獲超過限制,當然會報錯了,比如:

1 2 local s = ”am+df“ print(string.find(s, "()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()", 1, false))    -- 捕獲33個
當然你可以通過修改Lua的原始碼來調整你想要儲存的捕獲數量,這個數量定義在 luaconf.h 檔案中: wKioL1NFO2Tg2jXMAAB3lm-QgaY459.jpg

一般來說,對於使用,分析基本到此了,但是對於 Lua,因為原始碼簡單,而且優美,又是拿C語言寫的,心癢難耐,必須要了解一下原始碼才解恨。

Lua內建庫的載入方式就不說了,在各個大神的文章裡都可以看到,我們直接來看 string.find() 這個函式,函式在 lstrlib.c 檔案裡:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 static int str_find (lua_State *L) { return str_find_aux(L, 1); } static int str_find_aux (lua_State *L, int find) { size_t l1, l2; const char *s = luaL_checklstring(L, 1, &l1); const char *p = luaL_checklstring(L, 2, &l2); ptrdiff_t init = posrelat(luaL_optinteger(L, 3, 1), l1) - 1; if (init < 0) init = 0; else if ((size_t)(init) > l1) init = (ptrdiff_t)l1; if (find && (lua_toboolean(L, 4) ||  /* explicit request? */ strpbrk(p, SPECIALS) == NULL)) {  /* or no special characters? */ /* do a plain search */ const char *s2 = lmemfind(s+init, l1-init, p, l2); if (s2) { lua_pushinteger(L, s2-s+1); lua_pushinteger(L, s2-s+l2); return 2; } } else { MatchState ms; int anchor = (*p == '^') ? (p++, 1) : 0; const char *s1=s+init; ms.L = L; ms.src_init = s; ms.src_end = s+l1; do { const char *res; ms.level = 0; if ((res=match(&ms, s1, p)) != NULL) { if (find) { lua_pushinteger(L, s1-s+1);  /* start */ lua_pushinteger(L, res-s);   /* end */ return push_captures(&ms, NULL, 0) + 2; } else return push_captures(&ms, s1, res); } } while (s1++ < ms.src_end && !anchor); } lua_pushnil(L);  /* not found */ return 1; }

這個函式初步看起來還是比較長的,但是仔細分析一下就發現其實是很簡單的。前面那 6 行,就是接收前 3 個引數罷了,只不過處理了一下那個查詢起始點引數,防止了超出字串長度。最關鍵的地方就是緊接著的 if else 邏輯,find 是傳進來的引數,對於 string.find 來說就是1,所以不用管它,認為它一直是真就 OK 了,既然提到這裡了,那麼是不是還有別的地方也會呼叫這個函式原型的,bingo!我們搜尋一下就會發現,其實 string.match() 函式其實也是呼叫這個函式原型的,而它的 find 引數就是傳遞的 0 。哈哈,難道 string.match 函式其實跟 string.find 函式是一樣的?

1 2 3 4 5 6 static int str_match (lua_State *L) { return str_find_aux(L, 0); } static int str_find (lua_State *L) { return str_find_aux(L, 1); }

這個留到介紹 string.match 函式的時候再說。拉回來,繼續談這個 if else 邏輯,if 的判斷條件其實就是看你呼叫 string.find 的第四個引數,如果第四個引數傳遞了 true,也就是我上面說的,不使用特殊字元模式,或者是模式中壓根就沒有特殊字元,那個 SPECIALS 巨集同樣定義在這個檔案中:

wKioL1NFN3SgNZLiAAAx3EuAlz8482.jpg

如果沒有這些字元或者是不對這些字元特殊處理,那麼就是一個簡單的字串匹配,呼叫 lmemfind() 函式,如果找到了,就返回了匹配到的起止位置。 既然如此,那麼 else 裡就好理解了,它就是使用特殊字元進行匹配的處理,這裡的關鍵函式是 match(),它處理字串和模式進行匹配,並進行了捕獲,這個留到介紹模式的時候再接著說。最後如果匹配到了,那麼仍然返回匹配起止點,注意,這裡多了一個操作,就是把捕獲到的字串也壓入了棧。所以我們呼叫並捕獲的時候才會有後面那些捕獲的字串。

懷著一點小激動,我點開了 lmemfind() :

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 static const char *lmemfind (const char *s1, size_t l1, const char *s2, size_t l2) { if (l2 == 0) return s1;  /* empty strings are everywhere */ else if (l2 > l1) return NULL;  /* avoids a negative `l1' */ else { const char *init;  /* to search for a `*s2' inside `s1' */ l2--;  /* 1st char will be checked by `memchr' */ l1 = l1-l2;  /* `s2' cannot be found after that */ while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { init++;   /* 1st char is already checked */ if (memcmp(init, s2+1, l2) == 0) return init-1; else /* correct `l1' and `s1' to try again */ l1 -= init-s1; s1 = init; } } return NULL;  /* not found */ } }

總的來說,這個比較的方法還是中規中矩的,從頭開始查詢匹配串的第一個字元,只不過用的是 memchr 函式,找到了之後用 memcmp 函式來比較兩個字串是否是相同的,如果不相同就跳過檢查了的字元繼續。相比那些複雜的字串匹配演算法,這個既簡單又可愛,贊一個:),memcmp 函式的執行自然比 str 系列的比較要快一些,因為不用一直檢查 ‘\0’ 字元,關於 memcmp 函式的做法,這裡有一篇文章,雖然是說他的優化,但是看他的程式碼也能大致瞭解 memcmp 的做法:

2、string.match(s, pattern, start) 這個函式的功能是在字串 s 中查詢指定的模式 pattern,並返回模式 pattern 中指定的第一個捕獲。 第三個引數指明查詢的起始位置,預設為1。 相比 string.find 函式來說,string.match 要簡單的多了,它不需要你再選擇是否採用特殊字元,它必須要採用。pattern 中的內容跟 string.find 一樣,都是一個Lua的模式,跟 string.find 不同的地方在於,它返回的不在是匹配到的起止位置,而是返回 pattern 中指定的第一個捕獲,如果 pattern 中沒有指明捕獲,那麼它會返回整個 pattern 的匹配結果,當然,沒有匹配到依然返回 nil。

在介紹 string.find 函式的時候提到過,Lua 原始碼中 string.match 呼叫的函式其實跟 string.find 呼叫的函式是相同的,都是 str_find_aux(lua_State *L, int find) 函式,唯一不同的地方在於 string.match 呼叫時 find 引數傳遞的是 0,這樣就不會進入 str_find_aux() 裡簡單匹配的分支,直接進行模式匹配。

3、string.gmatch(s, pattern) 上面介紹的兩個函式,無論是 string.find 還是 string.match 函式,都是發現和模式相匹配的串後就停下來了,返回對應的內容,而經常我們會有在一個字串中找到所有跟模式相匹配的串的需求,string.gmatch() 函式就能夠滿足這個需求。 string.gmatch() 函式可以被當作迭代器進行呼叫,然後獲得所有跟模式相匹配的串,比如Lua官網給出的例子:
1 2 3 4 5 6 7 8 9 10 s = "hello world from Lua" for w in string.gmatch(s, "%a+") do print(w) end --[[ hello world from Lua ]]

至於 %a+ 的意義嘛,在 string.find() 的介紹裡提到過字元 + 的用法,至於 %a 嘛,它是匹配所有的字母。這裡需要注意的是,字串 s 裡由 4 個單詞,用 3 個空格進行了分隔,所以呼叫一次 string.gmatch(s, "%a+"),只會匹配 s 中的第一個單詞,因為遇到空格匹配就失敗了。

string.gmatch() 函式的用法基本也就是在迴圈裡當作迭代器用了,我還真沒發現有別的用法。 一個唯一需要注意的地方,就是特殊字元 ^ 在 string.gmatch 函式中的用法跟別處是不同的,在其他的函式中,^ 的用法是放在一個模式的前面,那麼這個模式必須從字串的最開頭開始匹配,如果匹配失敗則不會繼續去和後面的字元匹配了,什麼意思呢,用前面的例子:
1 2 3 local s = "am+df" print(string.find(s, "(m+)", 1, false))    -- 2    2    m print(string.find(s, "^(m+)", 1, false))    -- nil

第二個匹配,因為在模式前面增加了 ^ ,所以會從字串 s 的最開始就進行匹配,也就是從字母 a 開始匹配,a 當然無法和 (m+) 匹配成功了,所以直接就返回 nil 了。這個處理在上面講 string.find 的時候原始碼函式 str_find_aux() 裡 else 分支模式匹配裡可以看到,有專門處理 ^ 字元的程式碼。

那麼在 string.gmatch 函式中,如果把 ^ 放在模式的前面的意思什麼呢?它的意思是不進行匹配了,也就是直接返回,注意不是返回 nil,而是函式直接返回,棧上沒有任何返回值。 老樣子,雖然可以想象得到 string.gmatch() 的實現應該跟上面的差不多,但還是看一眼原始碼比較保險:

前面那兩行是返回Lua迭代器所需求的狀態和迭代函式,不用去管它。讓我們來看一下 gmatch_aux (lua_State *L) 函式,刨去為了迭代器做處理之後,就和 string.match() 函式實現沒有什麼區別了,最後都呼叫 match() 函式進行模式匹配。不同的地方就是上面說的字元 ^ 的處理這裡是沒有的。

4、string.gsub(s, pattern, rep, n) 這個函式跟 string.gmatch() 一樣,也帶一個 g,可以想象得到,這個函式也會獲得所有的匹配字串,而不像 string.find() 和 string.match() 一樣,碰到一個就結束了。確實是這樣。這個函式的作用是在字串 s 中查詢與模式 pattern 相匹配的所有串,然後用 rep 引數產生的字串進行替換,你可能要問,為什麼是 rep 產生的串,而不是 rep 自己呢?因為這裡的 rep 除了可以是一個字串外,還可以是一個函式,甚至可以是一個table。 當 rep 是一個字串的時候,一般來說是當作一個普通的字串來處理,也就是直接用這個字串來替換匹配到的串,但是這裡會特殊的處理符號 %% 後面接數字 1-9 的時候,也就是用前面模式捕獲的序號 1-9 對應的字串來替換模式匹配的內容,這樣說比較繞,還是看例子:
1 2 3 4 local s = "am+dmf" print(string.gsub(s, "()(m+)", "%1"))     -- a2+d5f     2 print(string.gsub(s, "()(m+)", "%2"))      -- am+dmf     2 print(string.gsub(s, "()(m+)", "%3"))    -- error: invalid capture index
上面我們用的模式是 ()(m+),這個模式會有2個捕獲,分別是字串當前的位置以及 m+,string.gsub() 匹配到的第一個地方是 am+dmf,這個時候兩個捕獲分別是 2 ,m,那麼 %1 也就是第一個捕獲,也就是 2,替換後的串為 a2+dmf,接著又匹配到第二個地方 am+dmf,這裡的兩個捕獲分別是 5,m,那麼 %1 指向的第一個捕獲是 5,替換後的串為 a2+d5f,這就是結果顯示的內容。後面那個數字 2 的意思是替換成功了2次。根據上面的分析就不難理解,為什麼用 %2 去替換的時候字串沒有變,因為本來就是用 m 去替換 m,當然不變。另外,第三個 print() 會報錯,因為只有2個捕獲,而你要去使用 %3 ,那麼自然就沒有這個捕獲了。 這裡可能還需要注意的地方就是 % 只會和後面緊接著的數字結合,換句話說為什麼前面要說是 1-9 就是這個原因,雖然捕獲可以預設達到之前說的 32 個,但是隻能用前 9 個了。有一個比較特殊的是 %0,它是用匹配到的串去替換,簡單來說就是重複匹配到的串,比如這樣:
1 2 local s = "am+dmf" print(string.gsub(s, "()(m+)", "%0%0%0"))    -- ammm+dmmmf    2

匹配到的串是 m,用 mmm 替換了原串中的 m

你可能要問,既然 % 被單獨處理了,那麼我想要用 % 去替換怎麼辦,只需要用 %% 就可以表示 % 自身了。比如:

1 2 local s = "am+dmf" print(string.gsub(s, "()(m+)", "%%"))    -- a%+d%f    2

當rep是一個table的時候,每次匹配到了之後,都會用第一個捕獲作為key去查詢這個table,然後用table的內容來替換匹配串,如果沒有指定捕獲,那麼,就用整個匹配串作為key去查詢,如果沒有查到對應key的值,或者對應的值不是字串和數字,那麼就不做替換:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 local s = "am+dmf" local t1 = { [2] = "hh", [5] = "xx", } local t2 = {} print(string.gsub(s, "()(m+)", t1))     -- ahh+dxxf     2 print(string.gsub(s, "()(m+)", t2))    -- am+dmf    2 local t3 = { [2] = false } print(string.gsub(s, "()(m+)", t3))    -- am+dmf    2 local t4 = { [2] = { 123 } } print(string.gsub(s, "()(m+)", t4))    -- error : invalid replacement value ( a table )

當rep是一個函式的時候,每當匹配到字串的時候,就把模式所有的捕獲按照捕獲順序作為引數傳遞給這個函式,如果沒有指定捕獲,則傳遞整個匹配的字串給函式,函式的返回值如果是字串或者是數字就替換掉匹配,如果不是則不做替換:

1 2 3 4 5 6 7 8 9 10 local s = "am+dmf" function f1(...) print(...)     -- 2     m    -- 5     m return "hh" end function f2() return { 123 } end print(string.gsub(s, "()(m+)", f1))     -- ahh+dhhf     2 print(string.gsub(s, "()(m+)", f2))    -- error : invalid replacement value ( a table )

第四個引數,用來表明,需要替換到第幾個匹配為止,比如:

1 2 3 4 5 6 local s = "am+dmf" print(string.gsub(s, "()(m+)", "%%", -1))     -- am+dmf     0 print(string.gsub(s, "()(m+)", "%%", 0))      -- am+dmf     0 print(string.gsub(s, "()(m+)", "%%", 1))      -- a%+dmf     1 print(string.gsub(s, "()(m+)", "%%", 2))      -- a%+d%f     2 print(string.gsub(s, "()(m+)", "%%", 3))    -- a%+d%f    2

依然來看看原始碼是怎麼寫的:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 static int str_gsub (lua_State *L) {

相關推薦

Lua字串模式捕獲

已經拿Lua用了快兩年的時間了,但是每次用到字串的模式匹配的時候就總要去翻看Lua官網的說明,網上也沒有一個比較詳細的說明,也有好多朋友都向我詢問這塊的內容,其實這塊的難點有三: 一個是對Lua的正則表示式不熟悉; 另一個是對Lua中string庫提供的幾個函式的用法不熟悉; 還有一點是Lua

淺析Lua中table的遍歷刪除

當我在工作中使用lua進行開發時,發現在lua中有4種方式遍歷一個table,當然,從本質上來說其實都一樣,只是形式不同,這四種方式分別是: forkey, value in pairs(tbtest) do   XXX  endforkey, value in ipairs(tbtest) do 

block,inlineinline-block概念區別

line eight wan pan 排列 isp 我們 .com 是個 總體概念 block和inline這兩個概念是簡略的說法,完整確切的說應該是 block-level elements (塊級元素) 和 inline elements (內聯元素)。block元素

python中xrangerange

log item .py 對象 nbsp net range all file 說到序列,我們第一想到的是一組有序元素組成的集合。同時,每個元素都有唯一的下標作為索引。 在Python中,有許多內界的序列。包括元組tuple,列表list,字符串str等。上面提到的序列

如何查找Linux中一些特殊數據類型定義,比如pid_tuid_t

分享 文件 技術分享 定義 log 相關 uid linux源碼 pid 1. 查看man手冊,找到pid_t,可以通過getpid函數來看 2. 打開sys/types.h 3. 打開bits/types.h 4.

黃聰:濃縮的才是精華:淺析GIF格式圖片的存儲壓縮

meid 單獨 圖片分辨率 change 之前 dex 本質 0.11 blog http://www.cnblogs.com/qcloud1001/p/6647080.html 成文迪, 在Web前端摸爬滾打的碼農一枚,對技術充滿熱情的菜鳥,致力為手Q的建設添磚加瓦

最小二乘法最大似然估計的聯系區別

enc bsp 聯系 角度 tro span nbsp sdn .science 對於最小二乘法,當從模型總體隨機抽取n組樣本觀測值後,最合理的參數估計量應該使得模型能最好地擬合樣本數據,也就是估計值和觀測值之差的平方和最小。而對於最大似然法,當從模型總體隨機抽取n組樣本觀

貝葉斯vs頻率派:武功到底哪家強?| 說人話的統計學·協

定義 這一 tps cbc 出發 上一條 習慣 做出 而已 回我們初次見識了統計學理論中的“獨孤九劍”——貝葉斯統計學(戳這裏回顧),它的起源便是大名鼎鼎的貝葉斯定理。 整個貝葉斯統計學的精髓可以用貝葉斯定理這一條式子來概括: 我們做數據分析,絕大多數情況下希望得到的是關於

jira安裝配置

com 文章 講解 art setup 軟件安裝包 使用 基於 tran Jira 安裝和配置 Jira 安裝和配置 Jira 安裝 Jira 配置 資料 Jira 安裝 Jira 安裝 官網:https://www.atlassian.com/softw

jenkins安裝配置

quest tac 軟件安裝包 setup and all pro 地址 新版 轉載來自:http://wiki.jikexueyuan.com/project/linux-in-eye-of-java/Jenkins-Install-And-Settings.html

集成學習算法總結----BoostingBagging

原理 過程 訓練 嚴重 oos 機器學習 ppr 次數 error 1、集成學習概述 1.1 集成學習概述 集成學習在機器學習算法中具有較高的準去率,不足之處就是模型的訓練過程可能比較復雜,效率不是很高。目前接觸較多的集成學習主要有2種:基於Boosting的和基於Bagg

C#中的newoverride

方法 too 找到 ring 父類 類繼承 衍生 virtual func  在衍生類中的方法上使用new和override關鍵字有何意義,可以通過一系列問題來找到答案。先看一段代碼: 1 class Program 2 { 3 s

Spartan6芯片配置模式詳解

roc gen bit png 寬度 con 讀取配置 3.2 多個 1. 配置概述  Spartan6系列FPGA通過把應用程序數據導入芯片內部存儲器完成芯片的配置。Spart-6 FPGA可以自己從外部非易失性存儲器導入編程數據,或者通過外界的微處理器、DSP等對其進行

C#可擴展編程之MEF學習筆記:導出類的方法屬性

學習 說了 如何 mod ati dem ont num imp 前面說完了導入和導出的幾種方法,如果大家細心的話會註意到前面我們導出的都是類,那麽方法和屬性能不能導出呢???答案是肯定的,下面就來說下MEF是如何導出方法和屬性的。   還是前面的代碼,第二篇中已經提供了下

設計模式六大原則

method 過多 這樣的 不同 提高 依賴倒置 同心圓 23種設計模式 變化 設計模式六大原則(1):單一職責原則 定義:不要存在多於一個導致類變更的原因。通俗的說,即一個類只負責一項職責。 問題由來:類T負責兩個不同的職責:職責P1,職責P2。當由於職責P1需求發

通俗易懂理解TCPUDP

建立 休息 如果 str 否則 無連接 一分鐘 tis 得到 知乎看到一位大牛“車小胖”的類比很貼切,就轉過來了。 原文鏈接:https://www.zhihu.com/question/51388497 或者:https://daily.zhihu.com/story/9

C語言 二維數組指針動態分配釋放

i++ 進制 numbers 很多 print 算術 uil 換算 som C 二維數組(指針)動態分配和釋放 先明確下概念: 所謂32位處理器就是一次只能處理32位,也就是4個字節的數據,而64位處理器一次就能處理64位,即8個字節的數據。如果我們將總長128位的指令分別

hashCode() equals() 區別作用

person set集合 static out fin 解決 詳細 返回 art 出處:https://www.jianshu.com/p/5a7f5f786b75 本章的內容主要解決下面幾個問題: 1 equals() 的作用是什麽? 2 equal

關於VO、DTO、DO、PO的概念、區別用處

次數 博文 舉例 exce 時序 能夠 帥哥美女 一次 明顯 概念: VO(View Object):視圖對象,用於展示層,它的作用是把某個指定頁面(或組件)的所有數據封裝起來。 DTO(Data Transfer Object):數據傳輸對象,這個概念來源於J2EE的設計

Dubbo 攔截器監聽器

今天要聊一個可能被其他dubbo原始碼研究的童鞋容易忽略的話題:Filter和Listener。我們先來看一下這兩個概念的官方手冊: · 攔截器 · 監聽器:引用監聽器和暴露監聽器 老實說,依賴之前的原始碼分析經驗,導致我饒了很大的彎路,一直找不到filter和listener被使用