> I prefer String#unpack("C*").  I'm not sure what encoding issues there
> are with that, however.

If it's a UTF8 string then use unpack("U*")

As for #first and #last:

  a = "abcdefg"
  a[0...2]        # first two characters
  a[0,2]          # first two characters
  a[-2..-1]       # last two characters
  a[-2,9999]      # last two characters (cheating)

I think the last case shows where a sanctioned "infinity" would be nice. But
otherwise:

  class String
    def first(n=1)
      self[0,n]
    end
    def last(n=1)
      return nil if n < 0
      return "" if n == 0
      return self if n > self.size
      self[-n..-1]
    end
  end

I still find that string operations sit uncomfortably together.

  string[a..b]   # Start pos is a, end pos is b
                 # If either a or b is negative, it's an offset from the end
                 # (which means it's not a Range in a useful sense)
                 # Return nil if a is not within string, or b is to the
                 # 'left' of a, after resolving negative offsets

  string[a,b]    # Start pos is a, length is b
                 # If a is negative, it's an offset from the end
                 # If b is negative, nil is returned

  string[a]      # return the a'th byte of string as an integer (ick)

I have to remind myself of these with irb each time I use them. "How do I
get from character a to the end of the string?" => str[a..-1]

"How do I get just the a'th character by itself (as a string)?" => str[a,1]

Regards,

Brian.