2011-12-19 77 views
0

我想在bash中實現一個CRC16校驗和。我正從現有的C++代碼移植。我快到了,但我得到了不同的答案。crc16算法從C++到bash

我不太明白爲什麼C++代碼和bash腳本之間的校驗和是不同的。

另一組眼睛將是一個很大的幫助。

這裏是C++代碼:

uint16_t Encoder::checksum(std::string thestring) 
{ 
    uint8_t d, e, f; 
    uint16_t c, r, crccalc; 
    c = 0xffff; 

    for (unsigned int i = 0; i < thestring.length(); i++) 
    { 
     d = thestring[i]; 
     e = c^d; 
     f = e^(e << 4); 
     r = (c >> 8)^(f << 8)^(f << 3)^(f >> 4); 
     c = r; 
    } 
    c ^= 0xffff; 
    crccalc = c; 
    return crccalc; 
} 

這裏是我的bash代碼:

function calc_crc16() 
{ 
    string=$1 
    while read -d "" -n 1 ; do astring+=("$reply") ; done <<< "$string" 

    cnt=${#astring[@]} 
    c=0xffff 

    for ((x=0;x<$cnt;x++)); do 
     char=${astring[$x]} 
     e=$(($c^$char)) 
     s=$(($e << 4)) 
     f=$(($e^$s)) 
     t1=$(($c >> 8)) 
     t2=$(($f << 8)) 
     t3=$(($f << 3)) 
     t4=$(($f >> 4)) 
     r1=$(($t1^$t2^$t3^$t4)) 
     c=$r1 
    done 
    c=$c^0xffff 
    echo "checksum = $c" 
} 

難道要有事做的整數的大小?我猜在bash中我沒有太多可以做的事情。

我得到一個實際的數字,但它不符合我知道正常工作的C++。有沒有人看到任何我可能會搞砸的東西?

+1

編寫一個快速測試,以查看C++ vs bash中的max int。祝你好運。 – shellter 2011-12-19 16:36:47

+1

我覺得我這裏最大的問題是***爲什麼*** – Sorpigal 2011-12-19 17:20:31

回答

1

好的。在Sorpigal的幫助下,我有一個工作版本。

我懷疑這可能都是在一個awk腳本內完成的,而這個腳本可能運行得更快。我可能會嘗試下一步。

謝謝大家的幫助。我不是故意在這裏偷取解決方案,但是我努力了,並且認爲它值得放棄。

反正這裏是一個工作版本:

function calc_crc16() 
{ 
    while read -r -d "" -n 1 ; do astring+=("$REPLY") ; done <<< "$1" 

    cnt=${#1} 
    c=65535 

    for ((x=0;x<$cnt;x++)); do 
     char=$(printf '%d' \'"${1:$x:1}") 
     e=$(((c^char) & 0x00FF)) 
     s=$(((e << 4) & 0x00FF)) 
     f=$(((e^s) & 0x00FF)) 
     r1=$(((c >> 8)^(f << 8)^(f << 3)^(f >> 4))) 
     c=$r1 
    done 
    c=$((c^0xffff)) 
    echo "checksum = $c" 
} 
3

第一個問題是靠近頂部

while read -d "" -n 1 ; do astring+=("$reply") ; done <<< "$string" 

$reply是錯誤的,因爲你沒有指定閱讀的名字是$REPLY變量名。

下一個錯誤是在結束

c=$c^0xffff 

這應該是

c=$(($c^0xffff)) 

至少這樣就沒有錯誤,正確和恰當運行是另一回事。

正確性問題:如果輸入字符串有空格怎麼辦?這會打破可怕的。時常引用變量exapnsions

變化

char=${astring[$x]} 

char="${astring[$x]}" 

奇怪的是這條規則是內部$(())結構不同。你的位操作應引用,沒有任何$變量在這些情況下

e=$((c^char)) 
s=$((e << 4)) 
f=$((e^s)) 
t1=$((c >> 8)) 
t2=$((f << 8)) 
t3=$((f << 3)) 
t4=$((f >> 4)) 
r1=$((t1^t2^t3^t4)) 

後來

c=$((c^0xffff)) 

這將導致變量加以擴展和空白不是吹的事情了。

一般而言,您還應該通過-rread,請參閱help read

爲什麼在將其處理成數組之前製作$1的額外副本?使用

while read -d "" -n 1 ; do astring+=("$REPLY") ; done <<< "$1" 

就足夠了。

在處理之前,可能沒有必要將輸入變爲數組。相反,您可以在循環中將字符切出字符串,這更接近於C++版本的功能。替換

char="${astring[$x]}" 

char="${1:$x:1}" 

這是直接在功能放慢參數進行操作;因爲我們不再做的一個副本,我們也需要得到$cnt不同的方式

cnt=${#1} 

但你真的有比這更大的問題,如事實字符不是在bash整數。要轉換,必須使用以下語法:

printf '%d' \'a 

其中a是要轉換的字符。插入到這個腳本的情況下這將是

char=$(printf '%d' \'"${1:$x:1}") 

現在,我們取得了一些進展,但我必須問你要考慮是否所有的這實在是值得的。即使你可以使它工作,你會得到什麼?

+0

謝謝。我解決這個問題。我收到「e = $(($ c^$ char))」這一行的錯誤,因爲$ char是'$'字符(來自我傳遞給它的字符串)。有沒有辦法避免拋出這個錯誤? bash似乎是以一種奇怪的方式解釋字符串。 – jasonmclose 2011-12-19 18:57:59

+0

@jasonmclose:看看我編輯的這幾個字和其他一些東西。 – Sorpigal 2011-12-19 19:23:17

+0

我知道這個前提看起來很奇怪。但我正在研究一些通過串行線路發送數據的代碼,並且我需要使用特定的CRC校驗數據。我不控制接收端代碼,所以我的校驗和必須匹配。如果我可以把它保存在一個腳本中,我們可以更好地管理該領域的事情(不知道如果我們甚至在這些框上有gcc)。我有一個可以運行的crc可執行文件,如果需要的話可以回退它。在大多數情況下,這對我來說是一個很好的bash課程。 – jasonmclose 2011-12-19 19:40:10

2

只是以供將來參考,這裏是我想出了awk腳本。

這和我的C++代碼一樣快,基本上是瞬時的。 bash需要大約10秒才能運行相同的字符串。 awk要快得多。

function awk_calc_crc16() 
{ 
    output=$(echo $1 | awk 'function ord(c){return chmap[c];} 
    BEGIN{c=65535; for (i=0; i < 256; i++){ chmap[sprintf("%c", i)] = i;}} 
    { 
     split($0, chars, ""); 
     for(i = 1; i <= length(chars); i++) 
     { 
      cval=ord(chars[i]) 
      e=and(xor(c, ord(chars[i])), 0x00FF); 
      s=and(lshift(e, 4), 0x00FF); 
      f=and(xor(e, s), 0x00FF); 
      r=xor(xor(xor(rshift(c, 8), lshift(f, 8)), lshift(f, 3)), rshift(f, 4)); 
      c=r; 
     } 
    } 
    END{c=xor(c, 0xFFFF); printf("%hu", c);}') 
    echo $output; 
}