一、问题
格式化输出大数字,使得输出结果更清晰地看出数字的数量级。默认用逗号(可用-d指定整数的分隔符)分隔整数部分,用点号(可用-t指定整数与小数的分隔符)分隔整数与小数部分。如1123456789.012输出为1,123,456,789.012。
二、详解
(1)算法
1123456789.012输出为1,123,456,789.012。
i、首先分开整数部分integer(cut-d. -f1取整数部分)和小数部分decimal(cut -d. -f2取小数部分)。
ii、整数部分的处理:
方式一:当作数字来处理(使用%取模运算,/取余运算)
a、每次取整数低三位,长度小于3就补前缀0(避免出现漏0情况,例如003)。
b、将此次得到的三位与上一次得到的三位用分隔符(如果没有指定就默认)组合起来。
c、整数去掉低三位,继续循环操作。当此整数小于等于999就不再继续,然后组合就得输出结果。
方式二:当作字符串来处理
a、每次取低三位,echo $str | cut -c$((${#str}-2))-${#str},${#str}为str的长度,$((${#str}-2))相当于expr${#str}-2。
b、原串每次都要裁掉尾部三个字符,每次取到的字串的长度为循环判断条件。
(2)代码
#!/bin/bash
SEPARATOR="." #默认分割符
formatnumber() #函数实现部分1123456789.012或者1123456789
{
#检测传给脚本的大数值中是否含有非法的整小数分隔符,即存在与用户指定或默认分隔符不同的符号。
#方法:删除其中所有数字,看剩下的内容,若不空且不等于$DD,则非法
separator="$(echo $1 | sed 's/[[:digit:]]//g')"
if [[ -n $separator && "$separator" != "$SEPARATOR" ]] # 蓝色部分可用 –a 表示
then
echo "number is wrong,please input again!"
exit 1
fi
#分别取整数部分和小数部分,如
integer=$(echo $1 | cut -d. -f1)
decimal=$(echo $1 | cut -d. -f2)
#使脚本能够处理各种整数与小数各种分隔符情况,可修改为
#integer=$(echo $1 | cut -d${SEPARATOR:="."} -f1)
#decimal=$(echo $1 | cut -d${SEPARATOR:="."} -f2)
if [ -n $decimal ] #非空,即有小数部分。
then
#等价于result="${DD:="."}$decimal"其中:=,若变量为空,可以使用:=设置一个默认值;不为空则为$DD的值。
if [ -z $DD ]
then
DD=.
fi
result=$DD$decimal
fi
thousand=$integer
while [ $thousand -gt 999 ]
do
remainder=$((thousand%1000))
while [ ${#remainder} -lt 3 ];do #${#remainder} 为remainder串的长度
remainder=0$remainder #避免remainder为00x时的丢0情况。
done
thousand=$(($thousand/1000))
#下四句等价于result="${TD:=","}$remainder$result"
if [ -z $TD ] ; then # 判断用户是否自定义整数分隔符, 若 空
TD=","
fi
result="$TD$remainder$result" # 与分隔符合成串。 注意顺序
done
if [ -n $thousand ]; then #若非空
result="$thousand$result"
fi
echo $result
}
#####函数入口####
#处理脚本标志参数
#./script -d . -t , 1123456789.012
#脚本标志参数处理命令: getopts "d:t:" opt
#带两个参数,第一个参数为带解析的参数标志序列串(如-d,-t等),以冒号(:)表示该选项必须带参数value值
#第二个参数为:变量,每执行一次存放 参数标志序列串中的一个标志字符
#返回值:未解析结束返回0,解析结束返回1
while getopts "d:t:" opt
do
case $opt in
d) DD="$OPTARG" ;; #$OPTARG为标志指定的参数值
t) TD="$OPTARG" ;; #可以之前初始化DD,TD,也可不初始化
*) echo "no opt:$opt" >&2; exit 1 ;;
esac
done
#内部变量$OPTARG:每次解析标志对应的参数值,便默认存放在此变量内
#内部变量$OPTIND:初始值是1,每次getopts处理完一个命令参数后就递增它,得到getopts要处理的下一个参数(也可以使用$#进行参数个数的处理,此时$*代表-d . -t , 1123456789.012和$#值为5)。
#当执行shift 1时,各个位置参数的值向左移1次,此时$1的值为原$2的值,$2的值为原$3的值,依次类推
shift $(($OPTIND - 1))
formatnumber $1
三、总结
(1)该代码包含字符串和数字处理的不同方法,对长数据的处理还是有借鉴意义的。
(2)getopts比较适用,可以添加一系列的长参数,其内部变量OPTARG和OPTIND应充分理解,熟练使用$#和$*。
(3)本代码若有不完善的地方,可请大家留言,也可联系本人yang.ao@i-soft.com.cn。