<ppdoc>
<copyright>
Copyright (c) 2001 by Addison Wesley Longman, Inc. This material may
be distributed only subject to the terms and conditions set forth in
the Open Publication License, v1.0 or later (the latest version is
presently available at http://www.opencontent.org/openpub/).
<p/>
Distribution of substantively modified versions of this document is
prohibited without the explicit permission of the copyright holder.
<p/>
Distribution of the work or derivative of the work in any standard
(paper) book form is prohibited unless prior permission is obtained
from the copyright holder.
</copyright>
<chapter  ref="xref-000001" name="Standard Types">
<p/>
So far we've been having fun implementing pieces of our jukebox code,
but we've been negligent. We've looked at arrays, hashes,
and procs, but we haven't really covered the other basic types in
Ruby: numbers, strings, ranges, and regular expressions. Let's
spend a few pages on these basic building blocks now.
<section>Numbers</section>
<p/>
Ruby supports integers and floating point numbers. Integers can be any length (up to a
maximum determined by the amount of free memory on your system).
Integers within a certain range (normally -2<sup>30</sup> to 2<sup>30</sup>-1 or
-2<sup>62</sup> to 2<sup>62</sup>-1) are held internally in binary form,
and are objects of class <classname>Fixnum</classname>.  Integers outside this range are
stored in objects of class <classname>Bignum</classname> (currently implemented as a
variable-length set of short integers).  This process is transparent,
and Ruby automatically manages the conversion back and forth.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[  num = 8
  7.times do
    print num.type, " ", num, "\n"
    num *= num
  end
]]></fullcode>
num<nbsp/>=<nbsp/>8
7.times<nbsp/>do
<nbsp/><nbsp/>print<nbsp/>num.type,<nbsp/>"<nbsp/>",<nbsp/>num,<nbsp/>"\n"
<nbsp/><nbsp/>num<nbsp/>*=<nbsp/>num
end
</alltt>
</codefragment>
<em>produces:</em>
<codefragment><alltt>
Fixnum<nbsp/>8
Fixnum<nbsp/>64
Fixnum<nbsp/>4096
Fixnum<nbsp/>16777216
Bignum<nbsp/>281474976710656
Bignum<nbsp/>79228162514264337593543950336
Bignum<nbsp/>6277101735386680763835789423207666416102355444464034512896
</alltt>
</codefragment>
<p/>
You write integers
using an optional leading sign, an
optional base indicator (<tt>0</tt> for octal, <tt>0x</tt> for hex, or
<tt>0b</tt> for binary), followed by a string of digits in the
appropriate base. Underscore characters are ignored in the digit
string.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[123456                    # Fixnum
123_456                   # Fixnum (underscore ignored)
-543                      # Negative Fixnum
123_456_789_123_345_789   # Bignum
0xaabb                    # Hexadecimal
0377                      # Octal
-0b101_010                # Binary (negated)
]]></fullcode>
123456<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>#<nbsp/>Fixnum
123_456<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>#<nbsp/>Fixnum<nbsp/>(underscore<nbsp/>ignored)
-543<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>#<nbsp/>Negative<nbsp/>Fixnum
123_456_789_123_345_789<nbsp/><nbsp/><nbsp/>#<nbsp/>Bignum
0xaabb<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>#<nbsp/>Hexadecimal
0377<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>#<nbsp/>Octal
-0b101_010<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>#<nbsp/>Binary<nbsp/>(negated)
</alltt>
</codefragment>
<p/>
You can also get the integer value corresponding to an ASCII
character or escape sequence by preceding it with a question mark.
Control and meta combinations can also be generated using
?\C-<em>x</em>, ?\M-<em>x</em>, and ?\M-\C-<em>x</em>.
The control version of a value is the same as
``<tt>value<nbsp/>&amp;<nbsp/>0x9f</tt>''. The meta version of a value is
``<tt>value<nbsp/>|<nbsp/>0x80</tt>''. Finally, the sequence ?\C-? generates an
ASCII delete, <tt>0177</tt>.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[?a                        # character code
?\n                       # code for a newline (0x0a)
?\C-a                     # control a = ?A & 0x9f = 0x01
?\M-a                     # meta sets bit 7
?\M-\C-a                  # meta and control a
?\C-?                     # delete character
]]></fullcode>
?a<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>#<nbsp/>character<nbsp/>code
?\n<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>#<nbsp/>code<nbsp/>for<nbsp/>a<nbsp/>newline<nbsp/>(0x0a)
?\C-a<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>#<nbsp/>control<nbsp/>a<nbsp/>=<nbsp/>?A<nbsp/>&amp;<nbsp/>0x9f<nbsp/>=<nbsp/>0x01
?\M-a<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>#<nbsp/>meta<nbsp/>sets<nbsp/>bit<nbsp/>7
?\M-\C-a<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>#<nbsp/>meta<nbsp/>and<nbsp/>control<nbsp/>a
?\C-?<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>#<nbsp/>delete<nbsp/>character
</alltt>
</codefragment>
<p/>
A numeric literal with a decimal point and/or an exponent is turned
into a <classname>Float</classname> object,
corresponding to the native architecture's
<tt>double</tt> data type. You must follow the decimal point with a
digit, as <tt>1.e3</tt> tries to invoke the method <meth>e3</meth> in class <classname>Fixnum</classname>.
<p/>
All numbers are objects, and respond to a variety of messages (listed
in full starting on pages 290, 313,
315, 323, and
349). So, unlike (say) C++, you find the absolute
value of a number by writing <tt>aNumber.abs</tt>, not
<tt>abs(aNumber)</tt>.
<p/>
Integers also support several useful iterators. We've seen one
already---<tt>7.times</tt> in the code example
on page 47.
Others include <meth>upto</meth> and
<meth>downto</meth>, for iterating up and down between two integers, and
<meth>step</meth>, which is more like a traditional <tt>for</tt> loop.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[  3.times        { print "X " }
  1.upto(5)      { |i| print i, " " }
  99.downto(95)  { |i| print i, " " }
  50.step(80, 5) { |i| print i, " " }
!-puts
]]></fullcode>
3.times<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>{<nbsp/>print<nbsp/>"X<nbsp/>"<nbsp/>}
1.upto(5)<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>{<nbsp/>|i|<nbsp/>print<nbsp/>i,<nbsp/>"<nbsp/>"<nbsp/>}
99.downto(95)<nbsp/><nbsp/>{<nbsp/>|i|<nbsp/>print<nbsp/>i,<nbsp/>"<nbsp/>"<nbsp/>}
50.step(80,<nbsp/>5)<nbsp/>{<nbsp/>|i|<nbsp/>print<nbsp/>i,<nbsp/>"<nbsp/>"<nbsp/>}
</alltt>
</codefragment>
<em>produces:</em>
<codefragment><alltt>
X<nbsp/>X<nbsp/>X<nbsp/>1<nbsp/>2<nbsp/>3<nbsp/>4<nbsp/>5<nbsp/>99<nbsp/>98<nbsp/>97<nbsp/>96<nbsp/>95<nbsp/>50<nbsp/>55<nbsp/>60<nbsp/>65<nbsp/>70<nbsp/>75<nbsp/>80
</alltt>
</codefragment>
<p/>
Finally, a warning for Perl users.
Strings that contain numbers are
not automatically converted into numbers when used in
expressions. This tends to bite most often when reading numbers from a 
file. The following code (probably) doesn't do what was intended.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[!-DATA=$stdin
DATA.each do |line|
  vals = line.split    # split line, storing tokens in val
  print vals[0] + vals[1], " "
end
]]></fullcode>
DATA.each<nbsp/>do<nbsp/>|line|
<nbsp/><nbsp/>vals<nbsp/>=<nbsp/>line.split<nbsp/><nbsp/><nbsp/><nbsp/>#<nbsp/>split<nbsp/>line,<nbsp/>storing<nbsp/>tokens<nbsp/>in<nbsp/>val
<nbsp/><nbsp/>print<nbsp/>vals[0]<nbsp/>+<nbsp/>vals[1],<nbsp/>"<nbsp/>"
end
</alltt>
</codefragment>
<p/>
Feed it a file containing
<p/>
<codefragment>
<alltt><fullcode><![CDATA[3 4
5 6
7 8
]]></fullcode>
3<nbsp/>4
5<nbsp/>6
7<nbsp/>8
</alltt>
</codefragment>
<p/>
and you'll get the output ``34 56 78.'' 
What happened?
<p/>
The problem is that the input was read as strings, not numbers. The plus
operator concatenates strings, so that's what we see in the output.
To fix this, use the <cim><file>string</file><front>String</front><back>to_i</back><mref>to_i</mref></cim> method to convert the string to
an integer.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[DATA.each do |line|
  vals = line.split
  print vals[0].to_i + vals[1].to_i, " "
end
!-puts
!-__END__
!-3 4
!-5 6
!-7 8
]]></fullcode>
DATA.each<nbsp/>do<nbsp/>|line|
<nbsp/><nbsp/>vals<nbsp/>=<nbsp/>line.split
<nbsp/><nbsp/>print<nbsp/>vals[0].to_i<nbsp/>+<nbsp/>vals[1].to_i,<nbsp/>"<nbsp/>"
end
</alltt>
</codefragment>
<em>produces:</em>
<codefragment><alltt>
7<nbsp/>11<nbsp/>15
</alltt>
</codefragment>
<section>Strings</section>
<p/>
Ruby strings are simply sequences of 8-bit
bytes. They normally hold printable
characters, but that is not a requirement; a string can also hold
binary data. Strings are objects of class <classname>String</classname>.
<p/>
Strings are often created using string literals---sequences of
characters between delimiters. Because binary data is otherwise
difficult to represent within program source, you can place various
escape sequences in a string literal. Each is replaced with the
corresponding binary value as the program is compiled. The type of
string delimiter determines the degree of substitution performed.
Within single-quoted strings, two consecutive backslashes are replaced
by a single backslash, and a backslash followed by a single quote
becomes a single quote.
<p/>
<codefragment>
<fullcode><![CDATA[!-    class String
!-      def inspect
!-        to_s
!-      end
!-    end
  'escape using "\\"'
  'That\'s right'
]]></fullcode><rubycode>
<tr>
  <td><tt>'escape<nbsp/>using<nbsp/>"\\"'</tt></td>
  <td>&#187;</td>
  <td><tt>escape<nbsp/>using<nbsp/>"\"</tt></td>
</tr>
<tr>
  <td><tt>'That\'s<nbsp/>right'</tt></td>
  <td>&#187;</td>
  <td><tt>That's<nbsp/>right</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
Double-quoted strings support a boatload more escape sequences.  The
most common is probably ``\n'', the newline character. 
Table 18.2 on page 203 gives the complete list. In addition,
you can substitute the value of any Ruby expression into a string
using the sequence <tt>#{</tt><nbsp/><em>expr</em><nbsp/><tt>}</tt>.
If the expression is just a global variable, a class variable, or an instance variable,
you can omit the braces.
<p/>
<codefragment>
<fullcode><![CDATA[!-    class String
!-      def inspect
!-        to_s
!-      end
!-    end
  "Seconds/day: #{24*60*60}"
  "#{'Ho! '*3}Merry Christmas"
!- $. = 3
  "This is line #$."
]]></fullcode><rubycode>
<tr>
  <td><tt>"Seconds/day:<nbsp/>#{24*60*60}"</tt></td>
  <td>&#187;</td>
  <td><tt>Seconds/day:<nbsp/>86400</tt></td>
</tr>
<tr>
  <td><tt>"#{'Ho!<nbsp/>'*3}Merry<nbsp/>Christmas"</tt></td>
  <td>&#187;</td>
  <td><tt>Ho!<nbsp/>Ho!<nbsp/>Ho!<nbsp/>Merry<nbsp/>Christmas</tt></td>
</tr>
<tr>
  <td><tt>"This<nbsp/>is<nbsp/>line<nbsp/>#$."</tt></td>
  <td>&#187;</td>
  <td><tt>This<nbsp/>is<nbsp/>line<nbsp/>3</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
