Class: String

Inherits:
Object show all
Includes:
Comparable
Defined in:
ext/enterprise_script_service/mruby/mrblib/string.rb,
ext/enterprise_script_service/mruby/mrblib/string.rb,
ext/enterprise_script_service/mruby/lib/mruby-core-ext.rb,
ext/enterprise_script_service/mruby-mpdecimal/mrblib/mpdecimal.rb,
ext/enterprise_script_service/mruby/mrbgems/mruby-sprintf/mrblib/string.rb,
ext/enterprise_script_service/mruby/mrbgems/mruby-string-ext/mrblib/string.rb

Overview

String

ISO 15.2.10

Instance Method Summary collapse

Methods included from Comparable

#<, #<=, #==, #>, #>=, #between?, #clamp

Instance Method Details

#%(args) ⇒ Object



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'ext/enterprise_script_service/mruby/lib/mruby-core-ext.rb', line 22

def %(params)
  if params.is_a?(Hash)
    str = self.clone
    params.each do |k, v|
      str.gsub!("%{#{k}}") { v }
    end
    str
  else
    if params.is_a?(Array)
      sprintf(self, *params)
    else
      sprintf(self, params)
    end
  end
end

#=~(re) ⇒ Object

ISO 15.2.10.5.3



247
248
249
# File 'ext/enterprise_script_service/mruby/mrblib/string.rb', line 247

def =~(re)
  re =~ self
end

#[]=(*args) ⇒ Object

Modify self by replacing the content of self. The portion of the string affected is determined using the same criteria as String#[].



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'ext/enterprise_script_service/mruby/mrblib/string.rb', line 195

def []=(*args)
  anum = args.size
  if anum == 2
    pos, value = args
    case pos
    when String
      posnum = self.index(pos)
      if posnum
        b = self[0, posnum.to_i]
        a = self[(posnum + pos.length)..-1]
        self.replace([b, value, a].join(''))
      else
        raise IndexError, "string not matched"
      end
    when Range
      head = pos.begin
      tail = pos.end
      tail += self.length if tail < 0
      unless pos.exclude_end?
        tail += 1
      end
      return self[head, tail-head]=value
    else
      pos += self.length if pos < 0
      if pos < 0 || pos > self.length
        raise IndexError, "index #{args[0]} out of string"
      end
      b = self[0, pos.to_i]
      a = self[pos + 1..-1]
      self.replace([b, value, a].join(''))
    end
    return value
  elsif anum == 3
    pos, len, value = args
    pos += self.length if pos < 0
    if pos < 0 || pos > self.length
      raise IndexError, "index #{args[0]} out of string"
    end
    if len < 0
      raise IndexError, "negative length #{len}"
    end
    b = self[0, pos.to_i]
    a = self[pos + len..-1]
    self.replace([b, value, a].join(''))
    return value
  else
    raise ArgumentError, "wrong number of arguments (#{anum} for 2..3)"
  end
end

#__sub_replace(pre, m, post) ⇒ Object

private method for gsub/sub



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'ext/enterprise_script_service/mruby/mrblib/string.rb', line 28

def __sub_replace(pre, m, post)
  s = ""
  i = 0
  while j = index("\\", i)
    break if j == length-1
    t = case self[j+1]
        when "\\"
          "\\"
        when "`"
          pre
        when "&", "0"
          m
        when "'"
          post
        when "1", "2", "3", "4", "5", "6", "7", "8", "9"
          ""
        else
          self[j, 2]
        end
    s += self[i, j-i] + t
    i = j + 2
  end
  s + self[i, length-i]
end

#casecmp(str) ⇒ Object

call-seq:

str.casecmp(other_str)   -> -1, 0, +1 or nil

Case-insensitive version of String#<=>.

"abcdef".casecmp("abcde")     #=> 1
"aBcDeF".casecmp("abcdef")    #=> 0
"abcdef".casecmp("abcdefg")   #=> -1
"abcdef".casecmp("ABCDEF")    #=> 0


125
126
127
128
129
# File 'ext/enterprise_script_service/mruby/mrbgems/mruby-string-ext/mrblib/string.rb', line 125

def casecmp(str)
  self.downcase <=> str.__to_str.downcase
rescue NoMethodError
  nil
