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是一样的。

 

相关推荐