Help us understand the problem. What is going on with this article?

【format】引数でよく見かける'%010d'の実態を解明[Ruby]

More than 1 year has passed since last update.

はじめに

Ruby on Railsで開発している中で、数値が動的に変化するために桁数が不統一になり、実装に悩むことがございました。そんな時、formatメソッドに出会い、formatメソッドが開発を大いに助けてくれました。ですので、その時に得た知識を備忘録として記録しておきます。

format(= sprintf)メソッドとは

formatメソッドは、フォーマットに従って文字列を生成(整形)し、オブジェクトとして返します
formatメソッドを使えば、例えば数字を返す時に10進数だけでなく、8進数や16進数で表示させたり、小数点の場合、何桁まで表示させるか、といった指定を簡単に行うことができます。
sprintfメソッドもよく見かけますが、こちらはformatメソッドの別名です(逆も然り)。
sprintfstring print formattedの略称であることを知っていると理解の助けになるかと思います。

printfメソッドとは

printfメソッドの実態はformat(sprintf)メソッドと基本的には変わりありません。
違いは、format(sprintf)メソッドが整形した文字列をオブジェクトとして返すのに対し、printfメソッドはコンソールに出力する点です。
また、printfprint formattedの略称です。

時間がない方は

記事の最後に'%010d'の実態を明かしていますので飛ばしてお読みいただいて構いません。

指示子

フォーマットの基本は%指示子の形式です。指示子によって与えられたデータをどのように整形するかが決められます。

%d(or %i)

整数を10進数で表現できます。

最初の引数は文字列で、その中で%文字という形式でどのように整形するかを指定し、それ以降の引数でフォーマット中の%文字に対応する値を順番に指定します。
また、文字列 % 配列の形式でも指定することができます。

例で確認した方がわかりやすいので、いくつか載せておきます。

$ irb
irb(main):001:0> format('第二引数は%dです', 7)
=> "第二引数は7です"
irb(main):002:0> format('%d/%d/%d' % [2018, 12, 21])
=> "2018/12/21"

ここで、フォーマットのフラグというものについて軽く記述します。
フラグは「%」と指示子の間に指定することができるものです。

フラグには、#-+空白0の5種類があります。
1. # : %b,%B(2進数表現)、%o(8進数表現)、%x,%X(16進数表現)についてリテラル表現と同じプレフィックス(0b,0B,0,0x,0X)を出力
2. - : 幅を指定した場合に出力を左寄せする
3. + : 「+」か「-」の符号を出力する
4. 空白 : 負の数の時のみ符号「-」を出力する
5. 0 : 最小幅を指定する際に余った桁を空白ではなく「0」で埋める

irb(main):003:0> format('%d', 7)
=> "7"
irb(main):004:0> format('%#d', 7)
=> "7"
irb(main):005:0> format('%-d', 7)
=> "7"
irb(main):006:0> format('%+d', 7)
=> "+7"
irb(main):007:0> format('% d', 7)
=> " 7"
irb(main):008:0> format('%0d', 7)
=> "7"

フラグの効果の違いがわかりづらいですね。
次の例もみて確認しましょう。5の最小幅(出力の最小の桁数)を指定して、フラグの挙動の違いを再度みていきます(5という数字に意味はありません)。

irb(main):009:0> format('%5d', 7)
=> "    7"
irb(main):010:0> format('%#5d', 7)
=> "    7"
irb(main):011:0> format('%-5d', 7)
=> "7    "
irb(main):012:0> format('%+5d', 7)
=> "   +7"
irb(main):013:0> format('% 5d', 7)
=> "    7"
irb(main):014:0> format('%05d', 7)
=> "00007"

上の#の挙動は依然わかりづらいですね。というのも10進数(%d)表現しているため、今回は#を指定していないと同然だからです。

%b(or %B)

整数を2進数で表現できます。

irb(main):001:0> format('%b', 7)
=> "111"
irb(main):002:0> format('%#b', 7)
=> "0b111"
irb(main):003:0> format('%-b', 7)
=> "111"
irb(main):004:0> format('%+b', 7)
=> "+111"
irb(main):005:0> format('% b', 7)
=> " 111"
irb(main):006:0> format('%0b', 7)
=> "111"

次は、先程と同様、5の最小幅を指定してみていきましょう。

