第一章:The Missing Code Library--4.優雅的表示大數

   程序員常常犯的一個錯誤是:在向用戶顯示計算的結果時,並無在第一時間格式化好它們。若是用戶沒有從右向左手動計數,而後在內心每三個數字就插入一個逗號的話,是很難界定43245435這個數字有沒有達到百萬的(英文中的數字記法,若是是漢語的話,我的以爲仍是4個數字插入一個逗號更好讀點,若是漢語須要這樣計數的話)。使用下面這個腳原本格式化你的計算結果。 git

nicenumber.sh
 #!/bin/sh
 # nicenumber.sh -- 給定一個數字,將它用逗號分隔的形式表示出來
 # 以DD和TD做爲實例。實例化nicenumber.sh,若是指定了第二個參數,會產生一個標準輸出。
 
 nicenumber()
 {
     # 注意,在輸入中假定點號是十進制的小數位。
     # 輸出中的十進制分隔符是點號,除非用戶使用了 -d 選項。
 
     integer=$(echo $1 | cut -d. -f1)         # 小數點左邊
     decimal=$(echo $1 | cut -d. -f2)        # 小數點右邊
 
     if [ $decimal != $1 ]; then
         # 這裏有個小數部分,將它包含進去
         result="${DD:="."}$decimal"
     fi
 
     thousands=$integer
 
     while [ $thousands -gt 999 ]; do
         remainder=$(($thousands%1000))         # 3個最低有效位數字
 
         while [ ${#remainder} -lt 3 ]; do     # 若是須要,強制以0打頭
             remainder="0$remainder"
         done
 
         thousands=$(($thousands/1000))         # remainder左側數字,若是有
         result="${TD:=","}${remainder}${result}"     # 從右向左生成
     done
 
     nicenum="${thousands}${result}"
     if [ ! -z $2 ]; then
         echo $nicenum
     fi
 }
 
 DD="."     # 十進制的點分隔符,區分整數部分和小數部分
 TD=","     # 千位分隔符,每三個數字添加一個
 
 while getopts "d:t:" opt; do     # 下面會詳談getopts的用法,它很特殊,但很強力
     case $opt in
         d)DD="$OPTARG";;
         t)TD="$OPTARG";;
     esac
 done
 shift $(($OPTIND-1))
 
 if [ $# -eq 0 ]; then
     echo "Usage: $(basename $0)[-d c] numeric value"
     echo " -d 指定十進制的小數位分隔符(默認是點號)"
     echo " -t 指定了千位分隔符(默認是逗號)"
 fi
 
 nicenumber $1 1     # 第二個參數強制 nicenumber 輸出到標準輸出
 
 exit 0

   腳本如何工做:
   這個腳本的核心是nicenumber函數中的while循環。該函數得到數值,而後循環將它分割爲3個最低有效位數字(也就是會出如今下一個逗號右邊的3個數字),以及保留的數值。接着,這些最低有效位又會經過循還進行處理。 程序員

   運行代碼:
   要運行這個腳本,只需簡單指定一個很是大的數值,而後腳本就會根據須要,要麼使用默認的要麼按照標誌位指定(-d或-t)的,來增長小數分隔符和千位分隔符。由於函數的輸出是一個數字,因此結果能夠像下面這樣:echo "Do you really want pay $(nicenumber $price) dollars?" shell

   運行結果: ide

./nicenumber.sh 5894625
 5,894,625
 ./nicenumber.sh 589462532.433
 589,462,532.433
 ./nicenumber.sh -d, -t. 589462532.433
 589.462.532,433
 ./newnicenum.sh 
 Usage: newnicenum.sh [-d c] [-t c] numeric value
  -d 指定十進制的小數位分隔符(默認是點號)
  -t 指定了千位分隔符(默認是逗號)

  延伸閱讀:
   不一樣的國家使用的小數位分隔符和千位分隔符是不一樣的,所以須要增長標誌位來靈活的指定。好比,德國和意大利會使用-d "."和-t ",",法國會用-d ","和-t " ",而瑞士有4種語言,他們會用-d "."和-t ""。這個例子很好的展現了靈活應用是如何優於寫死代碼的,因此這個腳本工具徹底能夠適用於大多數國家的用戶。另外一方面,該腳本中把輸入中的小數位分隔符寫死爲點號,若是你預計輸入中的小數位使用不一樣的分隔符,你能夠把兩個調用cut命令中的指定分隔符給改變。看下面: 函數

integer=$(echo $1 | cut "-d$DD" -f1) #小數位左側 decimal=$(echo $1 | cut "-d$DD" -f2)      #小數位右側

   這樣能夠運行,但若是使用一個徹底不一樣的小數分隔符的話,就不那麼完美了。一個更加精妙的解決方法是在這2行代碼前面先測試下,以此來確保預期的小數位分隔符是用戶所要求的。咱們能夠學習第2個腳本中的思想,用sed刪除全部數字,看看剩下的是什麼: 工具

1 seperator="$(echo $1 | sed 's/[[:digit:]]//g')" 2 if [ !-z "$seperator" -a "$seperator" != "$DD"]; then 3   echo "$0: Unknown decimal seperator $seperator encounted." >&2 4   exit 1 5 fi

我的心得: 學習

   1.cut中的分隔符是-d選項,-f是指的域。想一想awk。這個就是簡化版。
   2.在Shell腳本中對於字符串的引用--引號和大括號。
   3.getopts:分析傳遞到腳本中的命令行參數的最強力工具 測試

getopts具備如下特色:
   1.全部傳遞到腳本中的參數,前面必須加上一個減號【-】,getopts是不會處理不帶-前綴的參數的。
   2.getopts通常放在一個while循環中,而這個while循環和標準的while循環有些不一樣,它是沒有中括號[]判斷的。
   3.getopts將會取代外部命令getopt
在本腳本中使用的getopts結構的說明:
d和t都被認爲是標誌選項,d後面跟一個冒號,說明該選項要帶一個參數,同理t。 ui

shift $(($OPTIND-1))

上句的做用是參數指針向下移動一位。shift是能夠帶參數的,參數是移動的個數。將參數指針OPTIND減1,就是指向下一個參數的意思。這時候,$1指向第一個非選項參數了。
如何理解它,看我作個測試: spa

1 ./newnicenum.sh -d. -t 589462532.345 2 而後打印 3 Usage:  newnicenum.sh  [-d c] [-t c] numeric value4 -d 指定十進制的小數位分隔符(默認是點號) 5 -t 指定了千位分隔符(默認是逗號)"

爲何?由於 -t 後面沒有跟上一個參數,還記得getopts中 t 後面的冒號嗎?因此589462532.345被認爲是t的參數,而後$1指向爲空了,由於這個數字參數被shift掉了。因此$#(即參數個數)等於0了,打印3條語句。

再測試下:

1 ./newnicenum.sh -d -t, 589462532.345 2 而後打印 3 ./newnicenum.sh: Unknown decimal seperator . encounted.

爲何不打印那3句話了?由於它認爲小數位分隔符是點號,而$DD是爲空的($DD是在getopts中賦值的,爲空),它倆不一樣,因此符合nicenumber函數中的開頭的if判斷語句中-a後面的條件。

注:以上關於getopts的說明參考了"Advanced Bash-Scripting Guide"。

彙總下延伸中的內容,最終腳本以下:

#!/bin/sh
 
 nicenumber()
 {
     # 注意,在輸入中假定點號是十進制的小數位。
     # 輸出中的十進制分隔符是點號,除非用戶使用了 -d 選項。
 
     seperator="$(echo $1 | sed 's/[[:digit:]]//g')"
     if [ ! -z "$seperator" -a "$seperator" != "$DD" ]; then
         echo "$0: Unknown decimal seperator $seperator encounted." >&2
         exit 1
     fi
 
     integer=$(echo $1 | cut "-d$DD" -f1)     #小數位左側
     decimal=$(echo $1 | cut "-d$DD" -f2)    #小數位右側
 
     if [ $decimal != $1 ]; then
         # 這裏有個小數部分,將它包含進去
         result="${DD:="."}$decimal"
     fi
 
     thousands=$integer
 
     while [ $thousands -gt 999 ]; do
         remainder=$(($thousands%1000))         # 3個最低有效位數字
 
         while [ ${#remainder} -lt 3 ]; do     # 若是須要,強制以0打頭
             remainder="0$remainder"
         done
 
         thousands=$(($thousands/1000))         # remainder左側數字,若是有
         result="${TD:=","}${remainder}${result}"  # 從右向左生成,:=的用法,是若是TD尚未設置,就將它的默認值設置爲逗號
     done
 
     nicenum="${thousands}${result}"
     if [ ! -z $2 ]; then
         echo $nicenum
     fi
 }
 
 DD="."     # 十進制的點分隔符,區分整數部分和小數部分
 TD=","     # 千位分隔符,每三個數字添加一個
 
 while getopts "d:t:" opt; do     # 下面會詳談getopts的用法,它很特殊,但很強力
     case $opt in
         d)DD="$OPTARG";;
         t)TD="$OPTARG";;
     esac
 done
 shift $(($OPTIND-1))
 
 if [ $# -eq 0 ]; then
     echo "Usage: " $(basename $0) " [-d c] [-t c] numeric value"
     echo " -d 指定十進制的小數位分隔符(默認是點號)"
     echo " -t 指定了千位分隔符(默認是逗號)"
 fi
 
 nicenumber $1 1     # 第二個參數強制 nicenumber 輸出到標準輸出
 
 exit 0
相關文章
相關標籤/搜索