Ruby のフォーマット文字列のマイナーな機能の紹介

C言語に関する [printf のマイナーな機能の紹介
]( https://qiita.com/Nabetani/items/b18032a13359df22b50d ) の ruby 版。

[module function Kernel.#format]( https://docs.ruby-lang.org/ja/latest/method/Kernel/m/format.html ) に書いてあることのうち、私がよく知らなかったことを中心に。

# 桁区切り文字を出力するフラグ文字は、無い

上記のサイトに

> sprintf のすべての方言をサ ポートしていないこと(%': 3桁区切り)など

と、無いことが明言されている。残念。

# `%c`

上記のサイトに

> 引数の数値(0〜255)を文字コードとみなして対応する文字を出力します。

とあるが、255 よりも大きい値も受け入れてくれる。

```ruby:ruby2.5
puts( "%c" % 12354 )
#=> あ
```

あと。負の値の動きが怪しい。

```ruby:ruby2.5
puts( ("%c" % -1).inspect )
#=> "\xFF"
puts( ("%c" % -2).inspect )
#=> "\xFE"
puts( ("%c" % -3).inspect )
#=> ArgumentError: invalid character
```

なんで？

# `%p`

> Object#inspect の結果を出力します。

とある。知らなかった。

```ruby:ruby2.5
puts( "%p %p %p" % ["hoge", :fuga, {piyo:0}] )
#=> "hoge" :fuga {:piyo=>0}
```

# `%d`

> 引数が整数でなければ関数 Kernel.#Integer と同じ規則で整数に 変換されます。

とある。
つまり、文字列なども適当に整数にされる。知らなかった。

```ruby:ruby2.5
puts( "%d %d %d %d" % [ "\t-3\t", "077", "0xa", Time.now ] )
#=> -3 63 10 1545721423
```
# `%u`

> 引数の数値を符号なし整数とみなして10進表現の整数として出力します。

とあるが、`%d` との違いがわからない。

```ruby:ruby2.5
puts( "%u %u" % [ -100, "-100"] )
#=> -100 -100
```

なにがどう符号なし整数なのか。

# `%O` や `%F` は無い

`%o` と `%f` はあるけど、`%O` と `%F` は無い。

```ruby:ruby2.5
puts( "%o" % 63 )
#=> 77
puts( "%O" % 63 )
#=> ArgumentError: malformed format string - %O
puts( "%f" % 63.0 )
#=> 63.000000
puts( "%F" % 63.0 ) 
#=> ArgumentError: malformed format string - %F
```

両方受け入れそうなもんだけど、大文字はエラー。

ruby の書式文字列で指示子に使える文字を列挙すると、

```text
aAbBcdeEfgGiopsuxX
```

となる。意外と大文字は使えない。

# `%f`, `%e`, `%E`, `%g`, `%G` はシステム依存

> f, e, E, g, G に関しては sprintf(3) の結果を利用しています。従って出力結果は 実際にはシステムに依存することになります。

とあるので、OSやらに依存するらしい。手元( macOS, ruby 2.5.3p105 (略) [x86_64-darwin18] )ではこんな感じ：

```ruby:ruby2.5
puts("%f %g %G %e %E %a %A" % ([Float::INFINITY]*7))
#=> Inf Inf Inf Inf Inf Inf Inf
puts("%f %g %G %e %E %a %A" % ([Float::NAN]*7))
#=> NaN NaN NaN NaN NaN NaN NaN
```

前述のサイトとは出力が異なるね。

ところで。
OS付属の `sprintf` はロケールによって、小数点がコンマになったりするけど、そのあたりはどうなってるんだろう。
ロケールを変える方法がわからなかった。

# `%f`, `%g`, `%G`, `%e`, `%E`, `%a`, `%A` は、`to_f` を呼ぶ

上記サイトには書かれていないけど、`%f` に `Float` 以外を渡すと `to_f` が呼ばれる。

こんな具合。

```ruby:ruby2.5
class Hoge
  def to_f
    print "Hoge#to_f : "
    return 1.0
  end
end

puts( "%f" % Hoge.new )
#=> Hoge#to_f : 1.000000
```

`BigDecimal` と併用すると、罠っぽい挙動になる。

```ruby:ruby2.5
require "bigdecimal"
puts( "%.20f" % BigDecimal.new( "0.1" ) )
#=> 0.10000000000000000555
```

`BigDecimal` の 0.1 が出力されているのではなく、それを `to_f` した値が出力される。`to_f` されているので誤差がある。

# 16進数の浮動小数点変換指示子 `%a`

C言語の `printf` にも導入されている 16進数浮動小数点。ruby にもある。

```ruby:ruby2.5
puts( "%a %A" % [0.1, 0.1] )
#=> 0x1.999999999999ap-4 0X1.999999999999AP-4
```

この文字列を `Float` に戻すには、`String#scanf` を使う。

```ruby:ruby2.5
require 'scanf'
p "0x1.999999999999ap-4".scanf("%a")
# => [0.1]
```

知らなかった。
ちなみに、`%a` で書いたけど、 `%f` でもいいし、なんと `%F` でもいい。

# `%<name>`

> Symbol のインスタンス name をキーとする Hash を引数にした場合、 対応する値をフォーマットして参照します。

とのこと。

```ruby:ruby2.5
puts("%<hoge>d %<fuga>s" % { hoge:123, fuga:"foo" } )
#=> 123 foo
```

という具合。便利な局面もあるかも。

# `%{name}`

一方よく意味がわからないのが `%{name}` 。

> 対応する値をフォーマットせずに参照します。 幅や精度を指定するために使用することができます。

とのこと。

```ruby:ruby2.5
puts("%{hoge}d %{fuga}s" % { hoge:123, fuga:"foo" } )
#=> 123d foos
```

なんに使うんだろうこれ。「幅や精度を指定するため」とあるけど、わからない。