1## 2# String 3# 4# ISO 15.2.10 5class String 6 include Comparable 7 ## 8 # Calls the given block for each line 9 # and pass the respective line. 10 # 11 # ISO 15.2.10.5.15 12 def each_line(rs = "\n", &block) 13 return to_enum(:each_line, rs, &block) unless block 14 return block.call(self) if rs.nil? 15 rs.__to_str 16 offset = 0 17 rs_len = rs.length 18 this = dup 19 while pos = this.index(rs, offset) 20 block.call(this[offset, pos + rs_len - offset]) 21 offset = pos + rs_len 22 end 23 block.call(this[offset, this.size - offset]) if this.size > offset 24 self 25 end 26 27 # private method for gsub/sub 28 def __sub_replace(pre, m, post) 29 s = "" 30 i = 0 31 while j = index("\\", i) 32 break if j == length-1 33 t = case self[j+1] 34 when "\\" 35 "\\" 36 when "`" 37 pre 38 when "&", "0" 39 m 40 when "'" 41 post 42 when "1", "2", "3", "4", "5", "6", "7", "8", "9" 43 "" 44 else 45 self[j, 2] 46 end 47 s += self[i, j-i] + t 48 i = j + 2 49 end 50 s + self[i, length-i] 51 end 52 53 ## 54 # Replace all matches of +pattern+ with +replacement+. 55 # Call block (if given) for each match and replace 56 # +pattern+ with the value of the block. Return the 57 # final value. 58 # 59 # ISO 15.2.10.5.18 60 def gsub(*args, &block) 61 return to_enum(:gsub, *args) if args.length == 1 && !block 62 raise ArgumentError, "wrong number of arguments" unless (1..2).include?(args.length) 63 64 pattern, replace = *args 65 plen = pattern.length 66 if args.length == 2 && block 67 block = nil 68 end 69 if !replace.nil? || !block 70 replace.__to_str 71 end 72 offset = 0 73 result = [] 74 while found = index(pattern, offset) 75 result << self[offset, found - offset] 76 offset = found + plen 77 result << if block 78 block.call(pattern).to_s 79 else 80 replace.__sub_replace(self[0, found], pattern, self[offset..-1] || "") 81 end 82 if plen == 0 83 result << self[offset, 1] 84 offset += 1 85 end 86 end 87 result << self[offset..-1] if offset < length 88 result.join 89 end 90 91 ## 92 # Replace all matches of +pattern+ with +replacement+. 93 # Call block (if given) for each match and replace 94 # +pattern+ with the value of the block. Modify 95 # +self+ with the final value. 96 # 97 # ISO 15.2.10.5.19 98 def gsub!(*args, &block) 99 raise FrozenError, "can't modify frozen String" if frozen? 100 return to_enum(:gsub!, *args) if args.length == 1 && !block 101 str = self.gsub(*args, &block) 102 return nil unless self.index(args[0]) 103 self.replace(str) 104 end 105 106 ## 107 # Calls the given block for each match of +pattern+ 108 # If no block is given return an array with all 109 # matches of +pattern+. 110 # 111 # ISO 15.2.10.5.32 112 def scan(reg, &block) 113 ### *** TODO *** ### 114 unless Object.const_defined?(:Regexp) 115 raise NotImplementedError, "scan not available (yet)" 116 end 117 end 118 119 ## 120 # Replace only the first match of +pattern+ with 121 # +replacement+. Call block (if given) for each 122 # match and replace +pattern+ with the value of the 123 # block. Return the final value. 124 # 125 # ISO 15.2.10.5.36 126 def sub(*args, &block) 127 unless (1..2).include?(args.length) 128 raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 2)" 129 end 130 131 pattern, replace = *args 132 pattern.__to_str 133 if args.length == 2 && block 134 block = nil 135 end 136 unless block 137 replace.__to_str 138 end 139 result = [] 140 this = dup 141 found = index(pattern) 142 return this unless found 143 result << this[0, found] 144 offset = found + pattern.length 145 result << if block 146 block.call(pattern).to_s 147 else 148 replace.__sub_replace(this[0, found], pattern, this[offset..-1] || "") 149 end 150 result << this[offset..-1] if offset < length 151 result.join 152 end 153 154 ## 155 # Replace only the first match of +pattern+ with 156 # +replacement+. Call block (if given) for each 157 # match and replace +pattern+ with the value of the 158 # block. Modify +self+ with the final value. 159 # 160 # ISO 15.2.10.5.37 161 def sub!(*args, &block) 162 raise FrozenError, "can't modify frozen String" if frozen? 163 str = self.sub(*args, &block) 164 return nil unless self.index(args[0]) 165 self.replace(str) 166 end 167 168 ## 169 # Call the given block for each character of 170 # +self+. 171 def each_char(&block) 172 pos = 0 173 while pos < self.size 174 block.call(self[pos]) 175 pos += 1 176 end 177 self 178 end 179 180 ## 181 # Call the given block for each byte of +self+. 182 def each_byte(&block) 183 bytes = self.bytes 184 pos = 0 185 while pos < bytes.size 186 block.call(bytes[pos]) 187 pos += 1 188 end 189 self 190 end 191 192 ## 193 # Modify +self+ by replacing the content of +self+. 194 # The portion of the string affected is determined using the same criteria as +String#[]+. 195 def []=(*args) 196 anum = args.size 197 if anum == 2 198 pos, value = args 199 case pos 200 when String 201 posnum = self.index(pos) 202 if posnum 203 b = self[0, posnum.to_i] 204 a = self[(posnum + pos.length)..-1] 205 self.replace([b, value, a].join('')) 206 else 207 raise IndexError, "string not matched" 208 end 209 when Range 210 head = pos.begin 211 tail = pos.end 212 tail += self.length if tail < 0 213 unless pos.exclude_end? 214 tail += 1 215 end 216 return self[head, tail-head]=value 217 else 218 pos += self.length if pos < 0 219 if pos < 0 || pos > self.length 220 raise IndexError, "index #{args[0]} out of string" 221 end 222 b = self[0, pos.to_i] 223 a = self[pos + 1..-1] 224 self.replace([b, value, a].join('')) 225 end 226 return value 227 elsif anum == 3 228 pos, len, value = args 229 pos += self.length if pos < 0 230 if pos < 0 || pos > self.length 231 raise IndexError, "index #{args[0]} out of string" 232 end 233 if len < 0 234 raise IndexError, "negative length #{len}" 235 end 236 b = self[0, pos.to_i] 237 a = self[pos + len..-1] 238 self.replace([b, value, a].join('')) 239 return value 240 else 241 raise ArgumentError, "wrong number of arguments (#{anum} for 2..3)" 242 end 243 end 244 245 ## 246 # ISO 15.2.10.5.3 247 def =~(re) 248 re =~ self 249 end 250 251 ## 252 # ISO 15.2.10.5.27 253 def match(re, &block) 254 if String === re 255 if Object.const_defined?(:Regexp) 256 r = Regexp.new(re) 257 r.match(self, &block) 258 else 259 raise NotImplementedError, "String#match needs Regexp class" 260 end 261 else 262 re.match(self, &block) 263 end 264 end 265end 266 267## 268# String is comparable 269# 270# ISO 15.2.10.3 271module Comparable; end 272class String 273 include Comparable 274end 275