There are three more ways to construct string literals:
<tt>%q</tt>, <tt>%Q</tt>, and ``here documents.''
<p/>
<tt>%q</tt> and <tt>%Q</tt> start delimited single- and double-quoted
strings.
<p/>
<codefragment>
<fullcode><![CDATA[!-    class String
!-      def inspect
!-        to_s
!-      end
!-    end
   %q/general single-quoted string/
   %Q!general double-quoted string!
   %Q{Seconds/day: #{24*60*60}}
]]></fullcode><rubycode>
<tr>
  <td><tt>%q/general<nbsp/>single-quoted<nbsp/>string/</tt></td>
  <td>&#187;</td>
  <td><tt>general<nbsp/>single-quoted<nbsp/>string</tt></td>
</tr>
<tr>
  <td><tt>%Q!general<nbsp/>double-quoted<nbsp/>string!</tt></td>
  <td>&#187;</td>
  <td><tt>general<nbsp/>double-quoted<nbsp/>string</tt></td>
</tr>
<tr>
  <td><tt>%Q{Seconds/day:<nbsp/>#{24*60*60}}</tt></td>
  <td>&#187;</td>
  <td><tt>Seconds/day:<nbsp/>86400</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
The character following the ``q'' or ``Q'' is the delimiter. If it is
an opening bracket, brace, parenthesis, or less-than sign, the string
is read until the matching close symbol is found. Otherwise the string 
is read until the next occurrence of the same delimiter.
<p/>
Finally, you can construct a string using a <em>here document</em>.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[aString = <<END_OF_STRING
    The body of the string
    is the input lines up to
    one ending with the same
    text that followed the '<<'
END_OF_STRING
]]></fullcode>
aString<nbsp/>=<nbsp/>&lt;&lt;END_OF_STRING
<nbsp/><nbsp/><nbsp/><nbsp/>The<nbsp/>body<nbsp/>of<nbsp/>the<nbsp/>string
<nbsp/><nbsp/><nbsp/><nbsp/>is<nbsp/>the<nbsp/>input<nbsp/>lines<nbsp/>up<nbsp/>to
<nbsp/><nbsp/><nbsp/><nbsp/>one<nbsp/>ending<nbsp/>with<nbsp/>the<nbsp/>same
<nbsp/><nbsp/><nbsp/><nbsp/>text<nbsp/>that<nbsp/>followed<nbsp/>the<nbsp/>'&lt;&lt;'
END_OF_STRING
</alltt>
</codefragment>
<p/>
A here document consists of lines in the source up to, but not
including, the terminating string that you specify after the <tt>&lt;&lt;</tt> 
characters. Normally, this terminator must start in the first
column. However, if you put a minus sign after the  <tt>&lt;&lt;</tt> 
characters, you can indent the terminator.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[  print <<-STRING1, <<-STRING2
     Concat
     STRING1
        enate
        STRING2
!- puts
]]></fullcode>
print<nbsp/>&lt;&lt;-STRING1,<nbsp/>&lt;&lt;-STRING2
<nbsp/><nbsp/><nbsp/>Concat
<nbsp/><nbsp/><nbsp/>STRING1
<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>enate
<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>STRING2
</alltt>
</codefragment>
<em>produces:</em>
<codefragment><alltt>
<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>Concat
<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>enate
<p/>
</alltt>
</codefragment>
<subsection>Working with Strings</subsection>
<p/>
<classname>String</classname> is probably the largest built-in Ruby class, with over 75
standard methods. We won't go through them all here; the library
reference has a complete list. Instead, we'll look at
some common string idioms---things that are likely to pop up during
day-to-day programming.
<p/>
Let's get back to our jukebox. Although it's designed to be connected
to the Internet, it also holds copies of some popular songs on a local
hard drive. That way, if a squirrel chews through our 'net connection
we'll still be able to entertain the customers.
<p/>
For historical reasons (are there any other kind?), the list of songs
is stored as rows in a flat file. Each row holds the name of the file
containing the song, the song's duration, the artist, and the title,
all in vertical-bar-separated fields. A typical file might
start:
<p/>
<codefragment>
<alltt><fullcode><![CDATA[/jazz/j00132.mp3  | 3:45 | Fats     Waller     | Ain't Misbehavin'
/jazz/j00319.mp3  | 2:58 | Louis    Armstrong  | Wonderful World
/bgrass/bg0732.mp3| 4:09 | Strength in Numbers | Texas Red
         :                  :           :                   :
]]></fullcode>
/jazz/j00132.mp3<nbsp/><nbsp/>|<nbsp/>3:45<nbsp/>|<nbsp/>Fats<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>Waller<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>|<nbsp/>Ain't<nbsp/>Misbehavin'
/jazz/j00319.mp3<nbsp/><nbsp/>|<nbsp/>2:58<nbsp/>|<nbsp/>Louis<nbsp/><nbsp/><nbsp/><nbsp/>Armstrong<nbsp/><nbsp/>|<nbsp/>Wonderful<nbsp/>World
/bgrass/bg0732.mp3|<nbsp/>4:09<nbsp/>|<nbsp/>Strength<nbsp/>in<nbsp/>Numbers<nbsp/>|<nbsp/>Texas<nbsp/>Red
<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>:<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>:<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>:<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>:
</alltt>
</codefragment>
<p/>
Looking at the data, it's clear that we'll be using some of class
<classname>String</classname>'s many methods to extract and clean up the fields before we 
create <classname>Song</classname> objects based on them. At a minimum, we'll need to:
<p/>
<ul>
<li> break the line into fields,
</li><li> convert the running time from mm:ss to seconds, and
</li><li> remove those extra spaces from the artist's name.
</li></ul>
<p/>
Our first task is to split each line into fields, and
<cim><file>string</file><front>String</front><back>split</back><mref>split</mref></cim> will do the job nicely. In
this case, we'll pass <meth>split</meth> a regular expression,
<tt>/\s*\|\s*/</tt>, which splits the line into tokens
wherever <meth>split</meth> finds a vertical bar, optionally surrounded by
spaces.  And, because the line read from the file has a trailing
newline, we'll use <cim><file>string</file><front>String</front><back>chomp</back><mref>chomp</mref></cim> to strip it off just before we
apply the split.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[!-  class WordIndex
!-    def initialize
!-      @index = Hash.new(nil)
!-    end
!-    def index(anObject, *phrases)
!-      phrases.each do |aPhrase|
!-        aPhrase.scan /\w[-\w']+/ do |aWord|   # extract each word
!-          aWord.downcase!
!-          @index[aWord] = [] if @index[aWord].nil?
!-          @index[aWord].push(anObject)
!-        end
!-      end
!-    end
!-    def lookup(aWord)
!-      @index[aWord.downcase]
!-    end
!-  end
!-class Song
!-  include Comparable
!-  @@plays = 0
!-  attr_reader :name, :artist, :duration
!-  attr_writer :duration
!-  def initialize(name, artist, duration)
!-    @name     = name
!-    @artist   = artist
!-    @duration = duration
!-    @plays    = 0
!-  end
!-  def to_s
!-    "Song: #{@name}--#{@artist} (#{@duration})"
!-  end
!-  def name
!-    @name
!-  end
!-  def artist
!-    @artist
!-  end
!-  def duration
!-    @duration
!-  end
!-  def duration=(newDuration)
!-    @duration = newDuration
!-  end
!-  def durationInMinutes
!-    @duration/60.0   # force floating point
!-  end
!-  def durationInMinutes=(value)
!-    @duration = (value*60).to_i
!-  end
!-  def play
!-    @plays += 1
!-    @@plays += 1
!-    "This  song: #@plays plays. Total #@@plays plays."
!-  end
!-  def record
!-    "Recording..."
!-  end
!-  def inspect
!-    self.to_s
!-  end
!-  def <=>(other)
!-    self.duration <=> other.duration
!-  end
!-end
!-class SongList
!-  def initialize
!-    @songs = Array.new
!-    @index = WordIndex.new
!-  end
!-  def append(aSong)
!-    @songs.push(aSong)
!-    @index.index(aSong, aSong.name, aSong.artist)
!-    self
!-  end
!-  def deleteFirst
!-    @songs.shift
!-  end
!-  def deleteLast
!-    @songs.pop
!-  end
!-  def [](key)
!-    if key.kind_of?(Integer)
!-      @songs[key]
!-    else
!-      # ...
!-    end
!-  end
!-  def [](key)
!-    if key.kind_of?(Integer)
!-      return @songs[key]
!-    else
!-      for i in 0...@songs.length
!-        return @songs[i] if key == @songs[i].name
!-      end
!-    end
!-    return nil
!-  end
!-  def [](key)
!-    if key.kind_of?(Integer)
!-      result = @songs[key]
!-    else
!-      result = @songs.find { |aSong| key == aSong.name }
!-    end
!-    return result
!-  end
!-  def lookup(aWord)
!-    @index.lookup(aWord)
!-  end
!-  def createSearch(name, params)
!-    # ...
!-  end
!-end  
!-songFile = DATA
songs = SongList.new

songFile.each do |line|
  file, length, name, title = line.chomp.split(/\s*\|\s*/)
  songs.append Song.new(title, name, length)
end
puts songs[1]
!-__END__
!-/jazz/j00132.mp3  | 3:45 | Fats     Waller     | Ain't Misbehavin'
!-/jazz/j00319.mp3  | 2:58 | Louis    Armstrong  | Wonderful World
!-/bgrass/bg0732.mp3| 4:09 | Strength in Numbers | Texas Red
]]></fullcode>
songs<nbsp/>=<nbsp/>SongList.new
<p/>
songFile.each<nbsp/>do<nbsp/>|line|
<nbsp/><nbsp/>file,<nbsp/>length,<nbsp/>name,<nbsp/>title<nbsp/>=<nbsp/>line.chomp.split(/\s*\|\s*/)
<nbsp/><nbsp/>songs.append<nbsp/>Song.new(title,<nbsp/>name,<nbsp/>length)
end
puts<nbsp/>songs[1]
</alltt>
</codefragment>
<em>produces:</em>
<codefragment><alltt>
Song:<nbsp/>Wonderful<nbsp/>World--Louis<nbsp/><nbsp/><nbsp/><nbsp/>Armstrong<nbsp/>(2:58)
</alltt>
</codefragment>
<p/>
Unfortunately, whoever created the original file entered the artists'
names in columns, so some of them contain extra spaces. These will
look ugly on our high-tech, super-twist, flat-panel Day-Glo display, so 
we'd better remove these extra spaces before we go much further.
There are many ways of doing this, but
probably the simplest is <cim><file>string</file><front>String</front><back>squeeze</back><mref>squeeze</mref></cim>, which trims runs of
repeated characters. We'll use the <meth>squeeze!</meth> form of the
method, which alters the string in place.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[!-  class WordIndex
!-    def initialize
!-      @index = Hash.new(nil)
!-    end
!-    def index(anObject, *phrases)
!-      phrases.each do |aPhrase|
!-        aPhrase.scan /\w[-\w']+/ do |aWord|   # extract each word
!-          aWord.downcase!
!-          @index[aWord] = [] if @index[aWord].nil?
!-          @index[aWord].push(anObject)
!-        end
!-      end
!-    end
!-    def lookup(aWord)
!-      @index[aWord.downcase]
!-    end
!-  end
!-class Song
!-  include Comparable
!-  @@plays = 0
!-  attr_reader :name, :artist, :duration
!-  attr_writer :duration
!-  def initialize(name, artist, duration)
!-    @name     = name
!-    @artist   = artist
!-    @duration = duration
!-    @plays    = 0
!-  end
!-  def to_s
!-    "Song: #{@name}--#{@artist} (#{@duration})"
!-  end
!-  def name
!-    @name
!-  end
!-  def artist
!-    @artist
!-  end
!-  def duration
!-    @duration
!-  end
!-  def duration=(newDuration)
!-    @duration = newDuration
!-  end
!-  def durationInMinutes
!-    @duration/60.0   # force floating point
!-  end
!-  def durationInMinutes=(value)
!-    @duration = (value*60).to_i
!-  end
!-  def play
!-    @plays += 1
!-    @@plays += 1
!-    "This  song: #@plays plays. Total #@@plays plays."
!-  end
!-  def record
!-    "Recording..."
!-  end
!-  def inspect
!-    self.to_s
!-  end
!-  def <=>(other)
!-    self.duration <=> other.duration
!-  end
!-end
!-class SongList
!-  def initialize
!-    @songs = Array.new
!-    @index = WordIndex.new
!-  end
!-  def append(aSong)
!-    @songs.push(aSong)
!-    @index.index(aSong, aSong.name, aSong.artist)
!-    self
!-  end
!-  def deleteFirst
!-    @songs.shift
!-  end
!-  def deleteLast
!-    @songs.pop
!-  end
!-  def [](key)
!-    if key.kind_of?(Integer)
!-      @songs[key]
!-    else
!-      # ...
!-    end
!-  end
!-  def [](key)
!-    if key.kind_of?(Integer)
!-      return @songs[key]
!-    else
!-      for i in 0...@songs.length
!-        return @songs[i] if key == @songs[i].name
!-      end
!-    end
!-    return nil
!-  end
!-  def [](key)
!-    if key.kind_of?(Integer)
!-      result = @songs[key]
!-    else
!-      result = @songs.find { |aSong| key == aSong.name }
!-    end
!-    return result
!-  end
!-  def lookup(aWord)
!-    @index.lookup(aWord)
!-  end
!-  def createSearch(name, params)
!-    # ...
!-  end
!-end  
!-songFile = DATA
songs = SongList.new

songFile.each do |line|
  file, length, name, title = line.chomp.split(/\s*\|\s*/)
  name.squeeze!(" ")
  songs.append Song.new(title, name, length)
end
puts songs[1]
!-__END__
!-/jazz/j00132.mp3  | 3:45 | Fats     Waller     | Ain't Misbehavin'
!-/jazz/j00319.mp3  | 2:58 | Louis    Armstrong  | Wonderful World
!-/bgrass/bg0732.mp3| 4:09 | Strength in Numbers | Texas Red
]]></fullcode>
songs<nbsp/>=<nbsp/>SongList.new
<p/>
songFile.each<nbsp/>do<nbsp/>|line|
<nbsp/><nbsp/>file,<nbsp/>length,<nbsp/>name,<nbsp/>title<nbsp/>=<nbsp/>line.chomp.split(/\s*\|\s*/)
<nbsp/><nbsp/>name.squeeze!("<nbsp/>")
<nbsp/><nbsp/>songs.append<nbsp/>Song.new(title,<nbsp/>name,<nbsp/>length)
end
puts<nbsp/>songs[1]
</alltt>
</codefragment>
<em>produces:</em>
<codefragment><alltt>
Song:<nbsp/>Wonderful<nbsp/>World--Louis<nbsp/>Armstrong<nbsp/>(2:58)
</alltt>
</codefragment>
<p/>
Finally, there's the minor matter of the time format: the file says
2:58, and we want the number of seconds, 178. We could use
<meth>split</meth> again, this time splitting the time field around the
colon character.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[!- length = '3:45'
  mins, secs = length.split(/:/)
]]></fullcode>
mins,<nbsp/>secs<nbsp/>=<nbsp/>length.split(/:/)
</alltt>
</codefragment>
<p/>
Instead, we'll use a related method. <cim><file>string</file><front>String</front><back>scan</back><mref>scan</mref></cim> is similar to
<meth>split</meth> in that it breaks a string into chunks based on a
pattern. However, unlike <meth>split</meth>, with <meth>scan</meth> you
specify the pattern that you want the chunks to match. In this case,
we want to match one or more digits for both the minutes and seconds
component. The pattern for one or more digits is <tt>/\d+/</tt>.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[!-  class WordIndex
!-    def initialize
!-      @index = Hash.new(nil)
!-    end
!-    def index(anObject, *phrases)
!-      phrases.each do |aPhrase|
!-        aPhrase.scan /\w[-\w']+/ do |aWord|   # extract each word
!-          aWord.downcase!
!-          @index[aWord] = [] if @index[aWord].nil?
!-          @index[aWord].push(anObject)
!-        end
!-      end
!-    end
!-    def lookup(aWord)
!-      @index[aWord.downcase]
!-    end
!-  end
!-class Song
!-  include Comparable
!-  @@plays = 0
!-  attr_reader :name, :artist, :duration
!-  attr_writer :duration
!-  def initialize(name, artist, duration)
!-    @name     = name
!-    @artist   = artist
!-    @duration = duration
!-    @plays    = 0
!-  end
!-  def to_s
!-    "Song: #{@name}--#{@artist} (#{@duration})"
!-  end
!-  def name
!-    @name
!-  end
!-  def artist
!-    @artist
!-  end
!-  def duration
!-    @duration
!-  end
!-  def duration=(newDuration)
!-    @duration = newDuration
!-  end
!-  def durationInMinutes
!-    @duration/60.0   # force floating point
!-  end
!-  def durationInMinutes=(value)
!-    @duration = (value*60).to_i
!-  end
!-  def play
!-    @plays += 1
!-    @@plays += 1
!-    "This  song: #@plays plays. Total #@@plays plays."
!-  end
!-  def record
!-    "Recording..."
!-  end
!-  def inspect
!-    self.to_s
!-  end
!-  def <=>(other)
!-    self.duration <=> other.duration
!-  end
!-end
!-class SongList
!-  def initialize
!-    @songs = Array.new
!-    @index = WordIndex.new
!-  end
!-  def append(aSong)
!-    @songs.push(aSong)
!-    @index.index(aSong, aSong.name, aSong.artist)
!-    self
!-  end
!-  def deleteFirst
!-    @songs.shift
!-  end
!-  def deleteLast
!-    @songs.pop
!-  end
!-  def [](key)
!-    if key.kind_of?(Integer)
!-      @songs[key]
!-    else
!-      # ...
!-    end
!-  end
!-  def [](key)
!-    if key.kind_of?(Integer)
!-      return @songs[key]
!-    else
!-      for i in 0...@songs.length
!-        return @songs[i] if key == @songs[i].name
!-      end
!-    end
!-    return nil
!-  end
!-  def [](key)
!-    if key.kind_of?(Integer)
!-      result = @songs[key]
!-    else
!-      result = @songs.find { |aSong| key == aSong.name }
!-    end
!-    return result
!-  end
!-  def lookup(aWord)
!-    @index.lookup(aWord)
!-  end
!-  def createSearch(name, params)
!-    # ...
!-  end
!-end  
!-songFile = DATA
songs = SongList.new
songFile.each do |line|
  file, length, name, title = line.chomp.split(/\s*\|\s*/)
  name.squeeze!(" ")
  mins, secs = length.scan(/\d+/)
  songs.append Song.new(title, name, mins.to_i*60+secs.to_i)
end
puts songs[1]
!-__END__
!-/jazz/j00132.mp3  | 3:45 | Fats     Waller     | Ain't Misbehavin'
!-/jazz/j00319.mp3  | 2:58 | Louis    Armstrong  | Wonderful World
!-/bgrass/bg0732.mp3| 4:09 | Strength in Numbers | Texas Red
]]></fullcode>
songs<nbsp/>=<nbsp/>SongList.new
songFile.each<nbsp/>do<nbsp/>|line|
<nbsp/><nbsp/>file,<nbsp/>length,<nbsp/>name,<nbsp/>title<nbsp/>=<nbsp/>line.chomp.split(/\s*\|\s*/)
<nbsp/><nbsp/>name.squeeze!("<nbsp/>")
<nbsp/><nbsp/>mins,<nbsp/>secs<nbsp/>=<nbsp/>length.scan(/\d+/)
<nbsp/><nbsp/>songs.append<nbsp/>Song.new(title,<nbsp/>name,<nbsp/>mins.to_i*60+secs.to_i)
end
puts<nbsp/>songs[1]
</alltt>
</codefragment>
<em>produces:</em>
<codefragment><alltt>
Song:<nbsp/>Wonderful<nbsp/>World--Louis<nbsp/>Armstrong<nbsp/>(178)
</alltt>
</codefragment>
<p/>
Our jukebox has a keyword search capability. Given a word from a song
title or an artist's name, it will list all matching tracks. Type in
``fats,'' and it might come back with songs by Fats Domino, Fats
Navarro, and Fats Waller, for example. We'll implement this by
creating an indexing class. Feed it an object and some strings,
and it will index that object under every word (of two or more
characters) that occurs in those strings. This will illustrate a few
more of class <classname>String</classname>'s many methods.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[  class WordIndex
    def initialize
      @index = Hash.new(nil)
    end
    def index(anObject, *phrases)
      phrases.each do |aPhrase|
        aPhrase.scan /\w[-\w']+/ do |aWord|   # extract each word
          aWord.downcase!
          @index[aWord] = [] if @index[aWord].nil?
          @index[aWord].push(anObject)
        end
      end
    end
    def lookup(aWord)
      @index[aWord.downcase]
    end
  end
]]></fullcode>
class<nbsp/>WordIndex
<nbsp/><nbsp/>def<nbsp/>initialize
<nbsp/><nbsp/><nbsp/><nbsp/>@index<nbsp/>=<nbsp/>Hash.new(nil)
<nbsp/><nbsp/>end
<nbsp/><nbsp/>def<nbsp/>index(anObject,<nbsp/>*phrases)
<nbsp/><nbsp/><nbsp/><nbsp/>phrases.each<nbsp/>do<nbsp/>|aPhrase|
<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>aPhrase.scan<nbsp/>/\w[-\w']+/<nbsp/>do<nbsp/>|aWord|<nbsp/><nbsp/><nbsp/>#<nbsp/>extract<nbsp/>each<nbsp/>word
<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>aWord.downcase!
<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>@index[aWord]<nbsp/>=<nbsp/>[]<nbsp/>if<nbsp/>@index[aWord].nil?
<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>@index[aWord].push(anObject)
<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>end
<nbsp/><nbsp/><nbsp/><nbsp/>end
<nbsp/><nbsp/>end
<nbsp/><nbsp/>def<nbsp/>lookup(aWord)
<nbsp/><nbsp/><nbsp/><nbsp/>@index[aWord.downcase]
<nbsp/><nbsp/>end
end
</alltt>
</codefragment>
<p/>
The <cim><file>string</file><front>String</front><back>scan</back><mref>scan</mref></cim> method extracts elements from a string that
match a regular expression. In this case, the pattern
``<tt>\w[-\w']+</tt>'' matches any character that can
appear in a word, followed by one or more of the things specified in
the brackets (a hyphen, another word character, or a single quote). We'll talk
more about regular expressions beginning on page 56.  To make our
searches case insensitive, we map both the words we extract and the
words used as keys during the lookup to lowercase. Note the
exclamation mark at the end of the first <meth>downcase!</meth> method
name. As with the <meth>squeeze!</meth> method we used previously, this is 
an indication that the method will modify the receiver in place, in this 
case converting the string to lowercase.<footnote>There's a minor bug 
  in this code example: the song ``Gone, Gone, Gone'' would get
  indexed three times. Can you come up with a fix?</footnote>
<p/>
We'll extend our <classname>SongList</classname> class to index songs as they're added,
and add a method to look up a song given a word.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[class SongList
  def initialize
    @songs = Array.new
    @index = WordIndex.new
  end
  def append(aSong)
    @songs.push(aSong)
    @index.index(aSong, aSong.name, aSong.artist)
    self
  end
!-  def deleteFirst
!-    @songs.shift
!-  end
!-  def deleteLast
!-    @songs.pop
!-  end
!-  def [](key)
!-    if key.kind_of?(Integer)
!-      @songs[key]
!-    else
!-      # ...
!-    end
!-  end
!-  def [](key)
!-    if key.kind_of?(Integer)
!-      return @songs[key]
!-    else
!-      for i in 0...@songs.length
!-        return @songs[i] if key == @songs[i].name
!-      end
!-    end
!-    return nil
!-  end
!-  def [](key)
!-    if key.kind_of?(Integer)
!-      result = @songs[key]
!-    else
!-      result = @songs.find { |aSong| key == aSong.name }
!-    end
!-    return result
!-  end
  def lookup(aWord)
    @index.lookup(aWord)
  end
!-  def createSearch(name, params)
!-    # ...
!-  end
end  
]]></fullcode>
class<nbsp/>SongList
<nbsp/><nbsp/>def<nbsp/>initialize
<nbsp/><nbsp/><nbsp/><nbsp/>@songs<nbsp/>=<nbsp/>Array.new
<nbsp/><nbsp/><nbsp/><nbsp/>@index<nbsp/>=<nbsp/>WordIndex.new
<nbsp/><nbsp/>end
<nbsp/><nbsp/>def<nbsp/>append(aSong)
<nbsp/><nbsp/><nbsp/><nbsp/>@songs.push(aSong)
<nbsp/><nbsp/><nbsp/><nbsp/>@index.index(aSong,<nbsp/>aSong.name,<nbsp/>aSong.artist)
<nbsp/><nbsp/><nbsp/><nbsp/>self
<nbsp/><nbsp/>end
<nbsp/><nbsp/>def<nbsp/>lookup(aWord)
<nbsp/><nbsp/><nbsp/><nbsp/>@index.lookup(aWord)
<nbsp/><nbsp/>end
end
</alltt>
</codefragment>
<p/>
Finally, we'll test it all.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[!-  class WordIndex
!-    def initialize
!-      @index = Hash.new(nil)
!-    end
!-    def index(anObject, *phrases)
!-      phrases.each do |aPhrase|
!-        aPhrase.scan /\w[-\w']+/ do |aWord|   # extract each word
!-          aWord.downcase!
!-          @index[aWord] = [] if @index[aWord].nil?
!-          @index[aWord].push(anObject)
!-        end
!-      end
!-    end
!-    def lookup(aWord)
!-      @index[aWord.downcase]
!-    end
!-  end
!-class Song
!-  include Comparable
!-  @@plays = 0
!-  attr_reader :name, :artist, :duration
!-  attr_writer :duration
!-  def initialize(name, artist, duration)
!-    @name     = name
!-    @artist   = artist
!-    @duration = duration
!-    @plays    = 0
!-  end
!-  def to_s
!-    "Song: #{@name}--#{@artist} (#{@duration})"
!-  end
!-  def name
!-    @name
!-  end
!-  def artist
!-    @artist
!-  end
!-  def duration
!-    @duration
!-  end
!-  def duration=(newDuration)
!-    @duration = newDuration
!-  end
!-  def durationInMinutes
!-    @duration/60.0   # force floating point
!-  end
!-  def durationInMinutes=(value)
!-    @duration = (value*60).to_i
!-  end
!-  def play
!-    @plays += 1
!-    @@plays += 1
!-    "This  song: #@plays plays. Total #@@plays plays."
!-  end
!-  def record
!-    "Recording..."
!-  end
!-  def inspect
!-    self.to_s
!-  end
!-  def <=>(other)
!-    self.duration <=> other.duration
!-  end
!-end
!-class SongList
!-  def initialize
!-    @songs = Array.new
!-    @index = WordIndex.new
!-  end
!-  def append(aSong)
!-    @songs.push(aSong)
!-    @index.index(aSong, aSong.name, aSong.artist)
!-    self
!-  end
!-  def deleteFirst
!-    @songs.shift
!-  end
!-  def deleteLast
!-    @songs.pop
!-  end
!-  def [](key)
!-    if key.kind_of?(Integer)
!-      @songs[key]
!-    else
!-      # ...
!-    end
!-  end
!-  def [](key)
!-    if key.kind_of?(Integer)
!-      return @songs[key]
!-    else
!-      for i in 0...@songs.length
!-        return @songs[i] if key == @songs[i].name
!-      end
!-    end
!-    return nil
!-  end
!-  def [](key)
!-    if key.kind_of?(Integer)
!-      result = @songs[key]
!-    else
!-      result = @songs.find { |aSong| key == aSong.name }
!-    end
!-    return result
!-  end
!-  def lookup(aWord)
!-    @index.lookup(aWord)
!-  end
!-  def createSearch(name, params)
!-    # ...
!-  end
!-end  
!-songFile = DATA
songs = SongList.new
songFile.each do |line|
  file, length, name, title = line.chomp.split(/\s*\|\s*/)
  name.squeeze!(" ")
  mins, secs = length.scan(/\d+/)
  songs.append Song.new(title, name, mins.to_i*60+secs.to_i)
end
puts songs.lookup("Fats")
puts songs.lookup("ain't")
puts songs.lookup("RED")
puts songs.lookup("WoRlD")
!-__END__
!-/jazz/j00132.mp3  | 3:45 | Fats     Waller     | Ain't Misbehavin'
!-/jazz/j00319.mp3  | 2:58 | Louis    Armstrong  | Wonderful World
!-/bgrass/bg0732.mp3| 4:09 | Strength in Numbers | Texas Red
]]></fullcode>
songs<nbsp/>=<nbsp/>SongList.new
songFile.each<nbsp/>do<nbsp/>|line|
<nbsp/><nbsp/>file,<nbsp/>length,<nbsp/>name,<nbsp/>title<nbsp/>=<nbsp/>line.chomp.split(/\s*\|\s*/)
<nbsp/><nbsp/>name.squeeze!("<nbsp/>")
<nbsp/><nbsp/>mins,<nbsp/>secs<nbsp/>=<nbsp/>length.scan(/\d+/)
<nbsp/><nbsp/>songs.append<nbsp/>Song.new(title,<nbsp/>name,<nbsp/>mins.to_i*60+secs.to_i)
end
puts<nbsp/>songs.lookup("Fats")
puts<nbsp/>songs.lookup("ain't")
puts<nbsp/>songs.lookup("RED")
puts<nbsp/>songs.lookup("WoRlD")
</alltt>
</codefragment>
<em>produces:</em>
<codefragment><alltt>
Song:<nbsp/>Ain't<nbsp/>Misbehavin'--Fats<nbsp/>Waller<nbsp/>(225)
Song:<nbsp/>Ain't<nbsp/>Misbehavin'--Fats<nbsp/>Waller<nbsp/>(225)
Song:<nbsp/>Texas<nbsp/>Red--Strength<nbsp/>in<nbsp/>Numbers<nbsp/>(249)
Song:<nbsp/>Wonderful<nbsp/>World--Louis<nbsp/>Armstrong<nbsp/>(178)
</alltt>
</codefragment>
<p/>
We could spend the next 50 pages looking at all the methods in class
<classname>String</classname>. However, let's move on instead to look at a simpler
datatype: ranges.
<section>Ranges</section>
<p/>
Ranges occur everywhere: January to December, 0 to 9, rare to
well-done, lines 50 through 67, and so on.  If Ruby is to help us
model reality, it seems natural for it to support these ranges. In
fact, Ruby goes one better: it actually uses ranges to implement three
separate features: sequences, conditions, and intervals.<subsection>Ranges as Sequences</subsection>
<p/>
The first and perhaps most natural use of ranges is to express a
sequence.
Sequences have a start point, an end point, and a way to
produce successive values in the sequence. In Ruby, these sequences
are created using the ``..'' and ``...'' range operators. The
two-dot form creates an inclusive range, while the three-dot form
creates a range that excludes the specified high value.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[!-anArray = []
  1..10
  'a'..'z'
  0...anArray.length
]]></fullcode>
1..10
'a'..'z'
0...anArray.length
</alltt>
</codefragment>
<p/>
In Ruby, unlike in some earlier versions of Perl, ranges are not
represented internally as lists: the sequence 1..100000 is held as a
<classname>Range</classname> object containing references to two <classname>Fixnum</classname> objects.  If
you need to, you can convert a range to a list using the
<meth>to_a</meth> method.
<p/>
<codefragment>
<fullcode><![CDATA[  (1..10).to_a
  ('bar'..'bat').to_a
]]></fullcode><rubycode>
<tr>
  <td><tt>(1..10).to_a</tt></td>
  <td>&#187;</td>
  <td><tt>[1,<nbsp/>2,<nbsp/>3,<nbsp/>4,<nbsp/>5,<nbsp/>6,<nbsp/>7,<nbsp/>8,<nbsp/>9,<nbsp/>10]</tt></td>
