0埋め
日付とかをファイル名に付ける際、桁数を合わせるために
file_name=${year}$(printf "%02d" ${month})$(printf "%02d" ${day})
みたいにすると、year=2019
、month=9
、day=8
だとすると
file_name=20190908
となります。"%02d"
はd
が整数値で02
で2文字分確保、足りない分は左側を0で埋める、という意味になります。
%2d
だと足りない分は空白になります。
d
の代わりにs
なら通常の文字列を扱うようになります。
0始まりの文字の8進法扱い
シェルスクリプトでは変数としては全て単なる文字列で、
printf
などではそのコマンドの中で整数などを判断しますが、
整数でも8進法や16進法を認識してくれるものもあります。
printf
とかだと、
0
で始まる数字: 8進法0x
で始まる数字: 16進法
と判断されます。
$ printf "%d\n" 10
10
$ printf "%d\n" 010
8
$ printf "%d\n" 0x10
16
この時、09
などは8進法でありえない数字なのでエラーになり、00
を返します。
$ printf "%02d" 09
bash: printf: 09: invalid number
00
単にa
とか文字列を渡した場合も同様のエラーが出ます。
逆に、printf
の出力整形で、d
は10進法の出力を表していて、o
だと8進法、x
で16進法を表示することも出来ます。
$ printf "%d\n" 10
10
$ printf "%o\n" 10
12
$ printf "%x\n" 10
a
これ以外にも算術式でも0
/0x
が使えます。
$ echo $((010+0x10))
24
ただし、expr
やbc
では通常0がついてても0を除いた整数値として扱うので注意。
$ expr 010 + 010
20
$ expr 010 + 0x10
expr: not a decimal number: '0x10'
$ echo 010 + 010|bc
20
$ echo 010 + 0x10|bc
(standard_in) 1: parse error
bc
ではibase=8
とかすると入力を8進法にすることができますが、この場合は0などを付けずに入力します。
$ echo "ibase=8;010 + 010"|bc
16
ibase=16
なら16進法でこの場合も入力数字に0x
とかを付けずに。
(付けるとエラーになります。)
逆に出力を16進法とかに変えたければobase=16
とかを付けます。
$ echo "obase=16;5+5"|bc
A
起こりうる問題
この様な数字の扱いですが、Bashなどのシェルスクリプトでは 変数自体は単なる文字列なのできちんと確認しないとちょっと問題が起こります。
例えば月を最終的に0埋めの文字列にしたい時。
整形するには
month=$(printf "%02d" {$month})
とか変換すれば出来ます。
ただし、 月をスクリプトの引数で設定するようにしたりする時、
人によっては親切に09
などと0を付けて入力してくれるかもしれません。
この場合、上の式で
bash: printf: 09: invalid number
というエラーが出てしまいます。
また、date
コマンドとかで
date +"%m"
とかすると
09
と0埋めした状態で返してくれます。
これを上の式に渡せば上の様にエラーになります。
ただ、この問題が起こるのは8月、9月だけなので
それ以外の月に作ってテストして大丈夫だと思っていると
8月に入った段階でエラーになってしまいます。
また、エラーになってもbash -e
とかで終了させる様な設定にしてないと
00
という文字列を使って実行してしまい予期せぬ動作をするので危険です。
月の指定であれば08, 09のみが問題で、その他の数字はそのまま正しく出るので良いのですが、
3桁以上の場合とかなら012
は
$ printf "%03d" 012
010
となるのでこれも予期せぬ結果になってしまいます。
なのでこの辺りの0埋め数字を扱う際にはそれぞれのタイミングできちんと確認する必要があります。
対処法
0がついてる場合に外してあげてから渡せば問題がなくなります。
上の月の例であれば0が1つつくだけなので
$ month=${month#0}
とすれば0が付いてれば消されます。
もうちょっと一般的にやりたければ
$ month=$(echo $month|sed 's/^0*//')
みたいに左の0をすべて消すようにsedとかを使えばOK。
ちょっと別の話になりますが、Linux(GNU)だと
$ month=$(echo $month|sed 's/^0\+//')
とすれば0の1回以上の繰り返しに対応しますが、 Mac(BSD)だとこの正規表現は使えません。
今回の場合は0*
として0の0回以上の繰り返しに対応させても同じことなので
こちらにしておいたほうが移植性があがります。
これらの様に数字をきちんと処理しても良いですが、 そもそもこの例では数字を計算しているわけではないので 単に文字列として
$ printf "%02s" $month
09
とすればmonth
が0
でも09
でも09
という出力になります。
12
とかもそのまま12
になります。
単に数字を整形したいだけ、というのであればこ方法が一番確実かと思います。
その他の言語での8進法などの扱い
Pythonだと
0b
始まりで2進法0o
始まりで8進法0x
始まりで16進法
を表します。
>>> 10
10
>>> 0b10
2
>>> 0o10
8
>>> 0x10
16
10進法を各進法で表示したければbin
、oct
、hex
という組み込み関数が使えます。
>>> bin(10)
'0b1010'
>>> oct(10)
'012'
>>> hex(10)
'0xa'
これらの関数で出力されるのは文字列です。(数字じゃないので注意。)
C++だと
0b
始まりで2進法0
始まりで8進法0x
始まりで16進法
を表します。
1 2 3 4 5 6 7 8 9 10 |
|
で
10
2
8
16
となります。
Pythonと違って8進法で0o10
とかするとエラーになります。
error: unable to find numeric literal operator 'operator""o10'
cout
で各進法で表示したければ
マニピュレーターを使います。
std::showbase
で0
や0x
の表示をし、std::oct
やstd::hex
で進法を指定します。
prefixが必要ないならstd::showbase
なしで。(noshowbase
で元に戻せします。)
2進法ではhex
やoct
の様なマニピュレーターは無いのでからりにbitset
を使い変換し、
必要であれば0b
などは自分で付ける必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
出力は
10
012
0xa
0b1010
12
a
となります。