end

#casecmp?(str) ⇒ Boolean

call-seq:

str.casecmp?(other)  -> true, false, or nil

Returns true if str and other_str are equal after case folding, false if they are not equal, and nil if other_str is not a string.

Returns:

  • (Boolean)


138
139
140
141
142
# File 'ext/enterprise_script_service/mruby/mrbgems/mruby-string-ext/mrblib/string.rb', line 138

def casecmp?(str)
  c = self.casecmp(str)
  return nil if c.nil?
  return c == 0
end

#chars(&block) ⇒ Object



302
303
304
305
306
307
308
309
310
311
# File 'ext/enterprise_script_service/mruby/mrbgems/mruby-string-ext/mrblib/string.rb', line 302

def chars(&block)
  if block_given?
    self.split('').each do |i|
      block.call(i)
    end
    self
  else
    self.split('')
  end
end

#clearObject

call-seq:

string.clear    ->  string

Makes string empty.

a = "abcde"
a.clear    #=> ""


12
13
14
# File 'ext/enterprise_script_service/mruby/mrbgems/mruby-string-ext/mrblib/string.rb', line 12

def clear
  self.replace("")
end

#codepoints(&block) ⇒ Object Also known as: each_codepoint



322
323
324
325
326
327
328
329
330
331
332
333
# File 'ext/enterprise_script_service/mruby/mrbgems/mruby-string-ext/mrblib/string.rb', line 322

def codepoints(&block)
  len = self.size

  if block_given?
    self.split('').each do|x|
      block.call(x.ord)
    end
    self
  else
    self.split('').map{|x| x.ord}
  end
end

#each_byte(&block) ⇒ Object

Call the given block for each byte of self.



182
183
184
185
186
187
188
189
190
# File 'ext/enterprise_script_service/mruby/mrblib/string.rb', line 182

def each_byte(&block)
  bytes = self.bytes
  pos = 0
  while pos < bytes.size
    block.call(bytes[pos])
    pos += 1
  end
  self
end

#each_char(&block) ⇒ Object

Call the given block for each character of self.



171
172
173
174
175
176
177
178
# File 'ext/enterprise_script_service/mruby/mrblib/string.rb', line 171

def each_char(&block)
  pos = 0
  while pos < self.size
    block.call(self[pos])
    pos += 1
  end
  self
end

#each_line(rs = "\n", &block) ⇒ Object

Calls the given block for each line and pass the respective line.

ISO 15.2.10.5.15



12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'ext/enterprise_script_service/mruby/mrblib/string.rb', line 12

def each_line(rs = "\n", &block)
  return to_enum(:each_line, rs, &block) unless block
  return block.call(self) if rs.nil?
  rs.__to_str
  offset = 0
  rs_len = rs.length
  this = dup
  while pos = this.index(rs, offset)
    block.call(this[offset, pos + rs_len - offset])
    offset = pos + rs_len
  end
  block.call(this[offset, this.size - offset]) if this.size > offset
  self
end

#gsub(*args, &block) ⇒ Object

Replace all matches of pattern with replacement. Call block (if given) for each match and replace pattern with the value of the block. Return the final value.

ISO 15.2.10.5.18

Raises:



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
# File 'ext/enterprise_script_service/mruby/mrblib/string.rb', line 60

def gsub(*args, &block)
  return to_enum(:gsub, *args) if args.length == 1 && !block
  raise ArgumentError, "wrong number of arguments" unless (1..2).include?(args.length)

  pattern, replace = *args
  plen = pattern.length
  if args.length == 2 && block
    block = nil
  end
  if !replace.nil? || !block
    replace.__to_str
  end
  offset = 0
  result = []
  while found = index(pattern, offset)
    result << self[offset, found - offset]
    offset = found + plen
    result << if block
      block.call(pattern).to_s
    else
      replace.__sub_replace(self[0, found], pattern, self[offset..-1] || "")
    end
    if plen == 0
      result << self[offset, 1]
      offset += 1
    end
  end
  result << self[offset..-1] if offset < length
  result.join
end

#gsub!(*args, &block) ⇒ Object

Replace all matches of pattern with replacement. Call block (if given) for each match and replace pattern with the value of the block. Modify self with the final value.

ISO 15.2.10.5.19

