【问题标题】:crc16 algorithm from C++ to bash从 C++ 到 bash 的 crc16 算法
【发布时间】:2011-12-19 16:25:22
【问题描述】:

我正在尝试在 bash 中实现 CRC16 校验和。我正在从现有的 C++ 代码中移植。我快到了,但我得到了不同的答案。

我不太明白为什么 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++ 不匹配,我知道它可以正常工作。有没有人看到我可能把事情搞砸的地方?

【问题讨论】:

  • 编写一个快速测试,看看 C++ 与 bash 中的 max int 是多少。祝你好运。
  • 我认为我这里最大的问题是为什么

标签: bash checksum crc crc16


【解决方案1】:

第一个问题在顶部附近

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

$reply 是错误的,因为您没有为读取指定变量名称,所以名称是 $REPLY

下一个错误在最后

c=$c ^ 0xffff

这应该是

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

至少这样运行不会出错,正确性和适当性是另外一回事。

正确性问题:如果输入字符串有空格怎么办?这将可怕地打破。总是引用变量扩展

改变

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 ))

这将导致变量被扩展并且空格不会破坏。

一般情况下,您还应该将-r 传递给read,请参阅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}")

现在我们正在取得进展,但我真的必须请您考虑所有这些是否真的值得。即使你能成功,你有什么收获?

【讨论】:

  • 谢谢。我解决了这个问题。 “e=$(($c ^ $char))”行出现错误,因为 $char 是“$”字符(来自我传递给它的字符串)。有没有办法让它不抛出这个错误? bash 似乎以一种奇怪的方式解释字符串。
  • @jasonmclose:请参阅我的编辑,了解有关此内容和其他一些内容的一些信息。
  • 我知道前提似乎很奇怪。但我正在处理一些通过串行线路发送数据的代码,我需要使用特定的 CRC 对数据进行校验和。我不控制接收端代码,所以我的校验和必须匹配。如果我能把它保存在一个脚本中,我们可以更好地管理现场的事情(不知道我们是否会在这些盒子上安装 gcc)。我有一个可以工作的 crc 可执行文件,如果需要,可以依赖它。在大多数情况下,这对我来说是一个很好的 bash 课程。
【解决方案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;
}

【讨论】:

    【解决方案3】:

    好的。在 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"
    }
    

    【讨论】:

      【解决方案4】:

      更短(更快)的版本
      变化:
      - 不需要开头的while循环
      - 不需要 r1
      - 不需要cnt
      - 对 bash 变量使用大写字母
      - 在 char=$(printf ...
      中少一个“\”(反斜杠) - 删除了 0xFF 的前导 0

      function calc_crc16() {
        CRC=0xFFFF
      
        for ((X=0; X<${#1}; X++)); do
          CHAR=$(printf '%d' "'${1:$X:1}")
          E=$(((CRC ^ CHAR) & 0xFF))
          S=$(((E << 4)     & 0xFF))
          F=$(((E ^ S)      & 0xFF))
          CRC=$(((CRC >> 8) ^ (F << 8) ^ (F << 3) ^ (F >> 4)))
        done
      
        let CRC^=0xFFFF
        printf "0x%X\n" $CRC
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-11-27
        • 1970-01-01
        • 2023-02-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-27
        • 2012-10-23
        相关资源
        最近更新 更多