</tr>
<tr>
  <td><tt>('bar'..'bat').to_a</tt></td>
  <td>&#187;</td>
  <td><tt>["bar",<nbsp/>"bas",<nbsp/>"bat"]</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
Ranges implement methods that let you iterate over them and test their
contents in a variety of ways.
<p/>
<codefragment>
<fullcode><![CDATA[!-def dial(digit)
!-end
  digits = 0..9
  digits.include?(5)
  digits.min
  digits.max
  digits.reject {|i| i < 5 }
  digits.each do |digit|
    dial(digit)
  end
]]></fullcode><rubycode>
<tr>
<td colspan="3"><tt>digits<nbsp/>=<nbsp/>0..9</tt></td>
</tr>
<tr>
  <td><tt>digits.include?(5)</tt></td>
  <td>&#187;</td>
  <td><tt>true</tt></td>
</tr>
<tr>
  <td><tt>digits.min</tt></td>
  <td>&#187;</td>
  <td><tt>0</tt></td>
</tr>
<tr>
  <td><tt>digits.max</tt></td>
  <td>&#187;</td>
  <td><tt>9</tt></td>
</tr>
<tr>
  <td><tt>digits.reject<nbsp/>{|i|<nbsp/>i<nbsp/>&lt;<nbsp/>5<nbsp/>}</tt></td>
  <td>&#187;</td>
  <td><tt>[5,<nbsp/>6,<nbsp/>7,<nbsp/>8,<nbsp/>9]</tt></td>