Raises:



98
99
100
101
102
103
104
# File 'ext/enterprise_script_service/mruby/mrblib/string.rb', line 98

def gsub!(*args, &block)
  raise FrozenError, "can't modify frozen String" if frozen?
  return to_enum(:gsub!, *args) if args.length == 1 && !block
  str = self.gsub(*args, &block)
  return nil unless self.index(args[0])
  self.replace(str)
end

#insert(idx, str) ⇒ Object

call-seq:

   str.insert(index, other_str)   -> str

Inserts <i>other_str</i> before the character at the given
<i>index</i>, modifying <i>str</i>. Negative indices count from the
end of the string, and insert <em>after</em> the given character.
The intent is insert <i>aString</i> so that it starts at the given
<i>index</i>.

   "abcd".insert(0, 'X')    #=> "Xabcd"
   "abcd".insert(3, 'X')    #=> "abcXd"
   "abcd".insert(4, 'X')    #=> "abcdX"
   "abcd".insert(-3, 'X')   #=> "abXcd"
   "abcd".insert(-1, 'X')   #=> "abcdX"


254
255
256
257
258
259
260
261
262
# File 'ext/enterprise_script_service/mruby/mrbgems/mruby-string-ext/mrblib/string.rb', line 254

def insert(idx, str)
  if idx == -1
    return self << str
  elsif idx < 0
    idx += 1
  end
  self[idx, 0] = str
  self
end

#lines(&blk) ⇒ Object

call-seq:

  string.lines                ->  array of string
  string.lines {|s| block}    ->  array of string

Returns strings per line;

  a = "abc\ndef"
  a.lines    #=> ["abc\n", "def"]

If a block is given, it works the same as <code>each_line</code>.


361
362
363
364
365
366
367
368
369
# File 'ext/enterprise_script_service/mruby/mrbgems/mruby-string-ext/mrblib/string.rb', line 361

def lines(&blk)
  lines = self.__lines
  if blk
    lines.each do |line|
      blk.call(line)
    end
  end
  lines
end

#ljust(idx, padstr = ' ') ⇒ Object

call-seq:

   str.ljust(integer, padstr=' ')   -> new_str

If <i>integer</i> is greater than the length of <i>str</i>, returns a new
<code>String</code> of length <i>integer</i> with <i>str</i> left justified
and padded with <i>padstr</i>; otherwise, returns <i>str</i>.

   "hello".ljust(4)            #=> "hello"
   "hello".ljust(20)           #=> "hello               "
   "hello".ljust(20, '1234')   #=> "hello123412341234123"

Raises:



275
276
277
278
279
280
281
# File 'ext/enterprise_script_service/mruby/mrbgems/mruby-string-ext/mrblib/string.rb', line 275

def ljust(idx, padstr = ' ')
  raise ArgumentError, 'zero width padding' if padstr == ''
  return self if idx <= self.size
  pad_repetitions = (idx / padstr.length).ceil
  padding = (padstr * pad_repetitions)[0...(idx - self.length)]
  self + padding
end

#lstripObject

call-seq:

str.lstrip   -> new_str

Returns a copy of str with leading whitespace removed. See also String#rstrip and String#strip.

"  hello  ".lstrip   #=> "hello  "
"hello".lstrip       #=> "hello"


26
27
28
29
30
31
# File 'ext/enterprise_script_service/mruby/mrbgems/mruby-string-ext/mrblib/string.rb', line 26

def lstrip
  a = 0
  z = self.size - 1
  a += 1 while a <= z and " \f\n\r\t\v".include?(self[a])
  (z >= 0) ? self[a..z] : ""
end

#lstrip!Object

call-seq:

str.lstrip!   -> self or nil

Removes leading whitespace from str, returning nil if no change was made. See also String#rstrip! and String#strip!.

"  hello  ".lstrip   #=> "hello  "
"hello".lstrip!      #=> nil

Raises:



78
79
80
81
82
# File 'ext/enterprise_script_service/mruby/mrbgems/mruby-string-ext/mrblib/string.rb', line 78

def lstrip!
  raise FrozenError, "can't modify frozen String" if frozen?
  s = self.lstrip
  (s == self) ? nil : self.replace(s)
end

#match(re, &block) ⇒ Object

