ruby编程-ruby平台-中
作者:gcbeen
日期:2013年11月08日
一、用正则表达式进行模式匹配
- =~操作符,一个操作数必须是正则表达式,另一个必须是字符串。
pattern = /Ruby?/i pattern =~ "backrub" # => 4 "rub ruby" =~ pattern # => 0 pattern =~ "r" # => nil
# 模式匹配的其他一些信息 "hello" =~ /e\w{2}/ $~.string # => "hello" $~.to_s # => "ell" $~.pre_match # => "h" $~.post_match # => "o"
$~是一个特殊的线程局部和方法局部变量,它在两个并发运行的线程中的值是不同的。并且,使用了=~操作符的方法不会修改调用者方法中的$~变量值。可以使用面向对象方式引用(Regexp.last_match)。
- MatchData对象
pattern = /(Ruby|Perl)(\s+)(rocks|sucks)!/ text = "Ruby\trocks!" pattern =~ text # => 0 data = Regexp.last_match # => 匹配详细 data.size # => 4 data[0] # => "Ruby\trocks" data[1] # => "Ruby" data[2] # => "\t" data[3] # => "rocks" data[1, 2] # => ["Ruby", "\t"] data[1..3] # => ["Ruby", "\t", "rocks"] data.values_at(1, 3) # => ["Ruby", "rocks"] data.captures # => ["Ruby", "\t", "rocks"] Regexp.last_match(3) # => "rocks" # 开始和结束的匹配 data.begin(0) # => 0 data.begin(2) # => 4 data.end(2) # => 5 data.offset(3) # => [5, 10]
# 在Ruby 1.9中的有名捕获。 pattern = /(?<lang>Ruby|Perl) (?<ver>\d(\.\d)+) (?<review>rocks|sucks)!/ if (pattern =~ "Ruby 1.9.1 rocks!") $~[:lang] # => "Ruby" $~[:ver] # => "1.9.1" $~["review"] # => "rocks" $~.offset(:ver) # => [5, 10] end pattern.names # => ["lang", "ver", "review"] pattern.named_captures # => { "lang" => [1], "ver" => [2], "review" => [3] }
有名匹配和局部变量
在Ruby 1.9中,如果一个正则表达式字面量包含有名捕获并且出现在=~操作符的左侧,那么捕获分组名将成为局部变量,匹配的文本放入这些变量中。如果匹配失败这些变量将被赋值为nil。
if /(?\w+) (? \d+\.(\d+)+) (? \w+)/ =~ "Ruby 1.9 rules!" lang # => "Ruby" ver # => "1.9" review # => "rules" end
这值针对正则表达式字面量有效。如果一个匹配模式被存储在变量或常量中,或是方法的返回值,或出现在操作符的右侧,那么=~操作符不会进行这种操作。
match方法:返回MatchData对象
if data = pattern.match(text) handle_match(data) end # 在Ruby1.9中,对match的调用关联一个代码块。 pattern.match(text) { |data| handle_match(data) }
用于匹配数据的全局变量
# 特殊的正则表达式变量 $~ # 等价于 Regexp.last_match $& # 等价于 Regexp.last_match[0] $` # 等价于 Regexp.last_match.pre_match $' #'等价于 Regexp.last_match.post_match $1 # 等价于 Regexp.last_match[1] $2, etc # 等价于 Regexp.last_match[2], etc $+ # 等价于 Regexp.last_match[-1]
$~和这些从它继承而来的变量都是线程局部且方法局部的,两个Ruby线程可以同时进行模式匹配,无需担心相互干扰。
- 用字符串进行模式匹配
"ruby123"[/\d+/] # "123" "ruby123"[/([a-z]+) (\d+)/, 1] # "ruby" "ruby123"[/([a-z]+) (\d+)/, 2] # "123"
slice方法是字符串索引操作符[]的同义词方法。slice!方法返回值与slice方法相同,并且将从字符串中删除返回的匹配字符串。
r = "ruby123" r.slice!(/\d+/) # => "123", r:"ruby"
s = "one, two, three" s.split # ["one,", "two,", "three,"] s.split(", ") # ["one", "two", "three"] s.split(/\s*,\s*/) # ["one", "two", "three"]
index方法可以搜索一个字符、子字符串或模式,并返回起始位置。如果给定一个Regexp对象作参数,则该方法的行为与=~操作符相似,不过它还可以用第二个参数指定搜索的起始位置,这样就可以在搜索完第一个之后,继续搜索下一个匹配值。
text = "hello world" pattern = /l/ first = text.index(pattern) n = Regexp.last_match.end(0) second = text.index(pattern, n) last = text.rindex(pattern)
搜索并替换(sub,gsub,sub!,gsub!) 最重要的使用正则表达式的String方法是sub(用于替换)和gsub(用于全局替换),以及它们的变体sub!和gsub!方法,如果发生替换,返回修改的字符,否则返回nil。
phone = gets phone.sub!(/#.*$/, "") phone.gsub!(/\D/, "") text.gsub!("rails", "Rails") text.gsub!(/\brails\b/, "Rails") # 使用匹配文本(\0),使用匹配的第一个字串(\1) text.gsub(/\bruby\b/i, '<b>\0</b>') # 插入将在字符串字面量中被执行,这时字符串没有被传递给gsub方法,插入这个动作发生在模式匹配发生之前。$&变量要么没定义,要么是前次匹配所遗留的值。 text.gsub(/\bruby\b/i, "<b>#{$&}</b>") re = /(?<quote>['"]) (?<body>[^"']*)\k<quote>/ puts "These are 'quotes'".gsub(re, '\k<body>')
替换字符串也可以引用不在捕获分组中的文本,可以用&、`、\'和+来替换值为$&、$`、$'和$+的文本。
动态计算替换字符串
text = "RUBY Java perl PyThOn" lang = /ruby|java|perl|python/i text.gsub!(lang) {|l| l.capitalize}
pattern = /(['"])([^\1]*)\1/ #' text.gsub!(pattern) do if ($1 == '"') "'#{$2}'" else "\"#{$2}\"" end end
正则表达式编码
在Ruby 1.9中,Regexp对象有一个与字符串类似的encoding方法。可以用修饰符显示指定编码:
u表示UTF-8编码 s表示SJIS编码 e表示EUC-JP编码 n表示无编码
还可以在正则表达式中用\u转义字符显示指定UTF-8编码。如果不显示指定编码,则使用源程序编码,如果正则表达式中所有字符都是ASCII编码,那么即使源程序编码是ASCII编码的超集,也使用ASCII编码。
如果试图匹配的模式与文本编码不兼容,在Ruby 1.9中会抛出一个异常。如果Regexp对象的编码不是ASCII编码,fixed_encoding?方法会返回true。如果fixed_encoding?返回false,那么可以安全地使用这个模式来匹配编码为ASCII或ASCII超集的文本。
二、数字和数学运算
- 数字相关的方法
# 通用方法 0.zero? # => true 1.0.zero? # => false 0.0.nonzero? # => nil(像false一样使用) 1.nonzero? # => 1(像true一样工作) 1.integer? # => true 1.0.integer? # => false 1.scalar? # => false:不是复数 1.0.scalar? # => false:不是复数 Complex(1, 2).scalar? # => true:复数
# 整数方法 0.even? # => true 0.odd? # => false
# 浮点数方法 ZERO, INF, NAN = 1.0/0.0, 0.0/0.0 # 有限的数字 ZERO.finite? # => true INF.finite? # => false NAN.finite? # => false # 无限的数字 ZERO.infinite? # => nil INF.infinite? # => 1 -INF.infinite? # => -1 NAN.infinite? # => nil # 非数字 ZERO.nan? # => false INF.nan? # => false NAN.nan? # => true
# 取整数方法 1.1.ceil # => 2 -1.1.ceil # => -1 1.9.floor # => 1 -1.9.floor # => -2 1.1.round # => 1 0.5round # => 1 -0.5.round # => -1 1.1.truncate # => 1 -1.1.to_i # => -1 truncate同义词
# 浮点数的方法 -2.0.abs # => 2.0 -2.0 <=> 0.0 # => -1 # 常量 Float::MAX # => 1.79769313486232e+308 Float::MIN # => 2.2250738585072e-308 Float::EPSILON # => 2.22044604925031e-16
三、Math模块
# 常量 Math::PI Math::E # 方根 Math.sqrt(25.0) # => 5.0 27.0 ** (1.0/3.0) # => 3.0 # 对数 Math.log10(100.0) # => 2.0 Math.log(Math::E ** 3) # => 3.0 Math.log2(8) # => 3.0 Math.log(16, 4) # => 2.0 Math.exp(2) # => 7.38905609893065 和Math::E ** 2一样
# 三角函数 include Math sin(PI/2) # => 1.0 cos(0) # => 1.0 tan(PI/4) # => 1.0 asin(1.0)/PI # => 0.5 sinh(0) # => 0.0 asinh(1.0) # => 0.0 theta = atan2(y, x) # r = hypot(x, y) # f, e = frexp(1024.0) # => [0.5, 11] x = ldexp(f, e) # => 1024: x = f * 2 ** e erf(0.0) # => 0.0 erfc(0.0) # => 1.0
四、数字运算
BigDecimal替代Float。BigDecimal对象的有效数字可以任意长,并且大小不受限制。在进行计算时,它们可以提供比舍入方式更好的精度控制。
require "bigdecimal" dime = BigDecimal("0.1") 4 * dime - 3 * dime == dime # => true 4 * 0.1 - 3 * 0.1 == 0.1 # => false BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_EVEN) BigDecimal.limit(20) principal = BigDecimal("200000") apr = BigDecimal("6.5") years = 30 payments = years * 12 interest = apr/100/12 x = (interest + 1) ** payments monthly = (principal * interest * x) / (x -1) monthly = monthly.round(2) monthly = monthly.to_s("f")
五、复数
require 'complex' c = Complex(0.5, -0.2) # => 0.5 - 0.2i z = Complex.new(0.0, 0.0) 10.times { z = z * z + c} magnitude = z.abs x = Math.sin(z) Math.sqrt(-1.0).to_s # => "1.0i" Math.sqrt(-1.0) == Complex::I # => true
六、实数
标准库中的Rational类表示实数(两个整数之商),mathn库重定义了整数除法,用于创建实数。mathn还做了许多工作用于统一Ruby的算术运算,并使Integer、Rational和Complex类协同工作。
require 'rational' penny = Rational(1, 100) # => "1/100" require 'mathn' nickel = 5 / 100 dime = 10 / 100 quarter = 1 / 4 change = 2 * quarter + 3 * pnny # Rational result:53/100 (1/2 * 1/3).to_s # "1/6"
七、向量和矩阵
matrix库定义了Matrix和Vector类,分别代表数字矩阵和向量,以及对它们进行算术运算的操作符。
require 'matrix' unit = Vector[1, 1] identity = Matrix.identity(2) # 2x2 matrix identity * unit == unit # true sx, sy = 2.0, 3.0 scale = Matrix[[sx, 0], [0, sy]] scale * unit # [2.0, 3.0] theta = Math::PI/2 rotate = Matrix[[Math.cos(theta), -Math.sin(theta)], [Math.sin(theta), Math.cos(theta)]] rotate * unit # [-1.0, 1.0] scale * (rotate * unit) # [-2.0, 3.0]
八、随机数
在Ruby中,用全局函数Kernel.rand产生随机数。如果不带参数,它返回一个大于等于0.0而小于1.0的伪随机浮点数。
rand # => 0.4682060936325654 rand # => 0.6254414462555852 rand(100) # => 81 rand(100) # => 32
可重复的伪随机数序列
srand(0) [rand(100), rand(100)] # => [44, 47] srand(0) [rand(100), rand(100)] # => [44, 47]
九、日期和时间
Time类用于表示日期和时间。它是操作系统提供的日期和时间的一个简单包装,因此,在某些系统上,这个类不能用于表示1790年之前或2038年之后的日期。在date库中的Date和DateTime类则没有这个限制。
Time.now # => 返回现在的时间 Time.new # => Time.now同义词 Time.local(2007, 7, 8) # July 8, 2007 Time.local(2007, 7, 8, 9, 10) # July 8, 2007, 09:10am, local time Time.utc(2007, 7, 8, 9, 10) # July 8, 2007, 09:10 UTC Time.gm(2007, 7, 8, 9, 10, 11) # July 8, 2007, 09:10:11 GMT (same as UTC)
t = Time.utc(2000, 12, 31, 23, 59, 59, 999999) t.day # => 31 t.wday # => 0(0代表星期天) t.yday # => 366 t.usec # => 999999 微妙 t.zone # => "UTC" values = t.to_a # => [59, 59, 23, 31, 12, 2000, 0, 366, false, "UTC"] values[5] += 1 Time.utc(*values) # => Mon Dec 31 23:59:59 UTC 2001 # 时区 t.zone # => "UTC" t.utc? # => true t.utc_offset # => 0 t.localtime # 转换为本地时间 t.zone # "PST" t.utc? # false t.utc_offset # -28800 t.gmtime # 转换为utc t.getlocal # 返回一个新的本地时间 t.getutc # 返回一个新的utc时间 t.isdst # => false utc没有夏时制 t.getlocal.isdst # => false 没有夏时制 # 星期函数 t.sunday? # => true t.monday? # => false t.tuesday? # => false
t.to_s # => "Sun Dec 31 23:59:59 UTC 2000" Ruby 1.8 t.to_s # => "2000-12-31 23:59:59 UTC" Ruby 1.9 t.ctime # => "Sun Dec 31 23:59:59 2000" # 格式化时间 t.strftime("%Y-%m-%d %H:%M:%S") # => "2000-12-31 23:59:59" t.strftime("%H:%M") # => "23:59" t.strftime("%I:%M %P") # => "11:59 PM" 12小时制 t.strftime("%A, %B %d") # => "Sunday, December 31" t.strftime("%a, %b %d %y") # => "Sun, Dec 31 00" t.strftime("%x") # => "12/31/00" t.strftime("%X") # => "23:59:59" t.strftime("%c") # "Sun Dec 31 23:59:59 2000"
require 'parsedate' include ParseDate datestring = "2001-01-01" values = parsedate(datestring) # => [2001, 1, 1, nil, nil, nil, nil, nil] t = Time.local(*values) # => Mon Jan 01 00:00:00 -0800 2001 s = t.ctime # => "Mon Jan 1 00:00:00 2001" Time.local(*parsedate(s)) == t # => true s = "2001-01-01 00:00:00-0500" v = parsedate(s) # => [2001, 1, 1, 0, 0, 0, "-500", nil] t = Time.local(*v) # now = Time.now past = now - 10 # 10秒之前 future = now + 10 # 10秒之后 future - now # => 10 past <=> future past < future now >= future now == now
时间工具方法
class Numeric def milliseconds; self/1000.0; end def seconds; self; end def minutes; self*60; end def hours; self*60*60; end def days; self*60*60*24; end def weeks; self*60*60*24*7; end def to_milliseconds; self*1000; end def to_seconds; self; end def to_minutes; self/60.0; end def to_hours; self/(60*60.0); end def to_days; self/(60*60*24.0); end def to_weeks; self/(60*60*24*7.0); end end expires = now + 10.days # => 10小时后 expires - now # => 864000.0 秒 (expires - now).to_hours # => 240.0 小时 t = Time.now.to_i # => 1384160886 Time.at(t) # => 2013-11-11 17:08:06 +0800 t = Time.now.to_f # => 1384160954.451236 Time.at(0) # => 1970-01-01 08:00:00 +0800
十、集合
Enumerable模块是一种混入模块,它在each迭代器的基础上实现了一组有用的方法。Array、Hash和Set类都包含了Enumerable模块。
一些可枚举的类有自然的枚举顺序,它们的each方法遵循这个顺序。数组按照索引的升序枚举各个元素,Range对象按照升序枚举元素,IO对象按照对应的文件和socket读入的文本顺序枚举每一行,在Ruby 1.9中,Hash和Set按照元素插入的顺序进行枚举,在Ruby 1.9前,这些类是随意的顺序进行枚举的。
许多Enumerable的方法返回一个处理后的可枚举集合或一个子集。通常情况下,如果一个Enumerable方法返回一个集合,这个集合是一个Array对象。Hash类覆盖了reject方法,返回一个Hash对象。
对集合进行迭代和转换
(5..7).each { |x| print x } (5..7).each_with_index{ |x, i| print x, i }
在Ruby 1.9中,Enumerable定义了cycle迭代器,它重复迭代集合中的每个元素,无限循环一直到给定的代码块用break、return或者抛出异常来明确中止这个迭代。在对Enumerable对象进行第一次迭代时,cycle把所有元素存储在一个数组中,以后的迭代将在数组中进行。
each_slice和each_cons迭代器
(1..10).each_slice(4) { |x| print x } # => prints "[1, 2, 3, 4][5, 6, 7, 8][9, 10]" (1..5).each_cons(3) {|x| print x} # => prints "[1, 2, 3][2, 3, 4][3, 4, 5]"
data = [1, 2, 3, 4] roots = data.collect {|x| Math.sqrt(x) } words = %w[hello world] upper = words.map {|x| x.upcase} # collect同义词
(1..3).zip([4, 5, 6]) { |x| print x.inspect } # => "[1, 4][2, 5][3, 6]" (1..3).zip([4, 5, 6], [7, 8]) { |x| print x } # => "14725836" (1..3).zip('a'..'c') { |x, y| print x, y} # => "1a2b3c"
# 将可枚举集合转换为一个数组 (1..3).to_a # => [1, 2, 3] (1..3).entries # => [1, 2, 3] to_a同义词 # require 'set' (1..3).to_set
枚举器和外部迭代器
在Ruby 1.9中枚举器是用Enumerable::Enumerator类来实现。在Ruby 1.8中可以通过包含enumerator库来得到。
# 通过to_enum来创建一个Enumerator对象 e = [1..10].to_enum # 通过enum_for来创建一个Enumerator对象 e = "test".enum_for(:each_byte) # 直接用不带代码块的方式调用iterator方法 e = "test".each_byte
枚举器作为外部迭代器被使用
"Ruby".each_char.max # => "y" iter = "Ruby".each_char # => loop do print iter.next; end print iter.next # prints "R" iter.rewind # print iter.next # prints "R"
"Ruby".each_char.with_index.each do |c, i| puts "#{i}: #{c}"; end
blog comments powered by Disqus