</tr>
<tr>
<td colspan="3"><tt>digits.each<nbsp/>do<nbsp/>|digit|</tt></td>
</tr>
<tr>
<td colspan="3"><tt><nbsp/><nbsp/>dial(digit)</tt></td>
</tr>
<tr>
<td colspan="3"><tt>end</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
So far we've shown ranges of numbers and strings. However, as you'd
expect from an object-oriented language, Ruby can create ranges based
on objects that you define. The only constraints are that the objects
must respond to <meth>succ</meth> by returning the next object in sequence
and the objects must be comparable using <tt>&lt;=&gt;</tt>,
the general comparison operator.
Sometimes called the
spaceship operator, <tt>&lt;=&gt;</tt> compares two values, returning -1, 0, or +1
depending on whether the first is less than, equal to, or greater than
the second.
<p/>
Here's a simple class that represents rows of ``#'' signs. We might
use it as a text-based stub when testing the jukebox volume control.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[  class VU

    include Comparable

    attr :volume

    def initialize(volume)  # 0..9
      @volume = volume
    end

    def inspect
      '#' * @volume
    end

    # Support for ranges

    def <=>(other)
      self.volume <=> other.volume
    end

    def succ
      raise(IndexError, "Volume too big") if @volume >= 9
      VU.new(@volume.succ)
    end
  end
]]></fullcode>
class<nbsp/>VU
<p/>
<nbsp/><nbsp/>include<nbsp/>Comparable
<p/>
<nbsp/><nbsp/>attr<nbsp/>:volume
<p/>
<nbsp/><nbsp/>def<nbsp/>initialize(volume)<nbsp/><nbsp/>#<nbsp/>0..9
<nbsp/><nbsp/><nbsp/><nbsp/>@volume<nbsp/>=<nbsp/>volume
<nbsp/><nbsp/>end
<p/>
<nbsp/><nbsp/>def<nbsp/>inspect
<nbsp/><nbsp/><nbsp/><nbsp/>'#'<nbsp/>*<nbsp/>@volume
<nbsp/><nbsp/>end
<p/>
<nbsp/><nbsp/>#<nbsp/>Support<nbsp/>for<nbsp/>ranges
<p/>
<nbsp/><nbsp/>def<nbsp/>&lt;=&gt;(other)
<nbsp/><nbsp/><nbsp/><nbsp/>self.volume<nbsp/>&lt;=&gt;<nbsp/>other.volume
<nbsp/><nbsp/>end
<p/>
<nbsp/><nbsp/>def<nbsp/>succ
<nbsp/><nbsp/><nbsp/><nbsp/>raise(IndexError,<nbsp/>"Volume<nbsp/>too<nbsp/>big")<nbsp/>if<nbsp/>@volume<nbsp/>&gt;=<nbsp/>9
<nbsp/><nbsp/><nbsp/><nbsp/>VU.new(@volume.succ)
<nbsp/><nbsp/>end
end
</alltt>
</codefragment>
<p/>
We can test it by creating a range of <classname>VU</classname> objects.
<p/>
<codefragment>
<fullcode><![CDATA[!-  class VU
!-
!-    include Comparable
!-
!-    attr :volume
!-
!-    def initialize(volume)  # 0..9
!-      @volume = volume
!-    end
!-
!-    def inspect
!-      '#' * @volume
!-    end
!-
!-    # Support for ranges
!-
!-    def <=>(other)
!-      self.volume <=> other.volume
!-    end
!-
!-    def succ
!-      raise(IndexError, "Volume too big") if @volume >= 9
!-      VU.new(@volume.succ)
!-    end
!-  end
medium = VU.new(4)..VU.new(7)
medium.to_a
medium.include?(VU.new(3))
]]></fullcode><rubycode>
<tr>
<td colspan="3"><tt>medium<nbsp/>=<nbsp/>VU.new(4)..VU.new(7)</tt></td>
</tr>
<tr>
  <td><tt>medium.to_a</tt></td>
  <td>&#187;</td>
  <td><tt>[####,<nbsp/>#####,<nbsp/>######,<nbsp/>#######]</tt></td>
</tr>
<tr>
  <td><tt>medium.include?(VU.new(3))</tt></td>
  <td>&#187;</td>
  <td><tt>false</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<subsection>Ranges as Conditions</subsection>
<p/>
As well as representing sequences, ranges may also be used as
conditional expressions.
For example, the
following code fragment prints sets of lines from standard input,
where the first line in each set contains the word ``start'' and the
last line the word ``end.''
<p/>
<codefragment>
<alltt><fullcode><![CDATA[  while gets
    print if /start/../end/
  end
]]></fullcode>
while<nbsp/>gets
<nbsp/><nbsp/>print<nbsp/>if<nbsp/>/start/../end/
end
</alltt>
</codefragment>
<p/>
Behind the scenes, the range keeps track of the state of each of the
tests.  We'll show some examples of this in the description of loops
that starts on page 82.
<subsection>Ranges as Intervals</subsection>
<p/>
A final use of the versatile range is as an interval test:
seeing if
some value falls within the interval represented by the range.
This is done using <tt>===</tt>, the case equality operator.
<p/>
<codefragment>
<fullcode><![CDATA[  (1..10)    === 5
  (1..10)    === 15
  (1..10)    === 3.14159
  ('a'..'j') === 'c'
  ('a'..'j') === 'z'
]]></fullcode><rubycode>
<tr>
  <td><tt>(1..10)<nbsp/><nbsp/><nbsp/><nbsp/>===<nbsp/>5</tt></td>
  <td>&#187;</td>
  <td><tt>true</tt></td>
</tr>
<tr>
  <td><tt>(1..10)<nbsp/><nbsp/><nbsp/><nbsp/>===<nbsp/>15</tt></td>
  <td>&#187;</td>
  <td><tt>false</tt></td>
</tr>
<tr>
  <td><tt>(1..10)<nbsp/><nbsp/><nbsp/><nbsp/>===<nbsp/>3.14159</tt></td>
  <td>&#187;</td>
  <td><tt>true</tt></td>
</tr>
<tr>
  <td><tt>('a'..'j')<nbsp/>===<nbsp/>'c'</tt></td>
  <td>&#187;</td>
  <td><tt>true</tt></td>
</tr>
<tr>
  <td><tt>('a'..'j')<nbsp/>===<nbsp/>'z'</tt></td>
  <td>&#187;</td>
  <td><tt>false</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
The example of a case expression on page 81 shows this
test in action, determining a jazz style given a year.
<section>Regular Expressions</section>
<p/>
Back on page 50 when we were creating a song list
from a file, we used a regular expression to match the field delimiter
in the input file. We claimed that the expression <tt>line.split(/\s*\|\s*/)</tt> matched a vertical bar surrounded by optional
whitespace. Let's explore regular expressions in more detail to see why
this claim is true.
<p/>
Regular expressions are used to match patterns against strings.
Ruby provides built-in support that makes pattern matching and
substitution convenient and concise. In this section we'll work
through all the main features of regular expressions. There are some
details we won't cover: have a look at page 205 for
more information.
<p/>
Regular expressions are objects of type <tt>Regexp</tt>. They can be
created by calling the constructor explicitly or by using the literal
forms /<em>pattern</em>/ and %r\<em>pattern</em>\.
<p/>
<codefragment>
<fullcode><![CDATA[a = Regexp.new('^\s*[a-z]')
b = /^\s*[a-z]/
c = %r{^\s*[a-z]}
]]></fullcode><rubycode>
<tr>
  <td><tt>a<nbsp/>=<nbsp/>Regexp.new('^\s*[a-z]')</tt></td>
  <td>&#187;</td>
  <td><tt>/^\s*[a-z]/</tt></td>
</tr>
<tr>
  <td><tt>b<nbsp/>=<nbsp/>/^\s*[a-z]/</tt></td>
  <td>&#187;</td>
  <td><tt>/^\s*[a-z]/</tt></td>
</tr>
<tr>
  <td><tt>c<nbsp/>=<nbsp/>%r{^\s*[a-z]}</tt></td>
  <td>&#187;</td>
  <td><tt>/^\s*[a-z]/</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
Once you have a regular expression object, you can match it against a
string using <tt>Regexp#match(<obj>aString</obj>)</tt>
or the match
operators <tt>=~</tt>
(positive match) and <tt>!~</tt>
(negative match). The
match operators are defined for both <tt>String</tt> and <tt>Regexp</tt> objects.
If both operands of the match operator are <tt>Strings</tt>, the one on
the right will be converted to a regular expression.
<p/>
<codefragment>
<fullcode><![CDATA[  a = "Fats Waller"
  a =~ /a/
  a =~ /z/
  a =~ "ll"
]]></fullcode><rubycode>
<tr>
<td colspan="3"><tt>a<nbsp/>=<nbsp/>"Fats<nbsp/>Waller"</tt></td>
</tr>
<tr>
  <td><tt>a<nbsp/>=~<nbsp/>/a/</tt></td>
  <td>&#187;</td>
  <td><tt>1</tt></td>
</tr>
<tr>
  <td><tt>a<nbsp/>=~<nbsp/>/z/</tt></td>
  <td>&#187;</td>
  <td><tt>nil</tt></td>
</tr>
<tr>
  <td><tt>a<nbsp/>=~<nbsp/>"ll"</tt></td>
  <td>&#187;</td>
  <td><tt>7</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
The match operators return the character position at which the match
occurred. They also have the side effect of setting a whole load of
Ruby variables. <var>$&amp;</var> receives the part of the string that was
matched by the pattern, <var>$`</var> receives the part of the string
that preceded the match, and <var>$'</var> receives the string after the
match. We can use this to write a method, <meth>showRE</meth>, which
illustrates where a particular pattern matches.
<p/>
<codefragment>
<fullcode><![CDATA[!-class String
!- def inspect
!-  to_s
!- end
!-end
def showRE(a,re)
  if a =~ re
    "#{$`}<<#{$&}>>#{$'}"
  else
    "no match"
  end