ISO 15.2.10.5.27



253
254
255
256
257
258
259
260
261
262
263
264
# File 'ext/enterprise_script_service/mruby/mrblib/string.rb', line 253

def match(re, &block)
  if String === re
    if Object.const_defined?(:Regexp)
      r = Regexp.new(re)
      r.match(self, &block)
    else
      raise NotImplementedError, "String#match needs Regexp class"
    end
  else
    re.match(self, &block)
  end
end

#partition(sep) ⇒ Object

Raises:



144
145
146
147
148
149
150
151
152
153
# File 'ext/enterprise_script_service/mruby/mrbgems/mruby-string-ext/mrblib/string.rb', line 144

def partition(sep)
  raise TypeError, "type mismatch: #{sep.class} given" unless sep.is_a? String
  n = index(sep)
  unless n.nil?
    m = n + sep.size
    [ slice(0, n), sep, slice(m, size - m) ]
  else
    [ self, "", "" ]
  end
end

#prepend(arg) ⇒ Object

call-seq:

str.prepend(other_str)  -> str

Prepend—Prepend the given string to str.

a = "world"
a.prepend("hello ") #=> "hello world"
a                   #=> "hello world"


345
346
347
348
# File 'ext/enterprise_script_service/mruby/mrbgems/mruby-string-ext/mrblib/string.rb', line 345

def prepend(arg)
  self[0, 0] = arg
  self
end

#relative_pathObject



16
17
18
# File 'ext/enterprise_script_service/mruby/lib/mruby-core-ext.rb', line 16

def relative_path
  relative_path_from(Dir.pwd)
end

#relative_path_from(dir) ⇒ Object



12
13
14
# File 'ext/enterprise_script_service/mruby/lib/mruby-core-ext.rb', line 12

def relative_path_from(dir)
  Pathname.new(File.expand_path(self)).relative_path_from(Pathname.new(File.expand_path(dir))).to_s
end

#rjust(idx, padstr = ' ') ⇒ Object

call-seq:

   str.rjust(integer, padstr=' ')   -> new_str

If <i>integer</i> is greater than the length of <i>str</i>, returns a new
<code>String</code> of length <i>integer</i> with <i>str</i> right justified
and padded with <i>padstr</i>; otherwise, returns <i>str</i>.

   "hello".rjust(4)            #=> "hello"
   "hello".rjust(20)           #=> "               hello"
   "hello".rjust(20, '1234')   #=> "123412341234123hello"

Raises:



294
295
296
297
298
299
300
# File 'ext/enterprise_script_service/mruby/mrbgems/mruby-string-ext/mrblib/string.rb', line 294

def rjust(idx, padstr = ' ')
  raise ArgumentError, 'zero width padding' if padstr == ''
  return self if idx <= self.size
  pad_repetitions = (idx / padstr.length).ceil
  padding = (padstr * pad_repetitions)[0...(idx - self.length)]
  padding + self
end

#rpartition(sep) ⇒ Object

Raises:



155
156
157
158
159
160
161
162
163
164
# File 'ext/enterprise_script_service/mruby/mrbgems/mruby-string-ext/mrblib/string.rb', line 155

def rpartition(sep)
  raise TypeError, "type mismatch: #{sep.class} given" unless sep.is_a? String
  n = rindex(sep)
  unless n.nil?
    m = n + sep.size
    [ slice(0, n), sep, slice(m, size - m) ]
  else
    [ "", "", self ]
  end
end

#rstripObject

call-seq:

str.rstrip   -> new_str

Returns a copy of str with trailing whitespace removed. See also String#lstrip and String#strip.

"  hello  ".rstrip   #=> "  hello"
"hello".rstrip       #=> "hello"


43
44
45
46
47
48
# File 'ext/enterprise_script_service/mruby/mrbgems/mruby-string-ext/mrblib/string.rb', line 43

def rstrip
  a = 0
  z = self.size - 1
  z -= 1 while a <= z and " \f\n\r\t\v\0".include?(self[z])
  (z >= 0) ? self[a..z] : ""
end

#rstrip!Object

call-seq:

str.rstrip!   -> self or nil

Removes trailing whitespace from str, returning nil if no change was made. See also String#lstrip! and String#strip!.

