Ruby学习一 String
http://my4java.itpub.net/post/9983/57952
第二章 简单数据
本章包括:
1. Strings
2. Regular Expressions
3. Numbers
4. Times and Dates
5. Summary
理论指导实践,就像磁石吸引铁。
Karl Friedrich Gauss
对程序语言混合的度量是:它支持什么种类的数据?早期计算机是用纯粹数据数据的严格程序。不久,字符数据和字符的字符串被引入,这对于多用途语言的发展至关重。
随着时间推移,我们发现自己要处理越来越多的数据。现代语言通常包括对很多种类数据的支持。注意我们在这儿并没有说类型,是因为通常认为类型与数据有点不同。例如,正则表达式本质上通常存储字符形式的字符串,但我们并不认为它们是真正的字符串,因为它们还有别用途。
我们可以很容易地添加像数组和哈希表这样东西给列表,因为对Ruby来说它们是低级的实体。事实上,它们些附带用途。但数组和哈希表(更复杂的数据结构)应该单独有一章。
然后本章讨论Ruby中最常用的四种数据类型。它们是字符串,正则表达式,数字和时间与日期。
像其它语言中一样,字符串一些字符序列。与Ruby内的其它实体一样,字符串是第一类对象。
正则表达式是在文本内描述模型的浓缩符号。它使用的时间有10年了。
Numbers 需要一点解释,它们由整数和浮点数组成。在Ruby中,整数可以是类Fixnum或Bignum,这依赖于它们量级。
在许多语言中,时间和日期都有问题。Ruby以面向对象的接口来处理传统的时间与日期问题,以期望减少这些混乱。
提醒一下读者我们的讨论中没有包括Rang类。这是因为范围不是很有用的,也因为它们不复杂,但是本书代码会经常用到它。
现在让我们看看一些例子代码,我们从符串开始。
一、字符串
一旦原子成为基本的自然的构建块;然后质子成为基本材料,然后是夸子。现在我们说字符串是基本材料。
普林斯顿大学物理学教授David Gross,在1980年,计算机科学大会用简单问题开始了它的数据结构类。他没介绍自己和课程的名字;他没有课程提纲或教课书。他走进教室说,"最重要的数据类型什么?"
有一个或二个猜想。一些人认为,"指针",他说不,不是它。然后说出它观点:最重要的数据类型是字符数据。
他的观点正确。计算机被认为我们仆人,不是我们的主人,字符数据有人类可阅读上差别。(有些人可以很容易地读二进制数据,但我们忽略它们。)字符(然后是字符串)存在于人和计算机之间。包括我们可以想像到的每种信息,自然语言文字,都可以被编码为字符的字符串。
我们找到我自己能用字符串做什么呢?我们想连接它们,记号化它们,分析它们,完成搜索和替换,等等。Ruby可轻易地完成大多数任务。
1、完成指定字符串间的比较
Ruby内置了比较字符串思想;比较按我们希望的字典次序完成(也就是说,基于字符集次序)。但如果我们需要,我们可以引入我们自己的字符串比较规则,这些可以任意地复杂。
做为一个例子,假设我们想忽略英语句子中字符串以a,an,和the开头的字符。我们也想忽略大多数常用的标点符号。我们可以通过覆写内置方法<=>来做到,该方法通常由<,<=,>和>=调用(参见Listing2.1)。
Listing 2.1 Specialized String Comparisons
class String
alias old_compare <=>
def <=>(other)
a = self.dup
b = other.dup
# Remove punctuation
a.gsub!(/[,.?!:;]/, "")
b.gsub!(/[,.?!:;]/, "")
# Remove initial articles
a.gsub!(/^(a |an |the )/i, "")
b.gsub!(/^(a |an |the )/i, "")
# Remove leading/trailing whitespace
a.strip!
b.strip!
# Use the old <=>
a.old_compare(b)
end
end
title1 = "Calling All Cars"
title2 = "The Call of the Wild"
# Ordinarily this would print "yes"
if title1 < title2
puts "yes"
else
puts "no" # But now it prints "no"
end
注意我们用个别名来保存旧版本的<=>并在最后调用它。这是因为如果我们想使用<方法,而它调用新的<=>而不旧的哪个,结果是无穷递归,程序当掉。
也要注意==操作符不调用<=>方法(从Comparable中混插)。这意味着如果我们需要以一些特定方式检查等同性,我们将必须个别地覆写==方法。但这个例子中,==像我们希望的一样,它能工作。
2、Tokenizing a String
split方法将解析字符串并返回记号的数组。它接受两个参数,一个界定符,和一个字段限制,它是一个整数。
界定符缺省是空格。实际上,它使用$;或者等价的$FIELD_SEPARATOR。如果界定符是字符串,那么字符地串使用它做为记号分割器。
s1 = "It was a dark and stormy night."
words = s1.split # ["It", "was", "a", "dark", "and",
# "stormy", "night"]
s2 = "apples, pears, and peaches"
list = s2.split(", ") # ["apples", "pears", "and peaches"]
s3 = "lions and tigers and bears"
zoo = s3.split(/ and /) # ["lions", "tigers", "bears"]
limit参数对返回的字段数量有个上限,根据这些规则:
1. 如果被省略或是0:删除数组尾部的空字符串。
2. 如果是正整数,limit > 0: 为数组分配至多limit个元素。 (填充余下的字符串到最后字段)。
3. limit < 0: 相当于指定limit的值为正无穷。对字段的数量没有限制。
这儿是对三个规则的演示:
str = "alpha,beta,gamma,,"
list1 = str.split(",") # ["alpha","beta","gamma"]
list2 = str.split(",",2) # ["alpha", "beta,gamma,,"]
list3 = str.split(",",4) # ["alpha", "beta", "gamma", ","]
list4 = str.split(",",8) # ["alpha", "beta", "gamma", "", ""]
list5 = str.split(",",-1) # ["alpha", "beta", "gamma", "", ""]
3、格式化字符串
Ruby内的字符串格式化像C语言一样,由sprintf方法完成。它接受一个字符串和一个表达式列表做为参数并返回一个字符串。本质上格式化符串包含一套指示符,它们与C中的是一样的。
name = "Bob"
age = 28
str = sprintf("Hi, %s... I see you're %d years old.", name, age)
你可以会问我们为什么使用它而不用简单的#{ expr }来插入值。回答是sprintf方法可以做些额外的格式化工作,如指定最大宽度,指定十进制小数点的位置,添加或抑制前导零,左侧对齐,右对齐等等。
str = sprintf("%-20s %3d", name, age)
String类有个方法%,它将做同样的事情。它接受一个值或任意类型的值的数组。
str = "%-20s %3d" % [name, age] # Same as previous example
我们也方法ljust, rjust, 和 center;它们接受目的字符串和用来填充的空白。
str = "Moby-Dick"
s1 = str.ljust(13) # "Moby-Dick "
s2 = str.center(13) # " Moby-Dick "
s3 = str.rjust(13) # " Moby-Dick"
4、控制大写和小写
Ruby的String类提供了丰富的方法来控制大小写。我们这儿是的楖要。
downcase方法将转换字符串为小写。同样upcase方法将字符串转换为大写。
s1 = "Boston Tea Party"
s2 = s1.downcase # "boston tea party"
s3 = s2.upcase # "BOSTON TEA PARTY"
capitalize方法将字符串第一个字符变为大写,并强制其它字符为小写。
s4 = s1.capitalize # "Boston tea party"
s5 = s2.capitalize # "Boston tea party"
s6 = s3.capitalize # "Boston tea party"
swapcase方法将交换字符串内每个字符的大小写方式。
s7 = "THIS IS AN ex-parrot."
s8 = s7.swapcase # "this is an EX-PARROT."
每个方法都有用于替换的等价方法 (upcase!, downcase!, capitalize!, swapcase!).
没有用于侦测大小写情况的内置方法,但是很容易用正则表达式来做一个。
if string =~ /[a-z]/
puts "string contains lowercase charcters"
end
if string =~ /[A-Z]/
puts "string contains uppercase charcters"
end
if string =~ /[A-Z]/ and string =~ /a-z/
puts "string contains mixed case"
end
if string[0..0] =~ /[A-Z]/
puts "string starts with a capital letter"
end
注意,这些方法都忽略了locale。
5、子串的访问和赋值
在Ruby中,可以有几种不的方式访问子串。通常使用方括号,就像数组一样;但方括号可以包含一对Fixnums数值,一个Range对象,正则表达式,或一个字符串。依次讨论每种情况。
如果指定一对Fixnum数值,它们被视为偏移值和长度,相应的子串被返回:
str = "Humpty Dumpty"
sub1 = str[7,4] # "Dump"
sub2 = str[7,99] # "Dumpty" (overrunning is OK)
sub3 = str[10,-4] # nil (length is negative)
重要地是记住偏移值和长度(字符数量),而不是开始和结束的偏移值。
负索引从字符串尾部向后计数。这种情况,索引以1为基础,而不是以0为基础。长度仍然按向前方向添加。
str1 = "Alice"
sub1 = str1[-3,3] # "ice"
str2 = "Through the Looking-Glass"
sub3 = str2[-13,4] # "Look"
可以指定Range对象。在这种情况下,Range接受字符串的范围索引。Range可以有负数,但最小的数值必须是范围的第一个。如果Range太小或初始值超出字符串则返回nil。
str = "Winston Churchill"
sub1 = str[8..13] # "Church"
sub2 = str[-4..-1] # "hill"
sub3 = str[-1..-4] # nil
sub4 = str[25..30] # nil
如果指定了正则表达式,字符匹配的模式被返回。如果没有匹配,nil被返回。
str = "Alistair Cooke"
sub1 = str[/l..t/] # "list"
sub2 = str[/s.*r/] # "stair"
sub3 = str[/foo/] # nil
如果指定字符串,如果它会做为子串出现,则它会被返回(不是则返回nil)。
str = "theater"
sub1 = str["heat"] # "heat"
sub2 = str["eat"] # "eat"
sub3 = str["ate"] # "ate"
sub4 = str["beat"] # nil
sub5 = str["cheat"] # nil
Finally, in the trivial case, a single Fixnum as index will yield an ASCII code (或者是nil如果超出范围)。
str = "Aaron Burr"
ch1 = str[0] # 65
ch1 = str[1] # 97
ch3 = str[99] # nil
重要的是认识我们在这儿描述的符号,当用于对它们赋值时,它们也充当赋值符号。
str1 = "Humpty Dumpty"
str1[7,4] = "Moriar" # "Humpty Moriarty"
str2 = "Alice"
str2[-3,3] = "exandra" # "Alexandra"
str3 = "Through the Looking-Glass"
str3[-13,13] = "Mirror" # "Through the Mirror"
str4 = "Winston Churchill"
str4[8..13] = "H" # "Winston Hill"
str5 = "Alistair Cooke"
str5[/e$/] ="ie Monster" # "Alistair Cookie Monster"
str6 = "theater"
str6["er"] = "re" # "theatre"
str7 = "Aaron Burr"
str7[0] = 66 # "Baron Burr"
对一个计算表达式赋值nil将不会有任何影响。
6、字符串内的置换
你已经看到如何在字符串内完成简单置换。sub和gsub方法提供了更高级的以模式为基础的能力。还有sub!和gsub!,它们替换它们的副本。
sub方法将置换第一个给出替换字符串或指定块的匹配出现的地方。
s1 = "spam, spam, and eggs"
s2 = s1.sub(/spam/,"bacon") # "bacon, spam,
and eggs"
s3 = s2.sub(/(w+), (w+),/,'2, 1,') # "spam, bacon, and eggs"
s4 = "Don't forget the spam."
s5 = s4.sub(/spam/) { |m| m.reverse } # "Don't forget the maps."
s4.sub!(/spam/) { |m| m.reverse }
# s4 is now "Don't forget the maps."
就像例子中显示的,特殊符号1, 2, 等等可以被用做替换字符串。但是,特殊变量如$& (或$MATCH)不可以。
如果使用块形式,则特殊变量可以被使用。但是,如果你需要匹配的字符串,它将做为一个参数被传递给块。如果不需要,参数可以被删掉。
gsub方法(全局置换)本质上是一样的,除了所有的匹配都被置换而不只是第一个。
s5 = "alfalfa abracadabra"
s6 = s5.gsub(/a[bl]/,"xx") # "xxfxxfa xxracadxxra"
s5.gsub!(/[lfdbr]/) { |m| m.upcase + "-" }
# s5 is now "aL-F-aL-F-a aB-R-acaD-aB-R-a"
方法Regexp.last_match本质上与$&或者$MATCH是一样的。