Bash decimal to base 62 conversion

2020-08-14 10:02发布

I would like to reverse the operation performed by the following bash command:

$ echo $((62#a39qrT))
9207903953

i.e. convert decimal 9207903953 to base 62, keeping bash standard of {0..9},{a..z},{A..Z}.

I know I can do this by using bc, but I will have to manually convert each character then. For example, I do this currently:

BASE62=($(echo {0..9} {a..z} {A..Z}))
for i in $(echo "obase=62; 9207903953" | bc)
do
    echo -n ${BASE62[$i]} #Doesn't work if bc's output contains leading zeroes
done

There must be a way to do this in a less 'hackier' way. Do you know of a way to do this more efficiently?

EDIT: changed bc input.

4条回答
孤傲高冷的网名
2楼-- · 2020-08-14 10:25

Arbitrary base10 to baseX conversion function using gforth, and tr, (tr is needed since gforth and bash use different chars to print bases):

n2b() { gforth -e "$1 $2 base ! . cr bye" | tr '[0-9A-z]' '[0-9a-zA-Z]' ; }
n2b 9207903953 62
n2b 9207903953 61   # Also works with other bases.
n2b 9207903953 3

Output:

a39qrT 
aT1PM8
212202201021222121202
查看更多
在下西门庆
3楼-- · 2020-08-14 10:32

Or without bc and with arbitrary base:

function baseXencode() {
  awk 'BEGIN{b=split(ARGV[1],D,"");n=ARGV[2];do{d=int(n/b);i=D[n-b*d+1];r=i r;n=d}while(n!=0);print r}' "$1" "$2"
}
function base62encode() {
  baseXencode 0123465789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ "$1"
}
查看更多
唯我独甜
4楼-- · 2020-08-14 10:37
function base62encode() {
  bc<<<"obase=62;$1" | awk '{for (i=1; i<=NF; i++) printf "%c", $i+(($i<10)?48:(($i<36)?87:29))}'
}
  • bc<<<"obase=62;$1" converts to a sequence of space prefixed decimal numbers from 00 to 61
  • then offset each digit into ASCII table and convert to character with awk's printf

Or without the for loop:

function base62encode() {
  bc<<<"obase=62;$1" | awk 'BEGIN{RS=" +"}/./{printf "%c", $1+(($1<10)?48:(($1<36)?87:29))}';
}
查看更多
The star\"
5楼-- · 2020-08-14 10:43

I do really appreciate the solution you came up with, and I guess there's no way around it straight with bash. Here's the little point you've missed:

BASE62=($(echo {0..9} {a..z} {A..Z}))
for i in $(bc <<< "obase=62; 9207903953"); do
    echo -n ${BASE62[$(( 10#$i ))]}
done && echo

Output:

a39qrT
查看更多
登录 后发表回答