Comment detail

printfの自作 (Nested Flatten)

Rubyのsprintfの仕様に従って実装しました. pフラグは仕様が不明瞭だったので実装しませんでした.

format系のメソッドは仕様不可ということで, 指数表示変換は自前で容易したのでバグが潜在している可能性が高いです.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#%[フラグ][最小フィールド幅][.精度][長さ修飾子]変換指定子
MATCH_STR = '%([\+\-\s#0\*]*)(\d*)((?:\.\d+)?)([hliztl]*)([\w%]*)'
def mysprintf(*argv)
  def check_flag(str, flag, width, precision)
    str = str.to_s
    width = width.to_i
    pos = flag.include?('-')
    sign = (/\A([\-\+])\w+\z/ =~ str or flag.include?('+'))
    zero = flag.include?('0')
    w = (width.to_i > str.size ? width-str.size : 0)
    if sign
      s = (str.to_i.abs >= 0 ? '+' : '-')
      w = (w >= 1 ? w - 1 : 0)
    else
      s = ''
    end
    num = (/\.(\d+)/ =~ precision and $1.to_i <= w and $1.to_i > 0) ? $i.to_i+1 : 0
    filler = (zero ? '0' : ' ')*(w-num) + '0'*num
    "#{pos ? '' : filler}#{s}#{str}#{pos ? filler : ''}"
  end
  def check_spec(str, spec, opt)
    def exp_expr(n) #有理数を指数表示に変換
      i = 0
      break if eval("10**#{n.abs > 1 ? '1' : '0'}*10**(i#{n.abs > 1 ? '+= 1) >' : '-= 1) <='}#{'-1*' if n < 0}n") while 1
      "#{n.quo(10**(i+1))}e#{i+1}"
    end
    def e_flag(str)
      if str.include?('e')
        if str.include('.')
          str
        else
          "#{str.split('e')[0]}.e#{str.split('e')[1]}"
        end
      else
        exp_expr(str.to_f)
      end
    end
    def g_flag(str)
      str.to_f < 10e-3 ? e_flag(str) : str.to_f.to_s
    end
    if opt
      case spec
      when 'b': "0b#{str.to_i.to_s(2)}"
      when 'o': "0#{str.to_i.oct}"
      when 'x': "0x#{str.to_i.hex}"
      when 'X': "0x#{str.to_i.hex}".upcase
      when 'f': str.include('.') ? str : "#{str}."
      when 'F': (str.include('.') ? str : "#{str}.").upcase
      when 'e': e_flag(str)
      when 'E': e_flag(str).upcase
      when 'g': g_flag(str)
      when 'G': g_flag(str).upcase
      else
      end
    else
      case spec
      when 'd': str.to_i
      when 'i': str.to_i
      when 'u': (str.to_i < 0 ? 2**32 + str.to_i : str.to_i)
      when 'o': str.to_i.oct
      when 'x': str.to_i.hex
      when 'X': str.to_i.hex.to_s.upcase
      when 'e': e_flag(str)
      when 'E': e_flag(str).upcase
      when 'f': str.to_f
      when 'F': str.to_f.to_s.upcase
      when 'g': g_flag(str)
      when 'G': g_flag(str).upcase
      when 'c': str[0].chr
      when 's': str.to_s
      when '%': '%'
      else
        raise ArgumentError.new("malformed format string - #{spec}")
      end
    end
  end
  f = argv[0].match(/\A#{MATCH_STR}\z/)[1..-1]
  if f[0].include?('*')
    str = argv[2].to_s
    width = argv[1]
  else
    str = argv[1].to_s
    width = f[1]
  end
  if /\A[di]\z/ =~ f[4] and str.to_f != str.to_i
    raise ArgumentError.new("invalid value for Integer: #{str}")
  end
  flag = f[0]
  prec = f[2]
  spec = f[4]
  opt = f[0].include?('#')
  check_flag(check_spec(str, spec, opt), flag, width, prec)
end
p mysprintf("%%", "testtest") # => "%"
p sprintf("%%", "testtest")   # => "%"
p mysprintf("%5.0u", '-1')    # => "4294967295"
p sprintf("%5.0u", '-1')      # => "4294967295"
p mysprintf("% 2d", "1", '2') # => " 1"
p sprintf("% 2d", "1", '2')   # => " 1"

Index

Feed

Other

Link

Pathtraq

loading...