irb(main):007:0> format('%5b', 7)
=> "  111"
irb(main):008:0> format('%#5b', 7)
=> "0b111"
irb(main):009:0> format('%-5b', 7)
=> "111  "
irb(main):010:0> format('%+5b', 7)
=> " +111"
irb(main):011:0> format('% 5b', 7)
=> "  111"
irb(main):012:0> format('%05b', 7)
=> "00111"

これで#の挙動が確認できました。リテラル表現と同じプレフィックス「0b」が出力されています。

%o

整数を8進数で表現できます。

$ irb
irb(main):001:0> format('%5o', 9)
=> "   11"
irb(main):002:0> format('%#5o', 9)
=> "  011"
irb(main):003:0> format('%-5o', 9)
=> "11   "
irb(main):004:0> format('%+5o', 9)
=> "  +11"
irb(main):005:0> format('% 5o', 9)
=> "   11"
irb(main):006:0> format('%05o', 9)
=> "00011"

%x(or %X)

整数を16進数で表現できます。

$ irb
irb(main):001:0> format('%5x', 15)
=> "    f"
irb(main):002:0> format('%#5x', 15)
=> "  0xf"
irb(main):003:0> format('%-5x', 15)
=> "f    "
irb(main):004:0> format('%+5x', 15)
=> "   +f"
irb(main):005:0> format('% 5x', 15)
=> "    f"
irb(main):006:0> format('%05x', 15)
=> "0000f"

一応、16についても確認します。

irb(main):007:0> format('%5X', 16)
=> "   10"
irb(main):008:0> format('%#5X', 16)
=> " 0X10"
irb(main):009:0> format('%-5X', 16)
=> "10   "
irb(main):010:0> format('%+5X', 16)
=> "  +10"
irb(main):011:0> format('% 5X', 16)
=> "   10"
irb(main):012:0> format('%05X', 16)
=> "00010"

%s

文字列を生成します。引数.to_sを呼びます。

$ irb
irb(main):001:0> format('Hello, %s!', 'takuyanin')
=> "Hello, takuyanin!"
irb(main):002:0> "Hello, takuyanin version#{format('%d', 2)}"
=> "Hello, takuyanin version2"

上記のように、formatメソッドは式展開でも使うことができます。

ここで、精度について取り上げます。
%s%pに対して精度.を適用すると、最大桁数を指定することができます。

irb(main):003:0> format('%s', 'takuyanin')
=> "takuyanin"
irb(main):004:0> format('%.s', 'takuyanin')
=> ""
irb(main):005:0> format('%.1s', 'takuyanin')
=> "t"
irb(main):006:0> format('%.5s', 'takuyanin')
=> "takuy"
irb(main):007:0> format('%.10s', 'takuyanin')
=> "takuyanin"
irb(main):008:0> format('%10s', 'takuyanin')
=> " takuyanin"

%p

pメソッドと同じ形式で出力します。引数.inspectを呼びます。

$ irb
irb(main):001:0> format('%p', 'takuyanin')
=> "\"takuyanin\""

%f

浮動小数点数を出力します。

指示子が%fの時は、精度は小数点以下の桁数を示します

$ irb
irb(main):001:0> format('%f', Math::PI)
=> "3.141593"
irb(main):002:0> format('%.f', Math::PI)
=> "3"
irb(main):003:0> format('%.1f', Math::PI)
=> "3.1"
irb(main):004:0> format('%.5f', Math::PI)
=> "3.14159"
irb(main):005:0> format('%5f', Math::PI)
=> "3.141593"

上記の%fの例では、一番最初と最後の出力が同じですね。
一応こちらを説明すると、最後の%5f最小幅を指定しているにすぎないためです。

format('%010d', 数値)とは

もうお分かりですね。これは、数値(n)をformatメソッドで桁数を10に揃えているということになります(10進数表現)。

$ irb
irb(main):001:0> n = 7
=> 7
irb(main):002:0> format('%010d', n)
=> "0000000007"

おわりに

formatメソッドって素晴らしいですね。
知っているか知らないかで考えつくアルゴリズムが変わってくるのではと感じました。
時折、復習したいと思います。

Rails, Rubyに関していくつか記事をまとめてますので、takuyaninのマイページもご訪問ください。

少しでも役に立ったいう方は、いいね、お願いします(^^)

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away