第1章导言 Introduction
Ruby是一门纯粹面向对象语言,拥有强大的元编程(metaprogramming)能力,可以用于创建领域特定语言(domain-specific Language DSL)。
1 irb(interactive Ruby)与Ruby交互,在irb提示符下输入任意Ruby表达式,会执行表达式将其值显示出来。
2 ri查看Ruby文档
例子: ri Array
ri Array.sort 加“.”将类或模块名与方法进行分隔
ri Hash#each 引用实例方法
ri Math::sqrt ::引用类方法
第2章 Rub程序的结构和运行 TheStructure and Execution of Ruby Programs
2.1词法结构
1注释以#开头并持续到该行结束
#一旦出现在字符串或者正则表达式中将作为字符串或正则表达式一部分,而不再作为注释
1)嵌入式文档(embedded document)多行注释
=begin =begin开头以=end结尾(=end所在行都包括在内)begin后的文本都被视为注释忽略
注释文本 =必须作为改行第一个字符,才会生效
=end
2字面量Literals 字面量直接出现在Ruby源码中的值。包括:数字、文本字符串、正则表达式,其他字面量如数组、哈希值是更为复杂的表达式而非单个标记
1 整形
1.0浮点型
'one' / "two" 字符串
/three/ 正则表达式
3标点符号 Punctuation
4标示符Identifiers
对变量、类及方法进行命名,由字母、数字和下划线组成,不能以数字开头。
标点符号出现在Ruby标示符开头胡结尾,含义:
$:以$符号开头,沿袭Perl语言做法,Ruby也定义一些包含其他标点符号的全局变量
@:以一个@开头表示实例变量,以两个@符号开头类变量
?:作为一个有用惯例,返回布尔值的方法通常是以一个问号结尾的名字
!:一些方法以感叹号结尾,这是提醒使用这些方法要担心。这个命名惯例通常是为了对两种方法进行区分:以感叹号方法结尾,通常会改变调用它们的对象,不以感叹号结尾的方法则不修改调用它们的对象,而是修改原始对象的一个拷贝并返回
=:如果方法以等号结尾,调用此方法时可以省略此等号。这种方法通常被置于赋值操作左侧
5关键字
特殊字符:
__LINE__ | case | ensure | not | then | __ENCODING__ | class | false | or |
true | __FILE__ | def | for | redo | undef | BEGIN | define? | if |
rescue | unless | END | do | in | retry | until | alias | else |
module | return | when | begin | end | nil | super | yield | break |
方法定义关键字
at_exit | catch | private | require | attr | include | proc | throw | attr_accessor | lambda | protected |
attr_reader | load | public | attr_write | loop | raise |
|
|
|
|
|
全局方法关键字
Array | chomp! | gsub! | Select |
Float | chop | Interator? | sleep |
Integer | chop! | load | split |
String | eval | open | sprintf |
URI | exec | p | srand |
abort | exit | | sub |
autoload | exit! | printf | sub! |
autoload! | fail | putc | syscall |
binding | fork | puts | system |
Block_given? | format | rand | test |
callcc | getc | readline | trap |
caller | gets | readines | warn |
chomp | gsub | scan |
|
对象方法关键字
allocate | freeze | Kind_of? | superclass |
clone | frozen? | method | taint |
display | hash | methods | trainted? |
dup | id | new | to_a |
enum_for | inherited | nil? | to_enum |
eql? | inspect | object_id | to_s |
equal? | instance_of? | respond_to? | untaint |
extend | is_a? | send |
|
6空白符
使用\将换行符进行转义,防止语句被判定终止
Ruby1.9中如果一行第一个非空白字符字符是一个.这行被当成上一行的延续,在1.8中不可用
eg. animals = Array.new
.push("dog") #在Ruby1.8中不生效
.push("cow")
【注意】避免空白符依赖的方法
·不要再方法名和其后的左括号之间留白
·如果一个方法的第一个参数以括号开头,那在此方法调用中,请一直使用括号,比如f((3+2)+1)
·一直使用Ruby解释器的-w选项,忽略上述规则时发出警告
2.2句法结构
❀复杂表达式
[1,2,3] | Array |
{1=>"one",2=>"two"} | Hash |
1..3 | Range |
1)块结构
块包括2种:
1与迭代器(iterator)相关联的代码,通过{}传递给迭代器的代码段
Eg 3.times{print "Ruby!"}
2用do和end分界符
Eg 1.upto(10) do |x|
print x
end
3文件结构
1)如果Ruby程序中含有"shebang"注释,改行注释必须是第一行,指示Unix类操作系统如何执行该文件。
2)如果包含"coding"且不含"shebang"则必须在第一行;否则在第二行
3)如果一个文件含一行,改行代码仅含有__END__标记,而且此标记后无空白符,Ruby将停止处理该文件的处理。
该文件余下部分,可以包含任何数据,而且程序可以通过IO流对象DATA对其进行读取
4)使用require从其他文件中载入代码,require根据搜索路径来查找特定代码块,并保证不重复加载给定模块
4程序编码
1)指定程序使用编码 编码注释 eg #coding: utf-8
文本编辑器可以通过编码注释来获取文件编码方式:
Emacs 用户: # -*- coding: utf-8 -*-
vi用户:#vi: set fileencoding=utf-8 :
2)源编码和默认外部编码
·源编码告诉Ruby解释器如何解读一个脚本中的字符,通常采用编码注释来设定一个文件的源编码。
·默认外部编码(default external encoding):它是当Ruby从文件或流中读取时采用的默认编码。默认外部编码对于Ruby进程是全局的。
通常默认外部编码基于计算机区域设置,可以显式指定,通过编译器-K选项设定源编码的方式。
·1.9-K为了兼容1.8,新增-E和-encoding
ruby -Eutf-8
ruby -Eutf-8 #E与utf-8空格可选
ruby --encoding utf-8 #encoding与utf-8之间可以用空格和等号
ruby --encoding=utf-8
·可以使用Encoding.default_external查询默认外部编码,返回一个Encoding对象。
可以使用Encoding.locale_charmap获得区域设置得到的字符编码
5 Ruby程序运行
在命令行输入含有脚本的文件名,Ruby解释器会读取文件执行其中脚本,它首先执行任何BEGIN块,然后再从文件第一行执行,直到发生:
·执行一个导致Ruby程序终结的语句
·到达文件结尾
·读入一行代码,此代码用标记__END__标示了文件的逻辑结尾
通常(除非调用exit!方法),Ruby解释器在退出前执行END语句,以及任何通过at_exit函数注册过的“关闭钩子(shutdown hook)”代码
第3章 数据类型和对象Datatypes and Objects
1 数字
1)整数字面量
可以在整数字面量里插入下划线,被称为千分符(thousands separator) eg:1_000_000_000
十六进制:0X/0x开头
二进制:0b/0B开头 二进制可以使用二进制分隔符 eg:0b1111_1111
八进制:0开头
2)浮点数字面量
0.0 -3.14 6.02e23 1_000_000.01
3)Ruby中的算术操作
Ruby中整数不会出现溢出,Float类型会溢出(大于Float::MAX)到Infinity的float值
指数操作:** x**4 多指数操作,由右到左
Fixnum和Bignum值可以使用索引进行访问,索引0将返回最低有效位 eg:even = (x[0]==0)
4)浮点数的二进制表示和圆整错误
float存在精度问题,Bignum以十进制处理数字不存在问题,但是效率较float低
2文本 String
1)字符串字面量(String Literals)
·被单引号所引用的字符串是形式简单的字符串字面量,单引号之间的文本就是字符串的值
单引号的转义:\'
反斜线的转义:\\
多行文本:
eg message = 'These three literals are'\
'concatenated into one bythe interpreter. '\
'The resulting stringcontains no newlines.' 内插语法
·被双引号引用的字符串字面量
△双引号引用的字符串,支持换行符转义\n,制表符\t,双引号转义\"
△支持内插语法(相当于C语言中的sprintf方法)
eg: "360 degrees=#{2*Math::PI} radians" => "360degrees=6.28318530717959 radians"
△对于#,当#后跟{、$、@等字符时,#需要进行转义
△%内插语法: eg: "pi is about %.4f\n" %Math::PI => "piis about 3.1416"
多参数:"%s is about %.4f\n" %["pi",Math::PI] => "pi is about 3.1416"
△双引号内如果不对换行符进行转义,将会认为是文本中的一部分
由双引号引用的字符串内的反斜线转义序列
\ x | 除非是一个行终结符,或者是abcefnrtuvxCM01234567这些特殊字符中的一个,否则位于x之前的\是没有特殊作用的,即\x等价于x本身。 |
\a | BEL字符(ASCII码7),产生一次控制台铃声,等价于\C-g或\007 |
\b | 退格符(ASCII码8),等价于\C-h或\010 |
\e | ESC字符(ASCII码27),等价于\033 |
\f | 换页符(ASCII码12),等价于\C-l和\014 |
\n | 换行符(ASCII码10),等价于\C-j和\012 |
\r | 回车符(ASCII码13),等价于\C-m和\015 |
\s | 空格符(ASCII码32) |
\t | 水平制表符(ASCII码9),等价于\C-i和\011 |
\unnnn | Unicode码点nnnn,每个n都是十六进制数。必须四位。 |
\u{十六进制数字} | Ruby1.9后开始支持,也是Unicode码点 |
\v | 垂直制表符(ASCII码11),等价于\C-k和\013 |
\nnn | 字节nnn,其中nnn是三个位于000和377之间的八进制数字 |
\nn | 等价于\0nn,其中nn是两个位于00和77之间的八进制数字 |
\n | 等价于\00n,其中其中n位于0和7之间的八进制数字 |
\xnn | 字节nn是位于两个位于00和FF之间的十六进制数字 |
\xn | 位于0和F之间的十六进制数字 |
\cx | \C-x的缩写形式表示 |
\C-x | 表示一种字符 |
\M-x | 表示一种字符,编码:将x的高位置1.常用表示"meta"字符,这样的字符从技术上讲并不属于ASCII字符集。x可以是任何单个字符或是除了\M\u、\x、\nnn之外的转义字符。\M可以和\C组合使用,如\M-\C-A |
\eol | 一个位于行终止符前面的反斜线将转义该终结符。反斜线和行终结符都不会出现在字符串中 |
·字符串字面量的分界符
%q字符串字面量遵循单引号引用字符串规则
%Q字符串字面量遵循双引号引用字符串规则
如果需要对分界符进行转义,那么既可以使用反斜线也可以选择另一个分界符
eg %q(Don'tworry about escaping ' characters!)
%Q|"How areyou?", he sail|
%-This string literalends with a newline\n- # Q omitted in this one
·here document 解决长文本中的大量转移字符
以<<或<<-开头,后紧跟指定结尾分界符的标识符和字符串(<<与标识符间没有空格),从下一行开始直至分界符单独出现在一行为止。
eg1 document =<<HERE
This is a String literal.
It has two lines and abruptly ends …
HERE
eg2 greeting = <<HERE + <<THERE + "World"
Hello
HERE
There
THERE
eg3 document = <<'THIS IS THE END, MY ONLY FRIED, THE END'
#围绕分界符的单引号暗示字符串字面量就像一个被单引号引用的字符串一样
·
·lots ad lots of text goes here
·with no escaping at all.
THIS IS THE END, MY ONLY FRIED, THE END
·反引号所引用的命令的执行
当使用反引号【`】来引用文本时,该文本被作为一个由双引号引用的字符串来处理,该文本的值将被传递给特殊的名为Kernel.`的方法,
该方法将该文本的值作为一个操作系统shell命令来执行,并且将该命令的输出作为字符串返回。
eg `ls` => %x[ls]
·字符串字面量和可变性
Ruby遇见一个字符串字面量时,会新建一个对象。如果循环体内包含字面量,Ruby在每次迭代时都会创建新对象
·通过String.new方法创建新字符串
2)字符字面量
?A ?" ?? 特殊字符:?\t ?\C-x ?\111(0111)
Ruby1.9后的版本,字符就是长度为1的字符串,?A=>'A'
3)字符串操作法
·字符串连接:+
·字符串连接:<< 将其后的操作数作为字符编码处理 eg: alphabet << 67 #相当于C(ASCII码67)
·操作数转换字符串 .to_s Ruby的+操作法不会将其右侧操作数自动转换成字符串,必须显式转换
·*操作符期望其右侧操作数是个整数,返回一个字符串,该字符串将依据右侧操作数指定的次数来重复左侧文本
ellipsis = '.'*3 #返回…
4)访问字符和子字符串
s = 'hello';
#Ruby1.8 返回ASCII码
s[0] #访问第一个字符,'h'
s[s.length-1] #访问最后一个字符,'o'
s[-1] #返回最后一个字符
s[-2] #返回倒数第二字符
s[-s.length] #访问第一个字符
s[s.length] #索引中没有
#Ruby1.9 直接返回字符
·字符替换: s[0] = ?H #替换第一个字符为H
s[-1] = ?O #替换最后一个字符为O
s[s.length] = ?! #无法替换索引中
s[-1] = "" #删除最后一个字符
s[-1] = "p!" #在最后一个字符后面追加字符串
·子字符串: s[0,2] # "he"
s[-1,1] # "0"
s[0,0] # "" [m,n] m:字符串的索引位置,n长度
s[0,10] #"hello"
s[s.length,1] # ""
s[s.length+1,1] # nil:报错
s[0,-1] # nil:没意义
s[0,1] = "H" #替换第一个字母
s[s.length,0] = " world" #在字符串
s[5,0] = "," #在5号位置追加一个逗号(,)
·Range索引
s[2..3] # "ll"
s[-3..-1] #"llo"
s[0..0] #"h"
s[0...5] #0到5不包括5
s[0..5] #0到5包括5
s[2..1] #Range是空的,颠倒
s[7..10] #越界返回nil
·判断字符串中是否含有某字符串 s["l"]:找到字符串中的首个l
s[/[aeiou]/] = '*' 将字符串中的[aeiou]替换成*
·对字符串进行迭代
Ruby1.8定义一个each方法用于按行迭代一个字符串。String类包含一些来自Enumerable模块的方法,可以用于处理一个字符串里的行。
Rbuy1.8里,使用each_byte迭代器按字节方式来迭代一个字符串。
Ruby1.9里,String类取消了each方法,而且不再包含Enumerable模块。Ruby1.9定义了三个命名清晰的字符串迭代器取代each方法:
1)each_byte按字节对一个字符串进行顺序迭代
2)each_char按字符进行迭代
3)each_line按行迭代
·字符串编码和多字节字符
Ruby1.8:字符串是一个字节序列。当字符串用于表示文本(而非二进制数据),该字符串里的每个字节都被假定为代表了一个ASCII字符。
Ruby1.8里,字符串的单个元素不是字符,而是数字,即实际的字节值或字符编码值。
Ruby1.9:字符串是字符序列,那些字符不必局限于ASCII字符集。单个元素是字符表示成长度为1的字符串,而不是字符编码的整数值。
每个字符串都有一个编码方式,它指定了字符串的字节和那些字节代表的字符之间的对应关系。一些编码方式,如Unicode字符进行
编码的UTF-8编码方式,采用可变数目的字节来表示每个字符,因此字节和字符之间不再保持1对1(甚至不是2对1)的对应关系。
1)Ruby1.9里的多字节字符:length和size方法都返回一个字符串的字符数,而新的bytesize方法则返回字节数。
s="2×2=4"
s.length #=>5:characters:'2' '×' '2' '=' '4'
s.bytesize #=>6:Bytes(hex):32 c3 97 32 3d 34
※通过.encoding查看编码,如果编码可以用一位二进制表示就用一位默认ASCII,需要用两位的用UTF-8。ASCII字符串与UTF8字符串连接,
将得到UTF8字符串。将UTF8与SJIS字符串连接将报不兼容错误。可以使用Encoding.compatible来测试两个字符串(或一个字符串与正则)
的编码是否兼容。如果兼容返回两种编码的超集,否则返回nil、
※force_encoding并没有生成其调用者的拷贝,而是在修改了该字符串的编码之后再将其返回。
s="\xa4".forcde_encoding("utf-8") #不可用的utf8的字符串
s.valid_encoding? #=>false
※encode方法与force_encoding方法截然不同。返回一个字符串,代表了与其调用者一样的字符序列,但编码方式却不同。
为了像这样改变字符串的编码,encode方法必须改变构成该字符串的底层细节。如果force_encoding(nil)表示将字符串变成binary
encode方法传递两个参数,第一个参数指定你所期望的编码,第二个参数则指定字符串的当前编码。
在将该字符串的编码转换成由第一个参数所指定的编码之前,Ruby会首先使用第二个参数所指定的编码来解释该字符串。
# -*- coding: utf-8 -*-
euro1 = "\u20AC"
puts euro1
euro1.encoding #=><Encoding:UTF-8>
euro1.bytesize #=>3
euro2 =euro1.encode("iso-8859-15")
puts euro2.inspect
euro2.encoding #=><Encoding:iso-8859-15>
euro2.bytesize #=>1
euro3 =euro2.encode("utf-8")
euro1 == euro3 #=>true
byte = "\xA4"
char =byte.encode("utf-8","iso-8859-15")
一下代码具有相同的效果:
text = bytes.encode(to,from)
text =bytes.dup.force_encoding(from).encode(to)
2)Encoding类:代表一种字符编码。Encoding对象充当编码方式的标识符。
·name方法返回一个编码方式名字
·to_s方法是name的同义词
·inspect方法用比name方法更详尽的方式将一个Encoding对象转换成一个字符串。
·预定义常量:
Encoding::ASCII_8BIT
Encoding::UTF_8
Encoding::EUC_JP
Encoding::SHIFT_JIS
·find方法获得对应的Encoding对象,支持大小写
encoding= Encoding.find("utf-8")
·list获得编码方式列表Encoding.list方法只列出内建的编码及任何已经动态载入的编码。
·default_external方法可以得到一个Encoding对象,获得默认的外部编码。
·locale_charmap方法获得当前区域设置的编码方式。
3)Ruby1.8里的多字节字符
Ruby1.8将所有的字符串解释成由8位字节构成的序列。在标准库jcode模块里(UTF-8,EUC,SJIS)。
要使用这个库require jcode模块,然后将全局$KCODE变量设置成你的多字节字符所使用的编码方式(在启动Ruby解释器时使用-K命令行选项)。
jcode库为String对象定义一个新的jlength方法,将返回按字符计数的字符串长度,而不是按字节计。
所有整数Integer的实例。如果一个整数能容纳到31个二进制内,它是Fixnum类的实例,否则是Bignum类的实例。自动使用最合适的空间进行存储。
Float近似表示实数。
Complex类代表复数。
BigDecimal表示具有任意精度的实数,使用十进制表示法而不是二进制。
Rational表示有理数,表示两个整数相除后得到的数。