"  hello  ".rstrip   #=> "  hello"
"hello".rstrip!      #=> nil

Raises:



95
96
97
98
99
# File 'ext/enterprise_script_service/mruby/mrbgems/mruby-string-ext/mrblib/string.rb', line 95

def rstrip!
  raise FrozenError, "can't modify frozen String" if frozen?
  s = self.rstrip
  (s == self) ? nil : self.replace(s)
end

#scan(reg, &block) ⇒ Object

Calls the given block for each match of pattern If no block is given return an array with all matches of pattern.

ISO 15.2.10.5.32



112
113
114
115
116
117
# File 'ext/enterprise_script_service/mruby/mrblib/string.rb', line 112

def scan(reg, &block)
  ### *** TODO *** ###
  unless Object.const_defined?(:Regexp)
    raise NotImplementedError, "scan not available (yet)"
  end
end

#slice!(arg1, arg2 = nil) ⇒ Object

call-seq:

str.slice!(fixnum)           -> new_str or nil
str.slice!(fixnum, fixnum)   -> new_str or nil
str.slice!(range)            -> new_str or nil
str.slice!(other_str)        -> new_str or nil

Deletes the specified portion from str, and returns the portion deleted.

string = "this is a string"
string.slice!(2)        #=> "i"
string.slice!(3..6)     #=> " is "
string.slice!("r")      #=> "r"
string                  #=> "thsa sting"

Raises:



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'ext/enterprise_script_service/mruby/mrbgems/mruby-string-ext/mrblib/string.rb', line 182

def slice!(arg1, arg2=nil)
  raise FrozenError, "can't modify frozen String" if frozen?
  raise "wrong number of arguments (for 1..2)" if arg1.nil? && arg2.nil?

  if !arg1.nil? && !arg2.nil?
    idx = arg1
    idx += self.size if arg1 < 0
    if idx >= 0 && idx <= self.size && arg2 > 0
      str = self[idx, arg2]
    else
      return nil
    end
  else
    validated = false
    if arg1.kind_of?(Range)
      beg = arg1.begin
      ed = arg1.end
      beg += self.size if beg < 0
      ed += self.size if ed < 0
      ed -= 1 if arg1.exclude_end?
      validated = true
    elsif arg1.kind_of?(String)
      validated = true
    else
      idx = arg1
      idx += self.size if arg1 < 0
      validated = true if idx >=0 && arg1 < self.size
    end
    if validated
      str = self[arg1]
    else
      return nil
    end
  end
  unless str.nil? || str == ""
    if !arg1.nil? && !arg2.nil?
      idx = arg1 >= 0 ? arg1 : self.size+arg1
      str2 = self[0...idx] + self[idx+arg2..-1].to_s
    else
      if arg1.kind_of?(Range)
        idx = beg >= 0 ? beg : self.size+beg
        idx2 = ed>= 0 ? ed : self.size+ed
        str2 = self[0...idx] + self[idx2+1..-1].to_s
      elsif arg1.kind_of?(String)
        idx = self.index(arg1)
        str2 = self[0...idx] + self[idx+arg1.size..-1] unless idx.nil?
      else
        idx = arg1 >= 0 ? arg1 : self.size+arg1
        str2 = self[0...idx] + self[idx+1..-1].to_s
      end
    end
    self.replace(str2) unless str2.nil?
  end
  str
end

#stripObject

call-seq:

str.strip   -> new_str

Returns a copy of str with leading and trailing whitespace removed.

"    hello    ".strip   #=> "hello"
"\tgoodbye\r\n".strip   #=> "goodbye"


59
60
61
62
63
64
65
# File 'ext/enterprise_script_service/mruby/mrbgems/mruby-string-ext/mrblib/string.rb', line 59

def strip
  a = 0
  z = self.size - 1
  a += 1 while a <= z and " \f\n\r\t\v".include?(self[a])
  z -= 1 while a <= z and " \f\n\r\t\v\0".include?(self[z])
  (z >= 0) ? self[a..z] : ""
end

#strip!Object

call-seq:

   str.strip!   -> str or nil

Removes leading and trailing whitespace from <i>str</i>. Returns
<code>nil</code> if <i>str</i> was not altered.

Raises:



