【问题标题】:Convert decimal to Base-4 in bash在bash中将十进制转换为Base-4
【发布时间】:2017-09-04 11:29:14
【问题描述】:

我一直在使用一种非常基本的方法,大部分情况下是直接的,将基数为 10 的数字 {1..256} 转换为基数为 4 或四进制数的方法。我一直在使用简单的除法$(($NUM/4)) 来获得主要结果,以便获得余数$(($NUM%4)),然后反向打印余数以获得结果。我使用以下bash 脚本来执行此操作:

#!/bin/bash

NUM="$1"

main() {

local EXP1=$(($NUM/4))
local REM1=$(($NUM%4))
local EXP2=$(($EXP1/4))
local REM2=$(($EXP1%4))
local EXP3=$(($EXP2/4))
local REM3=$(($EXP2%4))
local EXP4=$(($EXP3/4))
local REM4=$(($EXP3%4))

echo "
$EXP1 remainder $REM1
$EXP2 remainder $REM2
$EXP3 remainder $REM3
$EXP4 remainder $REM4

Answer: $REM4$REM3$REM2$REM1
"
}

main

此脚本适用于数字 0-255 或 1-256。但超出这个(这些)范围,结果会变得混杂,经常重复或不准确。这不是什么大问题,因为我不打算转换超过 256 或小于 0 的数字(负数 [还])。

我的问题是:“有没有更简化的方法来做到这一点,可能使用exprbc

【问题讨论】:

  • 存储在数组中的查找表怎么样?使用大括号扩展很容易创建.. 0-255 比 1-256 简单
  • 这会起作用,但也会让我无法将它包含在一个简单的脚本中。我只是想简化算术表达式,可能通过递归方法而不是使用先前变量值的多个变量。我很可能会越了解这个概念。
  • 不确定你是否理解我的意图。这里有两行代码将十进制的 0-255 转换为 base4...base4=({0..3}{0..3}{0..3}{0..3})echo "${base4[34]}"
  • 啊,我明白了。相当聪明。完全避免使用$(()) 进行转换,而是根据${base4[here]} 中使用的值线性打印base4 等效值。我只是在 for 循环中尝试了这个,它是 100% 准确且非常快的。很好的主意。将您的解决方案放入答案中,我会将其标记为已回答。谢谢。

标签: bash shell math scripting arithmetic-expressions


【解决方案1】:

利用brace expansion创建查找表

$ echo {a..c}
a b c    
$ echo {a..c}{r..s}
ar as br bs cr cs
$ echo {0..3}{0..3}
00 01 02 03 10 11 12 13 20 21 22 23 30 31 32 33

因此,0-255 以十进制为底数为 4

$ base4=({0..3}{0..3}{0..3}{0..3})
$ echo "${base4[34]}"
0202
$ echo "${base4[255]}"
3333

【讨论】:

  • 再次感谢。最好的答案通常是最简单的。
  • 这是非常有限的并且会消耗大量内存! ...没有eval,语法{}{}{} 无法编写脚本...这个解决方案看起来很性感,但很重。
  • @F.Hauri 同意,这不适合一般情况
  • Sundeep 提供的解决方案对于大量的数字运算来说可能很繁重,但对于我的用例来说已经足够了。我已经使用这种方法编写了我需要的脚本,并且不需要eval
  • 我的印象是,一个坏习惯会使用比简单任务所需的更多代码。我使用 Sundeep 的想法制定的方法不到 5 行代码,并且使用 time 命令测量速度在不到 0.05 秒内执行。请记住,我不需要转换大于 0-255 的数字或负数。
【解决方案2】:

中的 Base 4 转换

int2b4() {
    local val out num ret=\\n;
    for ((val=$1;val;val/=4)){
        out=$((val%4))$out;
    }
    printf ${2+-v} $2 %s${ret[${2+1}]} $out
}

仅使用 1 个参数调用,这将转换为 base 4 并打印结果,后跟 换行符。如果存在第二个参数,则将填充此名称的变量,不打印。

int2b4 135
2013
int2b4 12345678
233012011032

int2b4 5432 var
echo $var
1110320

详细解释:

  • 主要部分是(可以写):

     out=""
     for (( val=$1 ; val > 0 ; val = val / 4 )) ;do
         out="$((val%4))$out"
         done
    

    我们的转换循环可以很容易理解(我希望)

  • local 确保out val num 为本地空变量并在本地初始化ret='\n'

  • printf 行使用一些 bashisms

    • 如果$2 为空,则${2+-v} 为空,否则代表-v
    • ${ret[${2+1}]}分别变成${ret[]}(或${ret[0]})和${ret[1]}

    所以这条线变成了

     printf "%s\n" $out
    

    如果没有第二个参数 ($2) 并且

      printf -v var "%s" $out
    

    如果第二个参数是var(请注意,不会将换行符附加到填充的变量中,而是添加用于终端打印)。

转换回十进制:

在 bash 下有一个 bashism 让您可以使用任意基数进行计算:

echo $((4#$var))
5432

echo $((4#1110320))
5432

在脚本中:

for integer in {1234..1248};do
    int2b4 $integer quaternary
    backint=$((4#$quaternary))
    echo $integer $quaternary $backint
  done

1234 103102 1234
1235 103103 1235
1236 103110 1236
1237 103111 1237
1238 103112 1238
1239 103113 1239
1240 103120 1240
1241 103121 1241
1242 103122 1242
1243 103123 1243
1244 103130 1244
1245 103131 1245
1246 103132 1246
1247 103133 1247
1248 103200 1248

【讨论】:

  • 这个解决方案很酷。有点神秘但很酷。我会给它+1。
  • 好的,我已经添加了一些解释。
  • 并在演示脚本中修改了变量名。
  • 我理解代码,不要误会我的意思。如果我将来使用类似的解决方案,我只需要确保我的管理员同事也能理解它。这是一个很好的解决方案。
猜你喜欢
  • 1970-01-01
  • 2013-01-06
  • 2016-10-14
  • 2016-05-21
  • 2013-05-07
  • 1970-01-01
  • 2014-05-27
  • 1970-01-01
  • 2021-09-30
相关资源
最近更新 更多