end

showRE('very interesting', /t/)
showRE('Fats Waller', /ll/)
]]></fullcode><rubycode>
<tr>
<td colspan="3"><tt>def<nbsp/>showRE(a,re)</tt></td>
</tr>
<tr>
<td colspan="3"><tt><nbsp/><nbsp/>if<nbsp/>a<nbsp/>=~<nbsp/>re</tt></td>
</tr>
<tr>
<td colspan="3"><tt><nbsp/><nbsp/><nbsp/><nbsp/>"#{$`}&lt;&lt;#{$&amp;}&gt;&gt;#{$'}"</tt></td>
</tr>
<tr>
<td colspan="3"><tt><nbsp/><nbsp/>else</tt></td>
</tr>
<tr>
<td colspan="3"><tt><nbsp/><nbsp/><nbsp/><nbsp/>"no<nbsp/>match"</tt></td>
</tr>
<tr>
<td colspan="3"><tt><nbsp/><nbsp/>end</tt></td>
</tr>
<tr>
<td colspan="3"><tt>end</tt></td>
</tr>
<tr>
<td colspan="3"><tt></tt></td>
</tr>
<tr>
  <td><tt>showRE('very<nbsp/>interesting',<nbsp/>/t/)</tt></td>
  <td>&#187;</td>
  <td><tt>very<nbsp/>in&lt;&lt;t&gt;&gt;eresting</tt></td>
</tr>
<tr>
  <td><tt>showRE('Fats<nbsp/>Waller',<nbsp/>/ll/)</tt></td>
  <td>&#187;</td>
  <td><tt>Fats<nbsp/>Wa&lt;&lt;ll&gt;&gt;er</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
