網上大多的教材都是面向大佬的不少細節原理都沒有講清楚,對於咱們這些新萌菜鳥們實在太不友好了。因而我寫一篇相對輕鬆易懂的博客,但願能對學習CRC的朋友們有所幫助吧!html
你說小學3年級的小明同窗不知好歹喜歡村長女兒王大麻子,因而羞澀的他想到寫一封情書已表心意。正所謂閨女似情人,愛女心切的村長凡是信件通通要過他之手。若是這份情書被她爸稍加「幾筆」豈不悲劇了?算法
如何驗證情書是否被動過手腳(驗證數據是否損壞),介於王大麻子數學不行,數數還行。做爲數學課表明的小明同窗馬上想到一個好主意:將全部的文字轉化二進制,只要數一數1的數量是奇數仍是偶數不就能夠了嗎!數組
好比我要傳遞M這個字符那麼他的ASCII的值爲0100 1101(M),就是數據末尾加上一個1或0,使其整個數據的1的數量能夠湊齊奇數或偶數。函數
若是規定信件全部1的個數爲奇數的話,那麼0100 1101(M)才4個1,咱們就數據的末尾加上一個1湊齊奇數(5個1):0100 1101 1;若是數據中的1恰好奇數,咱們就在末尾加上一個0就可(這個方法叫作奇校驗),固然相反若是規定的信件全部1的個數爲偶數(偶校驗),恰好處理的方法也是相反。oop
雖然這個方法簡單到連他沒有上學的弟弟都作得起(很容易經過硬件方式實現驗證),可是這個的出錯率是五五開啊(恰好少/多偶數個1),且永遠檢查不出0的錯誤(就算多100個零也看不出來)。性能
你說奇偶不行,哪我相加總該行吧?因而乎小明同窗又推出第二號方案:將每個文字(字節)的值相加來驗證。學習
好比傳遞LOVE,咱們就能夠獲得四個數字76(L) 79(O) 86(V) 69(E),他們相加可得310,若是這個數字嫌他太大了很差寫,咱們能夠限制他在0~255(一個字節)這個範圍以內,那麼咱們就獲得一個驗證數字55 =310 – 255(最簡單的辦法就是減去255的倍數)。而後將數字附加數據後面,對方接受到數據執行一樣的操做對比驗證數字是否一致。優化
雖然說這個方法要比上面的方法要可靠一些,但凡事總有列外:網站
1、好比數據錯誤恰好等於驗證數字
咱們要傳輸的數據:76 79 86 69 310(驗證數字)
發生錯誤的數據: 76 78(-1) 87(+1) 69 310(驗證數字仍是同樣的)編碼
2、單個字符的出錯率爲1/256
無很多天夜小明同窗左思右想,最終還剩最後三根頭髮之際他學會除法,頭頂一涼想到一個絕佳主意:若是我將信件上的全部文字組合成一個長數字,而後用一個我和王大麻子都知道的數字(約定的多項式)去除以,將餘數做爲一個驗證數字的話……
假設我要傳輸一個字符87(W),和王大麻子約定除數爲6,那麼結果很明顯就是87 ÷ 6 = 14……3,那麼咱們能夠將3做爲驗證數字附加原始數據的末尾發送。
但明顯咱們常規的借位除法大大超出了王大麻子的數學水平,因而小明同窗決定不作人了發明一個二進制除法,並取名爲「模二除法」。
所謂模二除法實際形式上和咱們的日常使用的除法同樣的(用於二進制運算),可是惟一不一同的是關於計算當中一切的加減法通通換成「異或」(XOR),一句話歸納異或運算就是:異性相吸(返回真1),同性相斥(返回假0):1 Xor 0 = 1,0 Xor 1 = 1, 1 Xor 1 = 0, 0 Xor 0 = 0。
那麼咱們用上面列子來試一下模二除法(模二除法的結果不等於普通除法)
王大麻子表示:我去!算完以後我還要覈對餘數?!不能再簡單點嗎?
因而乎小明同窗又開始沒日沒夜苦想,最終當最後的狼牙山「三壯士」也離他而去時,他頭頂一涼想到一個絕佳主意:在信件數據的末尾(即數據低位LSB)補上我和王大麻子都知道的數字的長度-1的0(生成項長度-1)而後在被同知數字相除,獲得的餘數再附加原始數據的末尾上。(這裏說的原始數據是指沒有補零的,且餘數長度必定與補零的長度一致)
口說無憑,咱們來從新用這個升級計算方法試一試。
咱們將餘數10補在原始數據1010111的末尾獲得了一個新數:101011110,而後咱們再用110去除,神奇的事情發生了:沒有餘數了。(接受端只要將已修改的數據與生成項模二相除沒有餘數即表明數據無誤)
這即是CRC(循環冗餘校驗,Cyclic Redundancy Check)是一種爲了檢測數據是否損壞處理辦法。而當中的模二除法是無借位(不向高位借位的)的性質十分重要:意味咱們計算CRC只須要簡單的數據位移和Xor便可(到後面你就知道了)。
固然理論上講CRC仍是出錯的可能(不過已經比程序猿能找到女友的概率要低了),因此選擇一個好的除數就是一個很是重要的事情,關於CRC的除數有個專業的名稱:生成多項式,簡稱生成項。如何選擇生成項這是須要必定的代數編碼知識(已經不是我這種鹹魚能搞懂的問題了)。好在咱們能夠直接用大佬計算好的生成項,不一樣的生成項有不一樣的CRC,如:CRC八、CRC1六、CRC-CCITT、CRC32……。
咱們不妨嘗試將二進制轉化一種多項式:數字中多少位1表明的是x的多少次方,0乘以任何數都是0因此不用管。
好比101010(42),能夠轉化爲:x^5+x^3+x。(注意最低位0次方,任何數零次方都等於1)
假設用x^3+x^2+x除去x+1,能夠獲得以下式子:(這一段基本上徹底搬運的循環冗餘校驗Wiki,寫的真的好!)
很好咱們在兩邊再乘一個x+1:
這裏不難看出:獲得一個商×除數+餘數的式子,其中的(x^2+1)就是商,-1就是餘數(我也不沒懂爲啥餘數是負數)。咱們還能夠對左邊式子再提取一次x,可得:
咱們能夠理解這裏提取的x是對於x^2+x+1(1011)進行補一次零變成了10110,實際上這個補零還有個說法叫作左移(要注意數據的方向高位在左邊)。
如何理解補零的性質這個很簡單,咱們類比十進制的補零如:1要補兩次零變成100,很明顯補零就是乘與10的幾回方。回到二進制中就是2的幾回方,而多項式中的x就能夠表明2。
經過以上的式子的整理能夠得出通用公式便是:
M(x)就表明的是原始數據,x^n表明的是數據末補n個0,K(x)表明的是Key也就是生成項,R(x)表明的餘數也是上一節提到FCS。接收者接受到M(x)*x^n+R(x)看看是能被K(x)整除不,能夠即爲正確數據。
要注意的一點x^n的長度(補零長度)受限於R(x)(目的是能夠直接追加在末尾,而不影響原始數據):他們長度一致,且必定比K(x)的長度少1。
關於R(x)爲何必定比K(x)的長度少1?我我的愚見是特殊的模二除法:K(x)的最高位必定1(否則沒有意義啊),而數據處理過程當中須要計算數據的最高位也是1(能夠對照着除法的當中與除數對應的那個被除數的那部分數據),他們進行Xor就變成0,實際計算每每是剩下的部分(K(x)長度-1)(在程序設計中反正都會變成0乾脆都不計算首位,這就是爲啥網上的CRC生成多項式的簡記碼都是默認捨棄首位1的緣由)。
CRC的原理實際上遠比這些要複雜的多,涉及到羣論這類咱們這些吃瓜羣衆望塵莫及的數理知識。以上也只是我對Wiki的搬運和理解瞎猜,但願你們能經過這個簡單列子大概瞭解CRC的性質,若有不對之處有望大佬不惜賜教!
雖然說上一節咱們已經知道CRC是怎麼計算的,但明顯電腦可不會寫除法公式(程序很難直接用這種方法)。且不說要對齊一個超長二進制數據進行逐位計算的困難不說,單單是像vbscript這種既沒有幾個位操做符又用起來蛋疼的語言基本上是很難實現(大佬:喵喵喵?)。不過能夠轉化一個思路:好比每次只取一部分數據來計算如何?
仔細觀察計算的過程,能夠發現其實每一次Xor都是固定不動的生成項與其對應的數據首位「消1」。那咱們就能夠假想出一個與生成項長度一致的「盒子」,取出一部分的數據出來若首位是1時就進行一次Xor,遇到0則左移到1爲止,左移形成的右端的空缺用0補充。而這裏0但願理解爲一種「存儲」,它「存儲」 生成項中未和數據進行計算的那一部分,按順序前後附加被計算數據的後面,當先一部分的數據所有計算以後,實際上「盒子」中剩下都是未和數據計算的部分的「和」11011 xor 10110 = 11011 xor ( 10000 xor 00100 xor 00010)(這裏實際上就是Xor的交換律到後面就會體會到他的強大)
經過以上的結論咱們能夠僞裝設計出一個算法(這裏我用的是VBScript):
Const PLAY = &H1021& '這裏生成項(實際上就是CRC-CCITT),注意這裏默認首位1是去掉的 Const TEXT = "123456789" '這裏就是原始數據 Dim L,I,CRC Do While L < Len(TEXT) '經過循環取得原始數據的每個字符來計算 L = L + 1 CRC = CRC Xor (Asc(Mid(TEXT,L,1)) * &H100&) '實際上文中提到的「盒子」專業的說法應該叫作:寄存器。 '這裏取出新的字符出來與CRC寄存器裏上一次未和數據進行計算的多項式的部分進行Xor,做爲新數據進行下一步處理。 '機智的你也許發現了:1021這個生成式是16位與一個字節8位不符怎麼辦?別忘咱們還有神器——補零!乘上H100 = 256 = 2 ^ 8 For I = 0 To 7 '判斷數據最高位是否爲 1 '1 - 左移一位(去掉數據首位1),剩下的部分進行Xor '0 - 就左移一位(去掉多餘的0) If (CRC And &H8000&) Then CRC = (CRC * 2) And &HFFFF& '// 左移 CRC = CRC Xor PLAY Else CRC = (CRC * 2) And &HFFFF& '// 左移 End If Next CRC = CRC And &HFFFF& '限制寄存器內數據大小爲16位。 Loop WScript.Echo Hex(CRC)
運行以後成功就能夠得出CRC-CCITT (XModem)的標準驗證值:31C3(在線計算網站:On-line CRC calculation and free library)
實際上CRC就像開心消消樂同樣,就是不斷消除首位數據。這時你想:要是能一口氣消除一個字節(8bit)以上的數據那該多好!
通常來說文藝青年會這麼作(CRC的正常計算):
可是2B青年會這麼想:可不能夠將生成項先Xor了?
咱們能夠先提早計算出與數據前幾位相符的生成項之和再Xor從而到達一口氣消掉了多位數據的目的,Xor的乘法交換律容許咱們提早計算出須要的數據A Xor B Xor C = A Xor ( B Xor C )
既然如此乾脆直接將前八位0000 0000 ~ 1111 1111(0 ~ 255,一個字節)這個範圍全部的生產項的和所有計算存儲成表格,等計算的時候直接取出數據的首字節出來做爲索引找到對應表格的中生存項的和與去掉首位字節的數據進行Xor不就能夠了嗎。
雖然說想法很美好,可是如何實現前8位從0~255全部的生成項之和了?咱們來思考一下CRC計算的本質是什麼?復讀沒錯是不斷地消除首位數據,那麼在Xor運算下消除方法就是:數據同樣!
那麼咱們將0~255這256個數字進行CRC逐位計算後剩下不就是已經消除掉前8位數據的生成項之和嗎!(由於前8位數據同樣因此被消除了)
用通俗點語言就是:咱們提早將一個字節的CRC驗證碼計算出來。(實際上這一段語文老師死得早的我構思好久,擔憂沒有上面這段過渡部分會形成讀者理解困難,但願你們能Get個人點……Orz)
如下就是關於CRC16的正序表格計算代碼:
Const PLAY = &H8005& '這裏生成項(CRC16),注意這裏默認首位1是去掉的 ReDim Table(255) '這裏存儲表格 '// 計算表格部分 Dim I,J,Temp '對於0~255這256個數字進行CRC計算,並將計算好的CRC驗證碼按順序存儲在表格(數組)中 For I = 0 To 255 Temp = (I * &H100&) And &HFFFF& For J = 0 To 7 If (Temp And &H8000) Then Temp = (Temp * 2) And &HFFFF& Temp = Temp Xor PLAY Else Temp = (Temp * 2) And &HFFFF& End If Next Table(I) = Temp And &HFFFF& Next '// 輸出CRC16表格代碼(使用VbsEdit的調試) Dim y,x,u Debug.WriteLine "Dim CRC16_Table:CRC16_Table = Array( _" For y = 1 To 64 For x = 1 To 4 Debug.Write "&H",String(4 - Len(Hex(Table(u))),"0"),Hex(Table(u)),"&" If u <> 255 Then Debug.Write ", " Else Debug.Write " " u = u + 1 Next Debug.WriteLine "_" Next Debug.WriteLine ")"
代碼無誤的話,應該會在調試框中獲得以下代碼(做爲驗證能夠看看):
Dim CRC16_Table:CRC16_Table = Array( _ &H0000&, &H8005&, &H800F&, &H000A&, _ &H801B&, &H001E&, &H0014&, &H8011&, _ &H8033&, &H0036&, &H003C&, &H8039&, _ &H0028&, &H802D&, &H8027&, &H0022&, _ &H8063&, &H0066&, &H006C&, &H8069&, _ &H0078&, &H807D&, &H8077&, &H0072&, _ &H0050&, &H8055&, &H805F&, &H005A&, _ &H804B&, &H004E&, &H0044&, &H8041&, _ &H80C3&, &H00C6&, &H00CC&, &H80C9&, _ &H00D8&, &H80DD&, &H80D7&, &H00D2&, _ &H00F0&, &H80F5&, &H80FF&, &H00FA&, _ &H80EB&, &H00EE&, &H00E4&, &H80E1&, _ &H00A0&, &H80A5&, &H80AF&, &H00AA&, _ &H80BB&, &H00BE&, &H00B4&, &H80B1&, _ &H8093&, &H0096&, &H009C&, &H8099&, _ &H0088&, &H808D&, &H8087&, &H0082&, _ &H8183&, &H0186&, &H018C&, &H8189&, _ &H0198&, &H819D&, &H8197&, &H0192&, _ &H01B0&, &H81B5&, &H81BF&, &H01BA&, _ &H81AB&, &H01AE&, &H01A4&, &H81A1&, _ &H01E0&, &H81E5&, &H81EF&, &H01EA&, _ &H81FB&, &H01FE&, &H01F4&, &H81F1&, _ &H81D3&, &H01D6&, &H01DC&, &H81D9&, _ &H01C8&, &H81CD&, &H81C7&, &H01C2&, _ &H0140&, &H8145&, &H814F&, &H014A&, _ &H815B&, &H015E&, &H0154&, &H8151&, _ &H8173&, &H0176&, &H017C&, &H8179&, _ &H0168&, &H816D&, &H8167&, &H0162&, _ &H8123&, &H0126&, &H012C&, &H8129&, _ &H0138&, &H813D&, &H8137&, &H0132&, _ &H0110&, &H8115&, &H811F&, &H011A&, _ &H810B&, &H010E&, &H0104&, &H8101&, _ &H8303&, &H0306&, &H030C&, &H8309&, _ &H0318&, &H831D&, &H8317&, &H0312&, _ &H0330&, &H8335&, &H833F&, &H033A&, _ &H832B&, &H032E&, &H0324&, &H8321&, _ &H0360&, &H8365&, &H836F&, &H036A&, _ &H837B&, &H037E&, &H0374&, &H8371&, _ &H8353&, &H0356&, &H035C&, &H8359&, _ &H0348&, &H834D&, &H8347&, &H0342&, _ &H03C0&, &H83C5&, &H83CF&, &H03CA&, _ &H83DB&, &H03DE&, &H03D4&, &H83D1&, _ &H83F3&, &H03F6&, &H03FC&, &H83F9&, _ &H03E8&, &H83ED&, &H83E7&, &H03E2&, _ &H83A3&, &H03A6&, &H03AC&, &H83A9&, _ &H03B8&, &H83BD&, &H83B7&, &H03B2&, _ &H0390&, &H8395&, &H839F&, &H039A&, _ &H838B&, &H038E&, &H0384&, &H8381&, _ &H0280&, &H8285&, &H828F&, &H028A&, _ &H829B&, &H029E&, &H0294&, &H8291&, _ &H82B3&, &H02B6&, &H02BC&, &H82B9&, _ &H02A8&, &H82AD&, &H82A7&, &H02A2&, _ &H82E3&, &H02E6&, &H02EC&, &H82E9&, _ &H02F8&, &H82FD&, &H82F7&, &H02F2&, _ &H02D0&, &H82D5&, &H82DF&, &H02DA&, _ &H82CB&, &H02CE&, &H02C4&, &H82C1&, _ &H8243&, &H0246&, &H024C&, &H8249&, _ &H0258&, &H825D&, &H8257&, &H0252&, _ &H0270&, &H8275&, &H827F&, &H027A&, _ &H826B&, &H026E&, &H0264&, &H8261&, _ &H0220&, &H8225&, &H822F&, &H022A&, _ &H823B&, &H023E&, &H0234&, &H8231&, _ &H8213&, &H0216&, &H021C&, &H8219&, _ &H0208&, &H820D&, &H8207&, &H0202& _ )
咱們能夠以空間換取時間,直接將CRC表格計算好做爲一個常數數組使用。
獲得表格以後回到驅動表法的計算:取出CRC寄存器中的首位字節,而後將CRC左移去掉首字節而後取出一字節新數據裝入CRC低端空出字節中,根據取出首字節找到對應表格中的生成項之和與CRC寄存器進行Xor,而後重複這個步驟直到數據所有取完計算完。要注意的是:由於驅動表法是一個一個字節計算,因此他必須計算以前在原始數據上補零(不能像直接計算法那樣經過逐位左移方式本身完成補零)。咱們來看一下代碼是如何實現的:
Dim CRC16_Table '這裏表格咱們直接就套用上一節咱們計算好的數據 Const TEXT = "123456789" '這裏就是原始數據 '這裏要注意驅動表法沒有逐位補零,因此咱們要手動在數據末尾增長兩個字節的零 Dim str:str = TEXT & String(2,Chr(0)) Dim L,I,T,CRC '初始化CRC寄存器值爲0 CRC = 0 Do While L < Len(str) L = L + 1 '取出CRC寄存器中的首字節 T = (CRC And &HFF00&) / &H100 '左移數據一個字節(就是去掉寄存器中的首字節),並在空出的字節放入新的數據,限制寄存器大小爲兩個字節。 CRC = ((CRC * &H100&) Or Asc(Mid(str,L,1))) And &HFFFF& '將已經去掉首字節的CRC寄存器與對應的表格生成項之和進行Xor CRC = CRC Xor CRC16_Table(T) '固然也能夠直接將上面三句縮寫成一句: 'CRC = (((CRC * 256) Or Asc(Mid(str,L,1))) And &HFFFF&) Xor Table16((CRC And &HFF00&) / &H100) Loop '限制CRC大小爲兩個字節 CRC = CRC And &HFFFF& WScript.Echo Hex(CRC)
輸出結果爲:FEE8(注意這個值並非標準的CRC16驗證碼,後面咱們還會講到如何修改它。)
這個部分的思路徹底來自poiu_elab大佬的【腦凍結】CRC我就拿下了,感謝大佬的分享!
咱們不妨用大佬的列子看看(醜不要臉的偷懶):
咱們針對31 32 33 34進行CRC-CCITT驅動表法的計算,着重觀察每次查詢表格的索引(也就是黃色部分),你會發現實際上索引就是原始數據與寄存器前2位數據Xor計算的結果。
好比第一次查詢時,寄存器爲0與原始數據31 Xor獲得查表的索引31,查得26 72並存入寄存器內;遇到第二個原始數據32與寄存器內的26進行Xor獲得14,查得52 B5因爲寄存器左移一個字節因而上一次72移到高位與52進行Xor獲得20,因而如今寄存器內就是20(72 Xor 52) B5;遇到第三個原始數據33與寄存器內20(72 xor 52)進行Xor,獲得13……重複這個過程直到查到最後一個原始數據爲止。
經過上面的步驟咱們能夠整理出直驅動表法的算法:
僞語言版:
循環:取得字符
寄存器 = 去掉首位字節的寄存器 Xor表格[寄存器的首字節 Xor 取出的原始數據]
C語言版 CRC32直驅表法:
While(len--)
r=(r<<8)^t[(r>>24)^*p++];
我我的的總結直驅動表法實際上就是將驅動表法再一次簡化,將數據輸入和表格查詢有機的結合在一塊兒,這麼作好處能夠不用在原始數據上補零,方便持續計算CRC(不停的加入新的數據)。
因爲vbscript對於32位數據進行四則運算(乘法補零)會溢出,因此不妨爲咱們嘗試一下寄存器分段處理(就是將寄存器分紅兩個部分處理來解決數據溢出的問題),代碼以下:
Dim Table_M(255) '表格的高位 Dim Table_L(255) '表格的低位 '// CRC32正序表格計算 Const PLOY_M = &H04C1& '生成項高位,CRC32生成項:04C11DB7 Const PLOY_L = &H1DB7& '生成項低位 Dim I,J,CRC_M,CRC_L For I = 0 To 255 CRC_M = (I * &H100&) And &HFFFF& CRC_L = 0 For J = 0 To 7 If (CRC_M And &H8000) Then '// 左移 CRC_M = (CRC_M And &H7FFF) * &H2 CRC_M = CRC_M Xor ((CRC_L And &H8000&) / &H8000&) '低位寄存器中高位會移動到高位寄存器中的低位 CRC_L = (CRC_L And &H7FFF) * &H2 '// Xor計算 CRC_M = CRC_M Xor PLOY_M CRC_L = CRC_L Xor PLOY_L Else '// 左移 CRC_M = (CRC_M And &H7FFF) * &H2 CRC_M = CRC_M Xor ((CRC_L And &H8000&) / &H8000&) CRC_L = (CRC_L And &H7FFF) * &H2 End If Table_M(I) = CRC_M Table_L(I) = CRC_L Next Next '// CRC32計算部分 Const TEXT = "123456789" '原始數據 CRC_M = 0 CRC_L = 0 Dim T Do While L < Len(TEXT) L = L + 1 '計算出查詢表格的索引 T = ((CRC_M And &HFF00&) / &H100&) Xor Asc(Mid(TEXT,L,1)) '分別計算出高位、低位寄存器,要注意的是低位寄存器中高位會移動到高位寄存器中的低位。 CRC_M = ((CRC_M And &HFF&) * &H100) Xor ((CRC_L And &HFF00&) / &H100&) Xor Table_M(T) CRC_L = ((CRC_L And &HFF&) * &H100) Xor Table_L(T) Loop '記得輸出不足CRC寄存器長度的數據時在前面補零 Debug.WriteLine String(4 - Len(Hex(CRC_M)),"0"),Hex(CRC_M),String(4 - Len(Hex(CRC_L)),"0"),Hex(CRC_L)
輸出結果爲:89A1897F
細心的朋友可能發現咱們上一節計算出的CRC32驗證值是不對的,爲了方便機器更好的計算CRC因此制定一些規則,稱爲CRC參數模型。咱們一睹CRC32模型的芳容:
Width:表明生成項長度
Poly:生成項
Init:寄存器計算前的初始值,其實初始值是爲了保存數據以前零(CRC計算特性是省略開頭的零)
RefIn:輸入原始數據進行二進制數據反轉,由於數據輸入是一個字節一個字節,因此反轉也是一個字節,如圖所示:
RefOut:最後輸出的CRC數據進行數據反轉,注意是整個CRC數據進行反轉(其實就是對CRC寄存器進行反轉)
XorOut:對已經RefOut的CRC數據進行Xor處理,方式和Init同樣。
Check:是對字符串「123456789」CRC計算的驗證值,做爲參考看看本身的程序計算是否有誤。
根據這個模型,咱們對上一節的CRC32算法再次魔改:
Dim Table_M:Table_M = Array( _ ' CRC32表格高位,因爲篇幅有限請自行補齊代碼 Dim Table_L:Table_L = Array( _ ' CRC32表格低位,因爲篇幅有限請自行補齊代碼 '顛倒二進制函數 Public Function RevBin(ByVal Value,ByVal lLen) RevBin = 0 If IsNumeric(Value) And Value Then lLen = lLen - 1 Dim I,REG For I = 0 To lLen If ((2^I) And Value) Then REG = REG Or 2^(lLen - I) Next RevBin = REG End If End Function '原始數據 Const TEXT = "123456789" 'Init:初始化寄存器 CRC_M = &HFFFF& CRC_L = &HFFFF& '計算CRC部分 Dim T Do While L < Len(TEXT) L = L + 1 'RefIn:注意這裏的對一個字節(8bit)的反轉(我曾經在這裏被坑過) T = ((CRC_M And &HFF00&) / &H100) Xor RevBin(Asc(Mid(TEXT,L,1)),8) CRC_M = ((CRC_M And &HFF) * &H100) Xor ((CRC_L And &HFF00&) / &H100) Xor Table_M(T) CRC_L = ((CRC_L And &HFF) * &H100) Xor Table_L(T) Loop 'RefOut:反轉計算出來的CRC值 Dim Temp Temp = RevBin(CRC_L,16) CRC_L = RevBin(CRC_M,16) CRC_M = Temp 'XorOut:最後一次Xor CRC_M = CRC_M Xor &HFFFF& CRC_L = CRC_L Xor &HFFFF& '輸出CRC驗證值 Debug.WriteLine String(4 - Len(Hex(CRC_M)),"0"),Hex(CRC_M),String(4 - Len(Hex(CRC_L)),"0"),Hex(CRC_L)
終於獲得正確的CRC32驗證碼:CBF43926
雖然能夠計算出CRC32,可是每次都要顛倒輸入、輸出的值這顯十分浪費性能。咱們能夠不能夠將這個算法直接鏡像,從而避免對其輸入、輸出的逆轉,只須要對錶格進行逆轉和相反的數據位移方向便可。
那麼新的問題已經出現,咱們怎能停滯不前:這逆轉的表格咋算啊?!
答案就是:要用魔法(逆轉)去戰勝魔法(逆轉):將生成項:04C11DB7逆轉爲EDB88320,新數據插入的方向爲數據低位(新數據不須要逆轉只須要經過它的值計算出對應的生成項之和),數據位移方向向右(這個就是逆轉的核心)。咱們能夠達到如下代碼:
Dim Table_M(255) '表格的高位 Dim Table_L(255) '表格的低位 '// CRC32逆序表格計算,CRC32生成項:EDB88320 Const PLOY_M = &HEDB8& '生成項高位 Const PLOY_L = &H8320& '生成項低位 Dim I,J,CRC_M,CRC_L For I = 0 To 255 CRC_M = 0 CRC_L = I For J = 0 To 7 If (CRC_L And &H1) Then '// 右移 CRC_L = (CRC_L And &HFFFE&) / &H2 CRC_L = CRC_L Xor ((CRC_M And &H1) * &H8000&) '高位寄存器中低位會移動到低位寄存器中的高位 CRC_M = (CRC_M And &HFFFE&) / &H2 '// Xor計算 CRC_M = CRC_M Xor PLOY_M CRC_L = CRC_L Xor PLOY_L Else '// 右移 CRC_L = (CRC_L And &HFFFE&) / &H2 CRC_L = CRC_L Xor ((CRC_M And &H1) * &H8000&) CRC_M = (CRC_M And &HFFFE&) / &H2 End If Next Table_M(I) = CRC_M Table_L(I) = CRC_L Next '//輸出逆序表格代碼 Public Sub Print(ByVal Name,ByVal lTable) Dim y,x,u Debug.WriteLine Name & " = Array( _" For y = 1 To 32 For x = 1 To 8 Debug.Write "&H",String(4 - Len(Hex(lTable(u))),"0"),Hex(lTable(u)),"&" If u <> 255 Then Debug.Write ", " Else Debug.Write " " u = u + 1 Next Debug.WriteLine "_" Next Debug.WriteLine ")" End Sub Call Print("CRC32Table_MSB",Table_M) Call Print("CRC32Table_LSB",Table_L)
計算出表格代碼根據上圖的鏡像算法,咱們能夠將新數據xor到寄存器最低位取得表格索引,而後兩個寄存器左移一個字節計算索引對應的逆轉的生成項之和。封裝一下代碼就是這樣的:
Class C_CRC '初始化類載入數據 Private m_CRC32Table_MSB Private m_CRC32Table_LSB Private Sub Class_Initialize() m_CRC32Table_MSB = Array( _ &H0000&, &H7707&, &HEE0E&, &H9909&, &H076D&, &H706A&, &HE963&, &H9E64&, _ &H0EDB&, &H79DC&, &HE0D5&, &H97D2&, &H09B6&, &H7EB1&, &HE7B8&, &H90BF&, _ &H1DB7&, &H6AB0&, &HF3B9&, &H84BE&, &H1ADA&, &H6DDD&, &HF4D4&, &H83D3&, _ &H136C&, &H646B&, &HFD62&, &H8A65&, &H1401&, &H6306&, &HFA0F&, &H8D08&, _ &H3B6E&, &H4C69&, &HD560&, &HA267&, &H3C03&, &H4B04&, &HD20D&, &HA50A&, _ &H35B5&, &H42B2&, &HDBBB&, &HACBC&, &H32D8&, &H45DF&, &HDCD6&, &HABD1&, _ &H26D9&, &H51DE&, &HC8D7&, &HBFD0&, &H21B4&, &H56B3&, &HCFBA&, &HB8BD&, _ &H2802&, &H5F05&, &HC60C&, &HB10B&, &H2F6F&, &H5868&, &HC161&, &HB666&, _ &H76DC&, &H01DB&, &H98D2&, &HEFD5&, &H71B1&, &H06B6&, &H9FBF&, &HE8B8&, _ &H7807&, &H0F00&, &H9609&, &HE10E&, &H7F6A&, &H086D&, &H9164&, &HE663&, _ &H6B6B&, &H1C6C&, &H8565&, &HF262&, &H6C06&, &H1B01&, &H8208&, &HF50F&, _ &H65B0&, &H12B7&, &H8BBE&, &HFCB9&, &H62DD&, &H15DA&, &H8CD3&, &HFBD4&, _ &H4DB2&, &H3AB5&, &HA3BC&, &HD4BB&, &H4ADF&, &H3DD8&, &HA4D1&, &HD3D6&, _ &H4369&, &H346E&, &HAD67&, &HDA60&, &H4404&, &H3303&, &HAA0A&, &HDD0D&, _ &H5005&, &H2702&, &HBE0B&, &HC90C&, &H5768&, &H206F&, &HB966&, &HCE61&, _ &H5EDE&, &H29D9&, &HB0D0&, &HC7D7&, &H59B3&, &H2EB4&, &HB7BD&, &HC0BA&, _ &HEDB8&, &H9ABF&, &H03B6&, &H74B1&, &HEAD5&, &H9DD2&, &H04DB&, &H73DC&, _ &HE363&, &H9464&, &H0D6D&, &H7A6A&, &HE40E&, &H9309&, &H0A00&, &H7D07&, _ &HF00F&, &H8708&, &H1E01&, &H6906&, &HF762&, &H8065&, &H196C&, &H6E6B&, _ &HFED4&, &H89D3&, &H10DA&, &H67DD&, &HF9B9&, &H8EBE&, &H17B7&, &H60B0&, _ &HD6D6&, &HA1D1&, &H38D8&, &H4FDF&, &HD1BB&, &HA6BC&, &H3FB5&, &H48B2&, _ &HD80D&, &HAF0A&, &H3603&, &H4104&, &HDF60&, &HA867&, &H316E&, &H4669&, _ &HCB61&, &HBC66&, &H256F&, &H5268&, &HCC0C&, &HBB0B&, &H2202&, &H5505&, _ &HC5BA&, &HB2BD&, &H2BB4&, &H5CB3&, &HC2D7&, &HB5D0&, &H2CD9&, &H5BDE&, _ &H9B64&, &HEC63&, &H756A&, &H026D&, &H9C09&, &HEB0E&, &H7207&, &H0500&, _ &H95BF&, &HE2B8&, &H7BB1&, &H0CB6&, &H92D2&, &HE5D5&, &H7CDC&, &H0BDB&, _ &H86D3&, &HF1D4&, &H68DD&, &H1FDA&, &H81BE&, &HF6B9&, &H6FB0&, &H18B7&, _ &H8808&, &HFF0F&, &H6606&, &H1101&, &H8F65&, &HF862&, &H616B&, &H166C&, _ &HA00A&, &HD70D&, &H4E04&, &H3903&, &HA767&, &HD060&, &H4969&, &H3E6E&, _ &HAED1&, &HD9D6&, &H40DF&, &H37D8&, &HA9BC&, &HDEBB&, &H47B2&, &H30B5&, _ &HBDBD&, &HCABA&, &H53B3&, &H24B4&, &HBAD0&, &HCDD7&, &H54DE&, &H23D9&, _ &HB366&, &HC461&, &H5D68&, &H2A6F&, &HB40B&, &HC30C&, &H5A05&, &H2D02& ) m_CRC32Table_LSB = Array( _ &H0000&, &H3096&, &H612C&, &H51BA&, &HC419&, &HF48F&, &HA535&, &H95A3&, _ &H8832&, &HB8A4&, &HE91E&, &HD988&, &H4C2B&, &H7CBD&, &H2D07&, &H1D91&, _ &H1064&, &H20F2&, &H7148&, &H41DE&, &HD47D&, &HE4EB&, &HB551&, &H85C7&, _ &H9856&, &HA8C0&, &HF97A&, &HC9EC&, &H5C4F&, &H6CD9&, &H3D63&, &H0DF5&, _ &H20C8&, &H105E&, &H41E4&, &H7172&, &HE4D1&, &HD447&, &H85FD&, &HB56B&, _ &HA8FA&, &H986C&, &HC9D6&, &HF940&, &H6CE3&, &H5C75&, &H0DCF&, &H3D59&, _ &H30AC&, &H003A&, &H5180&, &H6116&, &HF4B5&, &HC423&, &H9599&, &HA50F&, _ &HB89E&, &H8808&, &HD9B2&, &HE924&, &H7C87&, &H4C11&, &H1DAB&, &H2D3D&, _ &H4190&, &H7106&, &H20BC&, &H102A&, &H8589&, &HB51F&, &HE4A5&, &HD433&, _ &HC9A2&, &HF934&, &HA88E&, &H9818&, &H0DBB&, &H3D2D&, &H6C97&, &H5C01&, _ &H51F4&, &H6162&, &H30D8&, &H004E&, &H95ED&, &HA57B&, &HF4C1&, &HC457&, _ &HD9C6&, &HE950&, &HB8EA&, &H887C&, &H1DDF&, &H2D49&, &H7CF3&, &H4C65&, _ &H6158&, &H51CE&, &H0074&, &H30E2&, &HA541&, &H95D7&, &HC46D&, &HF4FB&, _ &HE96A&, &HD9FC&, &H8846&, &HB8D0&, &H2D73&, &H1DE5&, &H4C5F&, &H7CC9&, _ &H713C&, &H41AA&, &H1010&, &H2086&, &HB525&, &H85B3&, &HD409&, &HE49F&, _ &HF90E&, &HC998&, &H9822&, &HA8B4&, &H3D17&, &H0D81&, &H5C3B&, &H6CAD&, _ &H8320&, &HB3B6&, &HE20C&, &HD29A&, &H4739&, &H77AF&, &H2615&, &H1683&, _ &H0B12&, &H3B84&, &H6A3E&, &H5AA8&, &HCF0B&, &HFF9D&, &HAE27&, &H9EB1&, _ &H9344&, &HA3D2&, &HF268&, &HC2FE&, &H575D&, &H67CB&, &H3671&, &H06E7&, _ &H1B76&, &H2BE0&, &H7A5A&, &H4ACC&, &HDF6F&, &HEFF9&, &HBE43&, &H8ED5&, _ &HA3E8&, &H937E&, &HC2C4&, &HF252&, &H67F1&, &H5767&, &H06DD&, &H364B&, _ &H2BDA&, &H1B4C&, &H4AF6&, &H7A60&, &HEFC3&, &HDF55&, &H8EEF&, &HBE79&, _ &HB38C&, &H831A&, &HD2A0&, &HE236&, &H7795&, &H4703&, &H16B9&, &H262F&, _ &H3BBE&, &H0B28&, &H5A92&, &H6A04&, &HFFA7&, &HCF31&, &H9E8B&, &HAE1D&, _ &HC2B0&, &HF226&, &HA39C&, &H930A&, &H06A9&, &H363F&, &H6785&, &H5713&, _ &H4A82&, &H7A14&, &H2BAE&, &H1B38&, &H8E9B&, &HBE0D&, &HEFB7&, &HDF21&, _ &HD2D4&, &HE242&, &HB3F8&, &H836E&, &H16CD&, &H265B&, &H77E1&, &H4777&, _ &H5AE6&, &H6A70&, &H3BCA&, &H0B5C&, &H9EFF&, &HAE69&, &HFFD3&, &HCF45&, _ &HE278&, &HD2EE&, &H8354&, &HB3C2&, &H2661&, &H16F7&, &H474D&, &H77DB&, _ &H6A4A&, &H5ADC&, &H0B66&, &H3BF0&, &HAE53&, &H9EC5&, &HCF7F&, &HFFE9&, _ &HF21C&, &HC28A&, &H9330&, &HA3A6&, &H3605&, &H0693&, &H5729&, &H67BF&, _ &H7A2E&, &H4AB8&, &H1B02&, &H2B94&, &HBE37&, &H8EA1&, &HDF1B&, &HEF8D& ) End Sub '// 計算CRC部分 Public Function CRC32(ByVal Value) Dim CRC_MSB,CRC_LSB CRC_MSB = &HFFFF& CRC_LSB = &HFFFF& Dim StrLen:StrLen = Len(Value) Dim FirstByte,L Do While L < StrLen L = L + 1 FirstByte = (CRC_LSB And &HFF&) Xor Asc(Mid(Value,L,1)) CRC_LSB = ((CRC_LSB And &HFF00&) / &H100) Xor ((CRC_MSB And &HFF&) * &H100&) Xor m_CRC32Table_LSB(FirstByte) CRC_MSB = ((CRC_MSB And &HFF00&) / &H100) Xor m_CRC32Table_MSB(FirstByte) Loop CRC_MSB = CRC_MSB Xor &HFFFF& CRC_LSB = CRC_LSB Xor &HFFFF& CRC32 = String(4 - Len(Hex(CRC_MSB)),"0") & Hex(CRC_MSB) & String(4 - Len(Hex(CRC_LSB)),"0") & Hex(CRC_LSB) End Function End Class Dim CRC:Set CRC = New C_CRC WScript.Echo CRC.CRC32("123456789")
到這裏教程就徹底結束了,固然我這個代碼徹底沒有效率,純粹寫出演示鬧着玩(笑)。若是是想用vbs進行CRC計算的話,我推薦Demon大佬的這篇Blog:用VBS實現PHP的crc32函數。使用MSScriptControl.ScriptControl對象調用js就能夠十分方便的實現數據數據左右移。可讀性遠比我這篇代碼要高的多,推薦去讀一讀方便理解。
雖然說我碼這麼多字,但我仍是沒有自信能保證各位必定能懂(強行語文老師背鍋),因此我我的以爲寫的不錯的幾篇博客(也是我學習的博客),但願能對你們有用。
循環冗餘校驗(CRC)算法入門引導: https://blog.csdn.net/liyuanb...
我學習CRC3二、CRC1六、CRC原理和算法的總結(與WINRAR結果一致): https://wenku.baidu.com/view/...
【腦凍結】CRC我就拿下了: https://www.cnblogs.com/poiu-...
CRC的基本原理詳解: https://blog.csdn.net/dream_1...
循環冗餘檢驗 (CRC) 算法原理: http://www.cnblogs.com/esestt...
你真的搞明白CRC的計算過程了嗎?: http://blog.chinaaet.com/wuya...
CRC檢錯技術原理: https://www.cnblogs.com/forge...
CRC算法詳解與c源碼: https://wenku.baidu.com/view/...
最通俗的CRC校驗原理剖析: http://blog.51cto.com/winda/1...
VB的CRC32校驗代碼: https://blog.csdn.net/zdingyu...
CRC校驗碼原理、實例、手動計算: https://www.cnblogs.com/bugut...
如何計算CRC32校驗和?: https://codeday.me/bug/201708...
CRC算法與實現: https://blog.csdn.net/ncdawen...
再推薦兩個在線計算網站方便驗證程序:
CRC(循環冗餘校驗)在線計算: http://www.ip33.com/crc.html
On-line CRC calculation and free library: https://www.lammertbies.nl/co...
固然若是你E文比較好的話,仍是推薦Ross Williams的《A PAINLESS GUIDE TO CRC ERROR DETECTION ALGORITHMS》 http://www.ross.net/crc/downl...
哦,對了!故事的最後:王大麻子其實喜歡他們班的班長——小強