108
109
110
111
112
# File 'ext/enterprise_script_service/mruby/mrbgems/mruby-string-ext/mrblib/string.rb', line 108

def strip!
  raise FrozenError, "can't modify frozen String" if frozen?
  s = self.strip
  (s == self) ? nil : self.replace(s)
end

#sub(*args, &block) ⇒ Object

Replace only the first match of pattern with replacement. Call block (if given) for each match and replace pattern with the value of the block. Return the final value.

ISO 15.2.10.5.36



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'ext/enterprise_script_service/mruby/mrblib/string.rb', line 126

def sub(*args, &block)
  unless (1..2).include?(args.length)
    raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 2)"
  end

  pattern, replace = *args
  pattern.__to_str
  if args.length == 2 && block
    block = nil
  end
  unless block
    replace.__to_str
  end
  result = []
  this = dup
  found = index(pattern)
  return this unless found
  result << this[0, found]
  offset = found + pattern.length
  result << if block
    block.call(pattern).to_s
  else
    replace.__sub_replace(this[0, found], pattern, this[offset..-1] || "")
  end
  result << this[offset..-1] if offset < length
  result.join
end

#sub!(*args, &block) ⇒ Object

Replace only the first match of pattern with replacement. Call block (if given) for each match and replace pattern with the value of the block. Modify self with the final value.

ISO 15.2.10.5.37

Raises:



161
162
163
164
165
166
# File 'ext/enterprise_script_service/mruby/mrblib/string.rb', line 161

def sub!(*args, &block)
  raise FrozenError, "can't modify frozen String" if frozen?
  str = self.sub(*args, &block)
  return nil unless self.index(args[0])
  self.replace(str)
end

#to_dObject



20
21
22
# File 'ext/enterprise_script_service/mruby-mpdecimal/mrblib/mpdecimal.rb', line 20

def to_d
  Decimal.new(self)
end

#upto(max, exclusive = false, &block) ⇒ Object

call-seq:

   str.upto(other_str, exclusive=false) {|s| block }   -> str
   str.upto(other_str, exclusive=false)                -> an_enumerator

Iterates through successive values, starting at <i>str</i> and
ending at <i>other_str</i> inclusive, passing each value in turn to
the block. The <code>String#succ</code> method is used to generate
each value.  If optional second argument exclusive is omitted or is false,
the last value will be included; otherwise it will be excluded.

If no block is given, an enumerator is returned instead.

   "a8".upto("b6") {|s| print s, ' ' }
   for s in "a8".."b6"
     print s, ' '
   end

<em>produces:</em>

   a8 a9 b0 b1 b2 b3 b4 b5 b6
   a8 a9 b0 b1 b2 b3 b4 b5 b6

If <i>str</i> and <i>other_str</i> contains only ascii numeric characters,
both are recognized as decimal numbers. In addition, the width of
string (e.g. leading zeros) is handled appropriately.

   "9".upto("11").to_a   #=> ["9", "10", "11"]
   "25".upto("5").to_a   #=> []
   "07".upto("11").to_a  #=> ["07", "08", "09", "10", "11"]

Raises:



401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
# File 'ext/enterprise_script_service/mruby/mrbgems/mruby-string-ext/mrblib/string.rb', line 401

def upto(max, exclusive=false, &block)
  return to_enum(:upto, max, exclusive) unless block
  raise TypeError, "no implicit conversion of #{max.class} into String" unless max.kind_of? String

  len = self.length
  maxlen = max.length
  # single character
  if len == 1 and maxlen == 1
    c = self.ord
    e = max.ord
    while c <= e
      break if exclusive and c == e
      yield c.chr
      c += 1
    end
    return self
  end
  # both edges are all digits
  bi = self.to_i(10)
  ei = max.to_i(10)
  len = self.length
  if (bi > 0 or bi == "0"*len) and (ei > 0 or ei == "0"*maxlen)
    while bi <= ei
      break if exclusive and bi == ei
      s = bi.to_s
      s = s.rjust(len, "0") if s.length < len
      yield s
      bi += 1
    end
    return self
  end
  bs = self
  while true
    n = (bs <=> max)
    break if n > 0
    break if exclusive and n == 0
    yield bs
    break if n == 0
    bs = bs.succ
  end
  self
end