The match also sets the thread-global variables <var>$~</var> and
<var>$1</var> through <var>$9</var>.
The variable <var>$~</var> is a <classname>MatchData</classname> object
(described beginning on page 336) that holds everything you might
want to know about the match. <var>$1</var> and so on hold the values of
parts of the match. We'll talk about these later. And for people who
cringe when they see these Perl-like variable names, stay
tuned. There's good news at the end of the chapter.
<subsection>Patterns</subsection>
<p/>
Every regular expression  contains a pattern, which is used to match
the regular expression against a string.
<p/>
Within a pattern, all characters except ., |, (, ), [, {, +, \,
^, $, *, and ? match themselves.
<p/>
<codefragment>
<fullcode><![CDATA[!-class String
!- def inspect
!-  to_s
!- end
!-end
!-def showRE(a,re)
!-  if a =~ re
!-    "#{$`}<<#{$&}>>#{$'}"
!-  else
!-    "no match"
!-  end
!-end
showRE('kangaroo', /angar/)
showRE('!@%&-_=+', /%&/)
]]></fullcode><rubycode>
<tr>
  <td><tt>showRE('kangaroo',<nbsp/>/angar/)</tt></td>
  <td>&#187;</td>
  <td><tt>k&lt;&lt;angar&gt;&gt;oo</tt></td>
</tr>
<tr>
  <td><tt>showRE('!@%&amp;-_=+',<nbsp/>/%&amp;/)</tt></td>
  <td>&#187;</td>
  <td><tt>!@&lt;&lt;%&amp;&gt;&gt;-_=+</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
If you want to match one of these special characters literally,
precede it with a backslash.
This explains part of the pattern we used
to split the song line, <tt>/\s*\|\s*/</tt>. The <tt>\|</tt> means 
``match a vertical bar.'' Without the backslash, the ``<tt>|</tt>'' would have
meant <em>alternation</em> (which we'll describe later).
<p/>
<codefragment>
<fullcode><![CDATA[!-class String
!- def inspect
!-  to_s
!- end
!-end
!-def showRE(a,re)
!-  if a =~ re
!-    "#{$`}<<#{$&}>>#{$'}"
!-  else
!-    "no match"
!-  end
!-end
showRE('yes | no', /\|/)
showRE('yes (no)', /\(no\)/)
showRE('are you sure?', /e\?/)
]]></fullcode><rubycode>
<tr>
  <td><tt>showRE('yes<nbsp/>|<nbsp/>no',<nbsp/>/\|/)</tt></td>
  <td>&#187;</td>
  <td><tt>yes<nbsp/>&lt;&lt;|&gt;&gt;<nbsp/>no</tt></td>
</tr>
<tr>
  <td><tt>showRE('yes<nbsp/>(no)',<nbsp/>/\(no\)/)</tt></td>
  <td>&#187;</td>
  <td><tt>yes<nbsp/>&lt;&lt;(no)&gt;&gt;</tt></td>
</tr>
<tr>
  <td><tt>showRE('are<nbsp/>you<nbsp/>sure?',<nbsp/>/e\?/)</tt></td>
  <td>&#187;</td>
  <td><tt>are<nbsp/>you<nbsp/>sur&lt;&lt;e?&gt;&gt;</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
A backslash followed by an alphanumeric character is used to introduce
a special match construct, which we'll cover later.  In addition, a
regular expression may contain <tt>#{...}</tt> expression
substitutions.
<subsubsection>Anchors</subsubsection>
<p/>
By default, a regular expression will try to find the first match for
the pattern in a string. Match <tt>/iss/</tt> against the string
``Mississippi,'' and it will find the substring ``iss'' starting at
position one. But what if you want to force a pattern to match only at
the start or end of a string?
<p/>
The patterns <tt>^</tt>
and <tt>$</tt> match the beginning and end of a
line, respectively.  These are often used to <em>anchor</em> a pattern
match: for example, <tt>/^option/</tt> matches the word ``option'' only
if it appears at the start of a line. The sequence <tt>\A</tt> matches
the beginning of a string, and <tt>\z</tt> and <tt>\Z</tt> match the
end of a string.  (Actually, <tt>\Z</tt> matches the end of a string
<em>unless</em> the string ends with a ``\n'', it which case it
matches just before the ``\n''.)
<p/>
<codefragment>
<fullcode><![CDATA[!-class String
!- def inspect
!-  to_s
!- end
!-end
!-def showRE(a,re)
!-  if a =~ re
!-    "#{$`}<<#{$&}>>#{$'}"
!-  else
!-    "no match"
!-  end
!-end
showRE("this is\nthe time", /^the/)
showRE("this is\nthe time", /is$/)
showRE("this is\nthe time", /\Athis/)
showRE("this is\nthe time", /\Athe/)
]]></fullcode><rubycode>
<tr>
  <td><tt>showRE("this<nbsp/>is\nthe<nbsp/>time",<nbsp/>/^the/)</tt></td>
  <td>&#187;</td>
  <td><tt>this<nbsp/>is\n&lt;&lt;the&gt;&gt;<nbsp/>time</tt></td>
</tr>
<tr>
  <td><tt>showRE("this<nbsp/>is\nthe<nbsp/>time",<nbsp/>/is$/)</tt></td>
  <td>&#187;</td>
  <td><tt>this<nbsp/>&lt;&lt;is&gt;&gt;\nthe<nbsp/>time</tt></td>
</tr>
<tr>
  <td><tt>showRE("this<nbsp/>is\nthe<nbsp/>time",<nbsp/>/\Athis/)</tt></td>
  <td>&#187;</td>
  <td><tt>&lt;&lt;this&gt;&gt;<nbsp/>is\nthe<nbsp/>time</tt></td>
</tr>
<tr>
  <td><tt>showRE("this<nbsp/>is\nthe<nbsp/>time",<nbsp/>/\Athe/)</tt></td>
  <td>&#187;</td>
  <td><tt>no<nbsp/>match</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
Similarly, the patterns <tt>\b</tt> and <tt>\B</tt> match word boundaries
and nonword boundaries, respectively. Word characters are letters,
numbers, and underscore.
<p/>
<codefragment>
<fullcode><![CDATA[!-class String
!- def inspect
!-  to_s
!- end
!-end
!-def showRE(a,re)
!-  if a =~ re
!-    "#{$`}<<#{$&}>>#{$'}"
!-  else
!-    "no match"
!-  end
!-end
showRE("this is\nthe time", /\bis/)
showRE("this is\nthe time", /\Bis/)
]]></fullcode><rubycode>
<tr>
  <td><tt>showRE("this<nbsp/>is\nthe<nbsp/>time",<nbsp/>/\bis/)</tt></td>
  <td>&#187;</td>
  <td><tt>this<nbsp/>&lt;&lt;is&gt;&gt;\nthe<nbsp/>time</tt></td>
</tr>
<tr>
  <td><tt>showRE("this<nbsp/>is\nthe<nbsp/>time",<nbsp/>/\Bis/)</tt></td>
  <td>&#187;</td>
  <td><tt>th&lt;&lt;is&gt;&gt;<nbsp/>is\nthe<nbsp/>time</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<subsubsection>Character Classes</subsubsection>
<p/>
A character class is a set of characters between brackets:
<tt>[</tt><em>characters</em><tt>]</tt> matches any single character between the
brackets. <tt>[aeiou]</tt> will match a vowel, <tt>[,.:;!?]</tt> matches
punctuation, and so on. The significance of the special regular
expression characters---<tt>.|()[{+^$*?</tt>---is turned off inside
the brackets. However, normal string substitution still occurs, so
(for example) <tt>\b</tt> represents a backspace character and
<tt>\n</tt> a newline (see Table 18.2 on page 203).  In addition,
you can use the abbreviations shown in Table 5.1 on page 59, so
that (for example) <tt>\s</tt> matches any whitespace character, not
just a literal space.
<p/>
<codefragment>
<fullcode><![CDATA[!-class String
!- def inspect
!-  to_s
!- end
!-end
!-def showRE(a,re)
!-  if a =~ re
!-    "#{$`}<<#{$&}>>#{$'}"
!-  else
!-    "no match"
!-  end
!-end
showRE('It costs $12.', /[aeiou]/)
showRE('It costs $12.', /[\s]/)
]]></fullcode><rubycode>
<tr>
  <td><tt>showRE('It<nbsp/>costs<nbsp/>$12.',<nbsp/>/[aeiou]/)</tt></td>
  <td>&#187;</td>
  <td><tt>It<nbsp/>c&lt;&lt;o&gt;&gt;sts<nbsp/>$12.</tt></td>
</tr>
<tr>
  <td><tt>showRE('It<nbsp/>costs<nbsp/>$12.',<nbsp/>/[\s]/)</tt></td>
  <td>&#187;</td>
  <td><tt>It&lt;&lt;<nbsp/>&gt;&gt;costs<nbsp/>$12.</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
Within the brackets, the sequence c<sub>1</sub>-c<sub>2</sub> represents all the
characters between c<sub>1</sub> and c<sub>2</sub>, inclusive. 
<p/>
If you want to include the literal characters <tt>]</tt> and <tt>-</tt> within
a character class, they must appear at the start.
<p/>
<codefragment>
<fullcode><![CDATA[!-class String
!- def inspect
!-  to_s
!- end
!-end
!-def showRE(a,re)
!-  if a =~ re
!-    "#{$`}<<#{$&}>>#{$'}"
!-  else
!-    "no match"
!-  end
!-end
a = 'Gamma [Design Patterns-page 123]'
showRE(a, /[]]/)
showRE(a, /[B-F]/)
showRE(a, /[-]/)
showRE(a, /[0-9]/)
]]></fullcode><rubycode>
<tr>
<td colspan="3"><tt>a<nbsp/>=<nbsp/>'Gamma<nbsp/>[Design<nbsp/>Patterns-page<nbsp/>123]'</tt></td>
</tr>
<tr>
  <td><tt>showRE(a,<nbsp/>/[]]/)</tt></td>
  <td>&#187;</td>
  <td><tt>Gamma<nbsp/>[Design<nbsp/>Patterns-page<nbsp/>123&lt;&lt;]&gt;&gt;</tt></td>
</tr>
<tr>
  <td><tt>showRE(a,<nbsp/>/[B-F]/)</tt></td>
  <td>&#187;</td>
  <td><tt>Gamma<nbsp/>[&lt;&lt;D&gt;&gt;esign<nbsp/>Patterns-page<nbsp/>123]</tt></td>
</tr>
<tr>
  <td><tt>showRE(a,<nbsp/>/[-]/)</tt></td>
  <td>&#187;</td>
  <td><tt>Gamma<nbsp/>[Design<nbsp/>Patterns&lt;&lt;-&gt;&gt;page<nbsp/>123]</tt></td>
</tr>
<tr>
  <td><tt>showRE(a,<nbsp/>/[0-9]/)</tt></td>
  <td>&#187;</td>
  <td><tt>Gamma<nbsp/>[Design<nbsp/>Patterns-page<nbsp/>&lt;&lt;1&gt;&gt;23]</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
Put a <tt>^</tt>
immediately after the opening bracket to negate a
character class: <tt>[^a-z]</tt> matches any character that isn't a
lowercase alphabetic.
<p/>
Some character classes are used so frequently that Ruby provides
abbreviations for them. These abbreviations are listed in Table
5.1 on page 59---they may be used both within brackets and in
the body of a pattern.
<p/>
<codefragment>
<fullcode><![CDATA[!-class String
!- def inspect
!-  to_s
!- end
!-end
!-def showRE(a,re)
!-  if a =~ re
!-    "#{$`}<<#{$&}>>#{$'}"
!-  else
!-    "no match"
!-  end
!-end
showRE('It costs $12.', /\s/)
showRE('It costs $12.', /\d/)
]]></fullcode><rubycode>
<tr>
  <td><tt>showRE('It<nbsp/>costs<nbsp/>$12.',<nbsp/>/\s/)</tt></td>
  <td>&#187;</td>
  <td><tt>It&lt;&lt;<nbsp/>&gt;&gt;costs<nbsp/>$12.</tt></td>
</tr>
<tr>
  <td><tt>showRE('It<nbsp/>costs<nbsp/>$12.',<nbsp/>/\d/)</tt></td>
  <td>&#187;</td>
  <td><tt>It<nbsp/>costs<nbsp/>$&lt;&lt;1&gt;&gt;2.</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
<figure type="table">
  <caption>Character class abbreviations</caption>
  <table>
<th>
  <td><b>Sequence</b></td>
  <td><b>As [ ... ]</b></td>
  <td><b>Meaning</b></td>
</th>
<tr>
  <td><tt>\d</tt></td>
  <td>[0-9]</td>
  <td>Digit character</td>
</tr>
<tr>
  <td><tt>\D</tt></td>
  <td>[^0-9]</td>
  <td>Nondigit</td>
</tr>
<tr>
  <td><tt>\s</tt></td>
  <td>[\s\t\r\n\f]</td>
  <td>Whitespace character</td>
</tr>
<tr>
  <td><tt>\S</tt></td>
  <td>[^\s\t\r\n\f]</td>
  <td>Nonwhitespace character</td>
</tr>
<tr>
  <td><tt>\w</tt></td>
  <td>[A-Za-z0-9_]</td>
  <td>Word character</td>
</tr>
<tr>
  <td><tt>\W</tt></td>
  <td>[^A-Za-z0-9_]</td>
  <td>Nonword character</td>
</tr>
<bottomrule/></table>
<p/>
</figure>
<p/>
Finally, a period (``.'') appearing outside brackets represents any
character except a newline (and in multiline mode it matches a newline,
too).
<p/>
<codefragment>
<fullcode><![CDATA[!-class String
!- def inspect
!-  to_s
!- end
!-end
!-def showRE(a,re)
!-  if a =~ re
!-    "#{$`}<<#{$&}>>#{$'}"
!-  else
!-    "no match"
!-  end
!-end
a = 'It costs $12.'
showRE(a, /c.s/)
showRE(a, /./)
showRE(a, /\./)
]]></fullcode><rubycode>
<tr>
<td colspan="3"><tt>a<nbsp/>=<nbsp/>'It<nbsp/>costs<nbsp/>$12.'</tt></td>
</tr>
<tr>
  <td><tt>showRE(a,<nbsp/>/c.s/)</tt></td>
  <td>&#187;</td>
  <td><tt>It<nbsp/>&lt;&lt;cos&gt;&gt;ts<nbsp/>$12.</tt></td>
</tr>
<tr>
  <td><tt>showRE(a,<nbsp/>/./)</tt></td>
  <td>&#187;</td>
  <td><tt>&lt;&lt;I&gt;&gt;t<nbsp/>costs<nbsp/>$12.</tt></td>
</tr>
<tr>
  <td><tt>showRE(a,<nbsp/>/\./)</tt></td>
  <td>&#187;</td>
  <td><tt>It<nbsp/>costs<nbsp/>$12&lt;&lt;.&gt;&gt;</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<subsubsection>Repetition</subsubsection>
<p/>
When we specified the pattern that split the song list line, <tt>/\s*\|\s*/</tt>, we said we wanted to match a vertical bar surrounded by
an arbitrary amount of whitespace. We now know that the <tt>\s</tt>
sequences match a single whitespace character, so it seems likely that 
the asterisks somehow mean ``an arbitrary amount.'' In fact, the
asterisk is one of a number of modifiers that allow you to match multiple
occurrences of a pattern.
<p/>
If <em>r</em> stands for the immediately preceding regular expression
within a pattern, then:
<p/>
<table>
<tr>
  <td><em>r</em><tt>*</tt></td>
  <td>matches zero or more occurrences of <em>r</em>.</td>
</tr>
<tr>
  <td><em>r</em><tt>+</tt></td>
  <td>matches one or more occurrences of <em>r</em>.</td>
</tr>
<tr>
  <td><em>r</em><tt>?</tt></td>
  <td>matches zero or one occurrence of <em>r</em>.</td>
</tr>
<tr>
  <td><em>r</em><tt>{m,n}</tt></td>
  <td>matches at least ``m'' and at most ``n'' occurrences of <em>r</em>.</td>
</tr>
<tr>
  <td><em>r</em><tt>{m,}</tt></td>
  <td>matches at least ``m'' occurrences of <em>r</em>.</td>
</tr>
</table>
<p/>
These repetition constructs have a high precedence---they bind only to
the immediately preceding regular expression in the
pattern. <tt>/ab+/</tt> matches an ``a'' followed by one or more ``b''s,
not a sequence of ``ab''s. You have to be careful with the <tt>*</tt>
construct too---the pattern /a*/ will match any string; every string
has zero or more ``a''s.
<p/>
These patterns are called <em>greedy</em>,
because by default they will
match as much of the string as they can. You can alter this
behavior, and have them match the minimum, by adding a question mark
suffix. 
<p/>
<codefragment>
<fullcode><![CDATA[!-class String
!- def inspect
!-  to_s
!- end
!-end
!-def showRE(a,re)
!-  if a =~ re
!-    "#{$`}<<#{$&}>>#{$'}"
!-  else
!-    "no match"
!-  end
!-end
a = "The moon is made of cheese"
showRE(a, /\w+/)
showRE(a, /\s.*\s/)
showRE(a, /\s.*?\s/)
showRE(a, /[aeiou]{2,99}/)
showRE(a, /mo?o/)
]]></fullcode><rubycode>
<tr>
<td colspan="3"><tt>a<nbsp/>=<nbsp/>"The<nbsp/>moon<nbsp/>is<nbsp/>made<nbsp/>of<nbsp/>cheese"</tt></td>
</tr>
<tr>
  <td><tt>showRE(a,<nbsp/>/\w+/)</tt></td>
  <td>&#187;</td>
  <td><tt>&lt;&lt;The&gt;&gt;<nbsp/>moon<nbsp/>is<nbsp/>made<nbsp/>of<nbsp/>cheese</tt></td>
</tr>
<tr>
  <td><tt>showRE(a,<nbsp/>/\s.*\s/)</tt></td>
  <td>&#187;</td>
  <td><tt>The&lt;&lt;<nbsp/>moon<nbsp/>is<nbsp/>made<nbsp/>of<nbsp/>&gt;&gt;cheese</tt></td>
</tr>
<tr>
  <td><tt>showRE(a,<nbsp/>/\s.*?\s/)</tt></td>
  <td>&#187;</td>
  <td><tt>The&lt;&lt;<nbsp/>moon<nbsp/>&gt;&gt;is<nbsp/>made<nbsp/>of<nbsp/>cheese</tt></td>
</tr>
<tr>
  <td><tt>showRE(a,<nbsp/>/[aeiou]{2,99}/)</tt></td>
  <td>&#187;</td>
  <td><tt>The<nbsp/>m&lt;&lt;oo&gt;&gt;n<nbsp/>is<nbsp/>made<nbsp/>of<nbsp/>cheese</tt></td>
</tr>
<tr>
  <td><tt>showRE(a,<nbsp/>/mo?o/)</tt></td>
  <td>&#187;</td>
  <td><tt>The<nbsp/>&lt;&lt;moo&gt;&gt;n<nbsp/>is<nbsp/>made<nbsp/>of<nbsp/>cheese</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<subsubsection>Alternation</subsubsection>
<p/>
We know that the vertical bar is special, because our line splitting
pattern had to escape it with a backslash. That's because an unescaped 
vertical bar ``|'' matches either the regular expression that
precedes it or the regular expression that follows it.
<p/>
<codefragment>
<fullcode><![CDATA[!-class String
!- def inspect
!-  to_s
!- end
!-end
!-def showRE(a,re)
!-  if a =~ re
!-    "#{$`}<<#{$&}>>#{$'}"
!-  else
!-    "no match"
!-  end
!-end
a = "red ball blue sky"
showRE(a, /d|e/)
showRE(a, /al|lu/)
showRE(a, /red ball|angry sky/)
]]></fullcode><rubycode>
<tr>
<td colspan="3"><tt>a<nbsp/>=<nbsp/>"red<nbsp/>ball<nbsp/>blue<nbsp/>sky"</tt></td>
</tr>
<tr>
  <td><tt>showRE(a,<nbsp/>/d|e/)</tt></td>
  <td>&#187;</td>
  <td><tt>r&lt;&lt;e&gt;&gt;d<nbsp/>ball<nbsp/>blue<nbsp/>sky</tt></td>
</tr>
<tr>
  <td><tt>showRE(a,<nbsp/>/al|lu/)</tt></td>
  <td>&#187;</td>
  <td><tt>red<nbsp/>b&lt;&lt;al&gt;&gt;l<nbsp/>blue<nbsp/>sky</tt></td>
</tr>
<tr>
  <td><tt>showRE(a,<nbsp/>/red<nbsp/>ball|angry<nbsp/>sky/)</tt></td>
  <td>&#187;</td>
  <td><tt>&lt;&lt;red<nbsp/>ball&gt;&gt;<nbsp/>blue<nbsp/>sky</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
There's a trap for the unwary here, as ``|'' has a very low precedence.
The last example above matches ``red ball'' or ``angry sky'', not ``red
ball sky'' or ``red angry sky''. To match ``red ball sky'' or ``red
angry sky'', you'd need to override
the default precedence using grouping.
<subsubsection>Grouping</subsubsection>
<p/>
You can use parentheses to group terms within a regular
expression. Everything within the group is treated as a single regular
expression.
<p/>
<codefragment>
<fullcode><![CDATA[!-class String
!- def inspect
!-  to_s
!- end
!-end
!-def showRE(a,re)
!-  if a =~ re
!-    "#{$`}<<#{$&}>>#{$'}"
!-  else
!-    "no match"
!-  end
!-end
showRE('banana', /an*/)
showRE('banana', /(an)*/)
showRE('banana', /(an)+/)
]]></fullcode><rubycode>
<tr>
  <td><tt>showRE('banana',<nbsp/>/an*/)</tt></td>
  <td>&#187;</td>
  <td><tt>b&lt;&lt;an&gt;&gt;ana</tt></td>
</tr>
<tr>
  <td><tt>showRE('banana',<nbsp/>/(an)*/)</tt></td>
  <td>&#187;</td>
  <td><tt>&lt;&lt;&gt;&gt;banana</tt></td>
</tr>
<tr>
  <td><tt>showRE('banana',<nbsp/>/(an)+/)</tt></td>
  <td>&#187;</td>
  <td><tt>b&lt;&lt;anan&gt;&gt;a</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
<codefragment>
<fullcode><![CDATA[!-class String
!- def inspect
!-  to_s
!- end
!-end
!-def showRE(a,re)
!-  if a =~ re
!-    "#{$`}<<#{$&}>>#{$'}"
!-  else
!-    "no match"
!-  end
!-end
a = 'red ball blue sky'
showRE(a, /blue|red/)
showRE(a, /(blue|red) \w+/)
showRE(a, /(red|blue) \w+/)
showRE(a, /red|blue \w+/)
]]></fullcode><rubycode>
<tr>
<td colspan="3"><tt>a<nbsp/>=<nbsp/>'red<nbsp/>ball<nbsp/>blue<nbsp/>sky'</tt></td>
</tr>
<tr>
  <td><tt>showRE(a,<nbsp/>/blue|red/)</tt></td>
  <td>&#187;</td>
  <td><tt>&lt;&lt;red&gt;&gt;<nbsp/>ball<nbsp/>blue<nbsp/>sky</tt></td>
</tr>
<tr>
  <td><tt>showRE(a,<nbsp/>/(blue|red)<nbsp/>\w+/)</tt></td>
  <td>&#187;</td>
  <td><tt>&lt;&lt;red<nbsp/>ball&gt;&gt;<nbsp/>blue<nbsp/>sky</tt></td>
</tr>
<tr>
  <td><tt>showRE(a,<nbsp/>/(red|blue)<nbsp/>\w+/)</tt></td>
  <td>&#187;</td>
  <td><tt>&lt;&lt;red<nbsp/>ball&gt;&gt;<nbsp/>blue<nbsp/>sky</tt></td>
</tr>
<tr>
  <td><tt>showRE(a,<nbsp/>/red|blue<nbsp/>\w+/)</tt></td>
  <td>&#187;</td>
  <td><tt>&lt;&lt;red&gt;&gt;<nbsp/>ball<nbsp/>blue<nbsp/>sky</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
<codefragment>
<fullcode><![CDATA[!-class String
!- def inspect
!-  to_s
!- end
!-end
!-def showRE(a,re)
!-  if a =~ re
!-    "#{$`}<<#{$&}>>#{$'}"
!-  else
!-    "no match"
!-  end
!-end
!-a = 'red ball blue sky'
showRE(a, /red (ball|angry) sky/)
a = 'the red angry sky'
showRE(a, /red (ball|angry) sky/)
]]></fullcode><rubycode>
<tr>
  <td><tt>showRE(a,<nbsp/>/red<nbsp/>(ball|angry)<nbsp/>sky/)</tt></td>
  <td>&#187;</td>
  <td><tt>no<nbsp/>match</tt></td>
</tr>
<tr>
<td colspan="3"><tt>a<nbsp/>=<nbsp/>'the<nbsp/>red<nbsp/>angry<nbsp/>sky'</tt></td>
</tr>
<tr>
  <td><tt>showRE(a,<nbsp/>/red<nbsp/>(ball|angry)<nbsp/>sky/)</tt></td>
  <td>&#187;</td>
  <td><tt>the<nbsp/>&lt;&lt;red<nbsp/>angry<nbsp/>sky&gt;&gt;</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
Parentheses are also used to collect the results of pattern
matching. Ruby counts opening parentheses, and for each stores the result of
the partial match between it and the corresponding closing
parenthesis.   You can use this partial match both within the remainder
of the pattern and in your Ruby program. Within the pattern, the
sequence <tt>\1</tt> refers to the match of the first group, <tt>\2</tt> the
second group, and so on. Outside the pattern, the special variables
<tt>$1</tt>, <tt>$2</tt>, and so on, serve the same purpose.
<p/>
<codefragment>
<fullcode><![CDATA[  "12:50am" =~ /(\d\d):(\d\d)(..)/
  "Hour is #$1, minute #$2"
  "12:50am" =~ /((\d\d):(\d\d))(..)/
  "Time is #$1"
  "Hour is #$2, minute #$3"
  "AM/PM is #$4"
]]></fullcode><rubycode>
<tr>
  <td><tt>"12:50am"<nbsp/>=~<nbsp/>/(\d\d):(\d\d)(..)/</tt></td>
  <td>&#187;</td>
  <td><tt>0</tt></td>
</tr>
<tr>
  <td><tt>"Hour<nbsp/>is<nbsp/>#$1,<nbsp/>minute<nbsp/>#$2"</tt></td>
  <td>&#187;</td>
  <td><tt>"Hour<nbsp/>is<nbsp/>12,<nbsp/>minute<nbsp/>50"</tt></td>
</tr>
<tr>
  <td><tt>"12:50am"<nbsp/>=~<nbsp/>/((\d\d):(\d\d))(..)/</tt></td>
  <td>&#187;</td>
  <td><tt>0</tt></td>
</tr>
<tr>
  <td><tt>"Time<nbsp/>is<nbsp/>#$1"</tt></td>
  <td>&#187;</td>
  <td><tt>"Time<nbsp/>is<nbsp/>12:50"</tt></td>
</tr>
<tr>
  <td><tt>"Hour<nbsp/>is<nbsp/>#$2,<nbsp/>minute<nbsp/>#$3"</tt></td>
  <td>&#187;</td>
  <td><tt>"Hour<nbsp/>is<nbsp/>12,<nbsp/>minute<nbsp/>50"</tt></td>
</tr>
<tr>
  <td><tt>"AM/PM<nbsp/>is<nbsp/>#$4"</tt></td>
  <td>&#187;</td>
  <td><tt>"AM/PM<nbsp/>is<nbsp/>am"</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
The ability to use part of the current match later in that match
allows you to look for various forms of repetition.
<p/>
<codefragment>
<fullcode><![CDATA[!-class String
!- def inspect
!-  to_s
!- end
!-end
!-def showRE(a,re)
!-  if a =~ re
!-    "#{$`}<<#{$&}>>#{$'}"
!-  else
!-    "no match"
!-  end
!-end
  # match duplicated letter
  showRE('He said "Hello"', /(\w)\1/)
  # match duplicated substrings
  showRE('Mississippi', /(\w+)\1/)
]]></fullcode><rubycode>
<tr>
<td colspan="3"><tt>#<nbsp/>match<nbsp/>duplicated<nbsp/>letter</tt></td>
</tr>
<tr>
  <td><tt>showRE('He<nbsp/>said<nbsp/>"Hello"',<nbsp/>/(\w)\1/)</tt></td>
  <td>&#187;</td>
  <td><tt>He<nbsp/>said<nbsp/>"He&lt;&lt;ll&gt;&gt;o"</tt></td>
</tr>
<tr>
<td colspan="3"><tt>#<nbsp/>match<nbsp/>duplicated<nbsp/>substrings</tt></td>
</tr>
<tr>
  <td><tt>showRE('Mississippi',<nbsp/>/(\w+)\1/)</tt></td>
  <td>&#187;</td>
  <td><tt>M&lt;&lt;ississ&gt;&gt;ippi</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
You can also use back references to match delimiters.
<p/>
<codefragment>
<fullcode><![CDATA[!-class String
!- def inspect
!-  to_s
!- end
!-end
!-def showRE(a,re)
!-  if a =~ re
!-    "#{$`}<<#{$&}>>#{$'}"
!-  else
!-    "no match"
!-  end
!-end
  showRE('He said "Hello"', /(["']).*?\1/)
  showRE("He said 'Hello'", /(["']).*?\1/)
]]></fullcode><rubycode>
<tr>
  <td><tt>showRE('He<nbsp/>said<nbsp/>"Hello"',<nbsp/>/(["']).*?\1/)</tt></td>
  <td>&#187;</td>
  <td><tt>He<nbsp/>said<nbsp/>&lt;&lt;"Hello"&gt;&gt;</tt></td>
</tr>
<tr>
  <td><tt>showRE("He<nbsp/>said<nbsp/>'Hello'",<nbsp/>/(["']).*?\1/)</tt></td>
  <td>&#187;</td>
  <td><tt>He<nbsp/>said<nbsp/>&lt;&lt;'Hello'&gt;&gt;</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<subsection>Pattern-Based Substitution</subsection>
<p/>
Sometimes finding a pattern in a string is good enough.
If a friend
challenges you to find a word that contains the letters a, b, c, d,
and e in order, you could search a word list with the pattern
<tt>/a.*b.*c.*d.*e/</tt> and find ``absconded'' and ``ambuscade.'' That
has to be worth something.
<p/>
However, there are times when you need to change things based on a
pattern match. Let's go back to our song list file. Whoever created it 
entered all the artists' names in lowercase. When we display them on
our jukebox's screen, they'd look better in mixed case. How can we
change the first character of each word to uppercase?
<p/>
The methods <cim><file>string</file><front>String</front><back>sub</back><mref>sub</mref></cim> and <cim><file>string</file><front>String</front><back>gsub</back><mref>gsub</mref></cim> look for a portion of
a string matching their first argument and replace it with their
second argument. <cim><file>string</file><front>String</front><back>sub</back><mref>sub</mref></cim> performs one replacement, while
<cim><file>string</file><front>String</front><back>gsub</back><mref>gsub</mref></cim> replaces every occurrence of the match. Both routines
return a new copy of the <tt>String</tt> containing the substitutions.
Mutator versions <cim><file>string</file><front>String</front><back>sub!</back><mref>sub_oh</mref></cim> and <cim><file>string</file><front>String</front><back>gsub!</back><mref>gsub_oh</mref></cim> modify the
original string.
<p/>
<codefragment>
<fullcode><![CDATA[a = "the quick brown fox"
a.sub(/[aeiou]/,  '*')
a.gsub(/[aeiou]/, '*')
a.sub(/\s\S+/,  '')
a.gsub(/\s\S+/, '')
]]></fullcode><rubycode>
<tr>
<td colspan="3"><tt>a<nbsp/>=<nbsp/>"the<nbsp/>quick<nbsp/>brown<nbsp/>fox"</tt></td>
</tr>
<tr>
  <td><tt>a.sub(/[aeiou]/,<nbsp/><nbsp/>'*')</tt></td>
  <td>&#187;</td>
  <td><tt>"th*<nbsp/>quick<nbsp/>brown<nbsp/>fox"</tt></td>
</tr>
<tr>
  <td><tt>a.gsub(/[aeiou]/,<nbsp/>'*')</tt></td>
  <td>&#187;</td>
  <td><tt>"th*<nbsp/>q**ck<nbsp/>br*wn<nbsp/>f*x"</tt></td>
</tr>
<tr>
  <td><tt>a.sub(/\s\S+/,<nbsp/><nbsp/>'')</tt></td>
  <td>&#187;</td>
  <td><tt>"the<nbsp/>brown<nbsp/>fox"</tt></td>
</tr>
<tr>
  <td><tt>a.gsub(/\s\S+/,<nbsp/>'')</tt></td>
  <td>&#187;</td>
  <td><tt>"the"</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
The second argument to both functions can be either a <tt>String</tt> or
a block. If a block is used, the block's value is substituted into the
<classname>String</classname>.
<p/>
<codefragment>
<fullcode><![CDATA[a = "the quick brown fox"
a.sub(/^./) { $&.upcase }
a.gsub(/[aeiou]/) { $&.upcase }
]]></fullcode><rubycode>
<tr>
<td colspan="3"><tt>a<nbsp/>=<nbsp/>"the<nbsp/>quick<nbsp/>brown<nbsp/>fox"</tt></td>
</tr>
<tr>
  <td><tt>a.sub(/^./)<nbsp/>{<nbsp/>$&amp;.upcase<nbsp/>}</tt></td>
  <td>&#187;</td>
  <td><tt>"The<nbsp/>quick<nbsp/>brown<nbsp/>fox"</tt></td>
</tr>
<tr>
  <td><tt>a.gsub(/[aeiou]/)<nbsp/>{<nbsp/>$&amp;.upcase<nbsp/>}</tt></td>
  <td>&#187;</td>
  <td><tt>"thE<nbsp/>qUIck<nbsp/>brOwn<nbsp/>fOx"</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
So, this looks like the answer to converting our artists' names. The
pattern that matches the first character of a word is <tt>\b\w</tt>---look for a word boundary followed by a word character. Combine
this with <meth>gsub</meth> and we can hack the artists' names.
<p/>
<codefragment>
<fullcode><![CDATA[def mixedCase(aName)
  aName.gsub(/\b\w/) { $&.upcase }
end

mixedCase("fats waller")
mixedCase("louis armstrong")
mixedCase("strength in numbers")
]]></fullcode><rubycode>
<tr>
<td colspan="3"><tt>def<nbsp/>mixedCase(aName)</tt></td>
</tr>
<tr>
<td colspan="3"><tt><nbsp/><nbsp/>aName.gsub(/\b\w/)<nbsp/>{<nbsp/>$&amp;.upcase<nbsp/>}</tt></td>
</tr>
<tr>
<td colspan="3"><tt>end</tt></td>
</tr>
<tr>
<td colspan="3"><tt></tt></td>
</tr>
<tr>
  <td><tt>mixedCase("fats<nbsp/>waller")</tt></td>
  <td>&#187;</td>
  <td><tt>"Fats<nbsp/>Waller"</tt></td>
</tr>
<tr>
  <td><tt>mixedCase("louis<nbsp/>armstrong")</tt></td>
  <td>&#187;</td>
  <td><tt>"Louis<nbsp/>Armstrong"</tt></td>
</tr>
<tr>
  <td><tt>mixedCase("strength<nbsp/>in<nbsp/>numbers")</tt></td>
  <td>&#187;</td>
  <td><tt>"Strength<nbsp/>In<nbsp/>Numbers"</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<subsubsection>Backslash Sequences in the Substitution</subsubsection>
<p/>
Earlier we noted that the sequences <tt>\1</tt>, <tt>\2</tt>,
and so on are
available in the pattern, standing for the <em>n</em>th group matched so
far. The same sequences are available in the second argument of
<meth>sub</meth> and <tt>gsub</tt>.
<p/>
<codefragment>
<fullcode><![CDATA[  "fred:smith".sub(/(\w+):(\w+)/, '\2, \1')
  "nercpyitno".gsub(/(.)(.)/, '\2\1')
]]></fullcode><rubycode>
<tr>
  <td><tt>"fred:smith".sub(/(\w+):(\w+)/,<nbsp/>'\2,<nbsp/>\1')</tt></td>
  <td>&#187;</td>
  <td><tt>"smith,<nbsp/>fred"</tt></td>
</tr>
<tr>
  <td><tt>"nercpyitno".gsub(/(.)(.)/,<nbsp/>'\2\1')</tt></td>
  <td>&#187;</td>
  <td><tt>"encryption"</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
There are additional backslash sequences that work in substitution
strings: <tt>\&amp;</tt> (last match), <tt>\+</tt> (last matched group),
<tt>\`</tt> (string prior to match), <tt>\'</tt> (string after match), and
<tt>\\</tt> (a literal backslash).
It gets confusing if you want to include a literal backslash in a
substitution.  The obvious thing is to write
<p/>
<codefragment>
<alltt><fullcode><![CDATA[!- str = ''
str.gsub(/\\/, '\\\\')
]]></fullcode>
str.gsub(/\\/,<nbsp/>'\\\\')
</alltt>
</codefragment>
<p/>
Clearly, this code is trying to replace each backslash in <var>str</var>
with two. The programmer doubled up the backslashes in the replacement
text, knowing that they'd be converted to ``<tt>\\</tt>'' in syntax
analysis. However, when the substitution occurs, the regular
expression engine performs another pass through the string, converting
``<tt>\\</tt>'' to ``<tt>\</tt>'', so the net effect is to replace
each single backslash with another single backslash. You need to write
<tt>gsub(/\\/, '\\\\\\\\')</tt>!
<p/>
<codefragment>
<fullcode><![CDATA[!-    class String
!-      def inspect
!-        '"' + to_s + '"'
!-      end
!-    end
  str = 'a\b\c'
  str.gsub(/\\/, '\\\\\\\\')
]]></fullcode><rubycode>
<tr>
  <td><tt>str<nbsp/>=<nbsp/>'a\b\c'</tt></td>
  <td>&#187;</td>
  <td><tt>"a\b\c"</tt></td>
</tr>
<tr>
  <td><tt>str.gsub(/\\/,<nbsp/>'\\\\\\\\')</tt></td>
  <td>&#187;</td>
  <td><tt>"a\\b\\c"</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
However, using the fact that <tt>\&amp;</tt> is replaced by the matched
string, you could also write
<p/>
<codefragment>
<fullcode><![CDATA[!-    class String
!-      def inspect
!-        '"' + to_s + '"'
!-      end
!-    end
  str = 'a\b\c'
  str.gsub(/\\/, '\&\&')
]]></fullcode><rubycode>
<tr>
  <td><tt>str<nbsp/>=<nbsp/>'a\b\c'</tt></td>
  <td>&#187;</td>
  <td><tt>"a\b\c"</tt></td>
</tr>
<tr>
  <td><tt>str.gsub(/\\/,<nbsp/>'\&amp;\&amp;')</tt></td>
  <td>&#187;</td>
  <td><tt>"a\\b\\c"</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
If you use the block form of <meth>gsub</meth>, the string
for substitution is analyzed only once (during the syntax pass) and
the result is what you intended.
<p/>
<codefragment>
<fullcode><![CDATA[!-    class String
!-      def inspect
!-        '"' + to_s + '"'
!-      end
!-    end
 str = 'a\b\c'
 str.gsub(/\\/) { '\\\\' }
]]></fullcode><rubycode>
<tr>
  <td><tt>str<nbsp/>=<nbsp/>'a\b\c'</tt></td>
  <td>&#187;</td>
  <td><tt>"a\b\c"</tt></td>
</tr>
<tr>
  <td><tt>str.gsub(/\\/)<nbsp/>{<nbsp/>'\\\\'<nbsp/>}</tt></td>
  <td>&#187;</td>
  <td><tt>"a\\b\\c"</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
Finally, as an example of the wonderful expressiveness of combining
regular expressions with code blocks, consider the following code
fragment from the CGI library module, written by Wakou Aoyama. The code takes a string containing <smallfont>HTML</smallfont>
escape sequences and converts it into normal ASCII. Because it was
written for a Japanese audience, it uses the ``n'' modifier on the
regular expressions, which turns off wide-character processing. It
also illustrates Ruby's <kw>case</kw> expression, which we discuss
starting on page 81.
<p/>
<codefragment>
<alltt><fullcode><![CDATA[def unescapeHTML(string)
  str = string.dup
  str.gsub!(/&(.*?);/n) {
    match = $1.dup
    case match
    when /\Aamp\z/ni           then '&'
    when /\Aquot\z/ni          then '"'
    when /\Agt\z/ni            then '>'
    when /\Alt\z/ni            then '<'
    when /\A#(\d+)\z/n         then Integer($1).chr
    when /\A#x([0-9a-f]+)\z/ni then $1.hex.chr
    end
  }
  str
end

puts unescapeHTML("1&lt;2 &amp;&amp; 4&gt;3")
puts unescapeHTML("&quot;A&quot; = &#65; = &#x41;")
]]></fullcode>
def<nbsp/>unescapeHTML(string)
<nbsp/><nbsp/>str<nbsp/>=<nbsp/>string.dup
<nbsp/><nbsp/>str.gsub!(/&amp;(.*?);/n)<nbsp/>{
<nbsp/><nbsp/><nbsp/><nbsp/>match<nbsp/>=<nbsp/>$1.dup
<nbsp/><nbsp/><nbsp/><nbsp/>case<nbsp/>match
<nbsp/><nbsp/><nbsp/><nbsp/>when<nbsp/>/\Aamp\z/ni<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>then<nbsp/>'&amp;'
<nbsp/><nbsp/><nbsp/><nbsp/>when<nbsp/>/\Aquot\z/ni<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>then<nbsp/>'"'
<nbsp/><nbsp/><nbsp/><nbsp/>when<nbsp/>/\Agt\z/ni<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>then<nbsp/>'&gt;'
<nbsp/><nbsp/><nbsp/><nbsp/>when<nbsp/>/\Alt\z/ni<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>then<nbsp/>'&lt;'
<nbsp/><nbsp/><nbsp/><nbsp/>when<nbsp/>/\A#(\d+)\z/n<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>then<nbsp/>Integer($1).chr
<nbsp/><nbsp/><nbsp/><nbsp/>when<nbsp/>/\A#x([0-9a-f]+)\z/ni<nbsp/>then<nbsp/>$1.hex.chr
<nbsp/><nbsp/><nbsp/><nbsp/>end
<nbsp/><nbsp/>}
<nbsp/><nbsp/>str
end
<p/>
puts<nbsp/>unescapeHTML("1&amp;lt;2<nbsp/>&amp;amp;&amp;amp;<nbsp/>4&amp;gt;3")
puts<nbsp/>unescapeHTML("&amp;quot;A&amp;quot;<nbsp/>=<nbsp/>&amp;#65;<nbsp/>=<nbsp/>&amp;#x41;")
</alltt>
</codefragment>
<em>produces:</em>
<codefragment><alltt>
1&lt;2<nbsp/>&amp;&amp;<nbsp/>4&gt;3
"A"<nbsp/>=<nbsp/>A<nbsp/>=<nbsp/>A
</alltt>
</codefragment>
<subsection>Object-Oriented Regular Expressions</subsection>
<p/>
We have to admit that while all these weird variables are very
convenient to use, they aren't very object oriented, and they're
certainly cryptic. And didn't we say that everything in Ruby was an
object? What's gone wrong here?
<p/>
Nothing, really. It's just that when Matz designed Ruby, he produced a
fully object-oriented regular expression handling system. He then made
it look familiar to Perl programmers by wrapping all these
$-variables on top of it all. The objects and classes are still there,
underneath the surface.  So let's spend a while digging them out.
<p/>
We've already come across one class: regular expression literals
create instances of class <classname>Regexp</classname> (documented beginning on page
361).
<p/>
<codefragment>
<fullcode><![CDATA[  re = /cat/
  re.type
]]></fullcode><rubycode>
<tr>
<td colspan="3"><tt>re<nbsp/>=<nbsp/>/cat/</tt></td>
</tr>
<tr>
  <td><tt>re.type</tt></td>
  <td>&#187;</td>
  <td><tt>Regexp</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
The method <cim><file>regexp</file><front>Regexp</front><back>match</back><mref>match</mref></cim> matches a regular expression
against a string. If unsuccessful, the method returns <tt>nil</tt>. On success,
it returns an instance of class <classname>MatchData</classname>, documented beginning
on page 336. And that <classname>MatchData</classname> object gives you
access to all available information about the match. All that good stuff
that you can get from the $-variables is bundled in a handy little
object.
<p/>
<codefragment>
<fullcode><![CDATA[  re = /(\d+):(\d+)/     # match a time hh:mm
  md = re.match("Time: 12:34am")
  md.type
  md[0]         # == $&
  md[1]         # == $1
  md[2]         # == $2
  md.pre_match  # == $`
  md.post_match # == $'
]]></fullcode><rubycode>
<tr>
<td colspan="3"><tt>re<nbsp/>=<nbsp/>/(\d+):(\d+)/<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>#<nbsp/>match<nbsp/>a<nbsp/>time<nbsp/>hh:mm</tt></td>
</tr>
<tr>
<td colspan="3"><tt>md<nbsp/>=<nbsp/>re.match("Time:<nbsp/>12:34am")</tt></td>
</tr>
<tr>
  <td><tt>md.type</tt></td>
  <td>&#187;</td>
  <td><tt>MatchData</tt></td>
</tr>
<tr>
  <td><tt>md[0]<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>#<nbsp/>==<nbsp/>$&amp;</tt></td>
  <td>&#187;</td>
  <td><tt>"12:34"</tt></td>
</tr>
<tr>
  <td><tt>md[1]<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>#<nbsp/>==<nbsp/>$1</tt></td>
  <td>&#187;</td>
  <td><tt>"12"</tt></td>
</tr>
<tr>
  <td><tt>md[2]<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>#<nbsp/>==<nbsp/>$2</tt></td>
  <td>&#187;</td>
  <td><tt>"34"</tt></td>
</tr>
<tr>
  <td><tt>md.pre_match<nbsp/><nbsp/>#<nbsp/>==<nbsp/>$`</tt></td>
  <td>&#187;</td>
  <td><tt>"Time:<nbsp/>"</tt></td>
</tr>
<tr>
  <td><tt>md.post_match<nbsp/>#<nbsp/>==<nbsp/>$'</tt></td>
  <td>&#187;</td>
  <td><tt>"am"</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
Because the match data is stored in its own object, you can keep the
results of two or more pattern matches available at the same time,
something you can't do using the $-variables. In the next example,
we're matching the same <classname>Regexp</classname> object against two strings. Each
match returns a unique <classname>MatchData</classname> object, which we verify by
examining the two subpattern fields.
<p/>
<codefragment>
<fullcode><![CDATA[  re = /(\d+):(\d+)/     # match a time hh:mm
  md1 = re.match("Time: 12:34am")
  md2 = re.match("Time: 10:30pm")
  md1[1, 2]
  md2[1, 2]
]]></fullcode><rubycode>
<tr>
<td colspan="3"><tt>re<nbsp/>=<nbsp/>/(\d+):(\d+)/<nbsp/><nbsp/><nbsp/><nbsp/><nbsp/>#<nbsp/>match<nbsp/>a<nbsp/>time<nbsp/>hh:mm</tt></td>
</tr>
<tr>
<td colspan="3"><tt>md1<nbsp/>=<nbsp/>re.match("Time:<nbsp/>12:34am")</tt></td>
</tr>
<tr>
<td colspan="3"><tt>md2<nbsp/>=<nbsp/>re.match("Time:<nbsp/>10:30pm")</tt></td>
</tr>
<tr>
  <td><tt>md1[1,<nbsp/>2]</tt></td>
  <td>&#187;</td>
  <td><tt>["12",<nbsp/>"34"]</tt></td>
</tr>
<tr>
  <td><tt>md2[1,<nbsp/>2]</tt></td>
  <td>&#187;</td>
  <td><tt>["10",<nbsp/>"30"]</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
So how do the $-variables fit in? Well, after every pattern match,
Ruby stores a reference to the result (<tt>nil</tt> or a <classname>MatchData</classname>
object) in a thread-local variable (accessible using <var>$~</var>).
All the other regular expression variables are then derived from this
object. Although we can't really think of a use for the following
code, it demonstrates that all the other <classname>MatchData</classname>-related $-variables
are indeed slaved off the value in <var>$~</var>.
<p/>
<codefragment>
<fullcode><![CDATA[  re = /(\d+):(\d+)/
  md1 = re.match("Time: 12:34am")
  md2 = re.match("Time: 10:30pm")
  [ $1, $2 ]   # last successful match
  $~ = md1
  [ $1, $2 ]   # previous successful match
]]></fullcode><rubycode>
<tr>
<td colspan="3"><tt>re<nbsp/>=<nbsp/>/(\d+):(\d+)/</tt></td>
</tr>
<tr>
<td colspan="3"><tt>md1<nbsp/>=<nbsp/>re.match("Time:<nbsp/>12:34am")</tt></td>
</tr>
<tr>
<td colspan="3"><tt>md2<nbsp/>=<nbsp/>re.match("Time:<nbsp/>10:30pm")</tt></td>
</tr>
<tr>
  <td><tt>[<nbsp/>$1,<nbsp/>$2<nbsp/>]<nbsp/><nbsp/><nbsp/>#<nbsp/>last<nbsp/>successful<nbsp/>match</tt></td>
  <td>&#187;</td>
  <td><tt>["10",<nbsp/>"30"]</tt></td>
</tr>
<tr>
<td colspan="3"><tt>$~<nbsp/>=<nbsp/>md1</tt></td>
</tr>
<tr>
  <td><tt>[<nbsp/>$1,<nbsp/>$2<nbsp/>]<nbsp/><nbsp/><nbsp/>#<nbsp/>previous<nbsp/>successful<nbsp/>match</tt></td>
  <td>&#187;</td>
  <td><tt>["12",<nbsp/>"34"]</tt></td>
</tr>
</rubycode>
<p/>
</codefragment>
<p/>
Having said all this, we have to 'fess up. Andy and Dave normally use
the $-variables rather than worrying about <classname>MatchData</classname> objects. For
everyday use, they just end up being more convenient. Sometimes we just 
can't help being pragmatic.
</chapter>
</ppdoc>
