本文介绍 Ruby 基本的数据类型,主要参考《Ruby编程语言》。
Ruby支持的数据类型包括基本的Number、String、Ranges、Symbols,以及true、false和nil这几个特殊值,同时还有两种重要的数据结构——Array和Hash。
数字
Ruby 中所有的数字都是 Numberic 类的实例,所有整数都是 Integer 的实例。Fixnum 和 Bignum之间的转换是透明的。Ruby 利用 Float 来近似的表示实数,该类会利用本地平台的浮点数表示形式。各个类之间的关系如下,图片来源于《Ruby编程语言》。
图片补充以下几点,以免忘记。
- 除法、取模与负数。
当一个操作数为负数时,Ruby的整数除法和取模操作不同于C/C++和Java。例如,-7/3,可以用浮点表示为-2.33。Ruby采取的是向负无穷大圆整,于是整除结果为-3,但是C的做法是向0圆整,所得结果为-2。可以得知,在Ruby中,-a/b和a/-b相等,但是却不一定等于-(a/b)。
而在Ruby取模操作中,-7/3的结果为2,而C中结果却是为-1。在Ruby中,结果符号始终和第二个数操作符号保持一致。在C中结果符号始终和第一个操作数保持一致。另外Ruby还定义了remainder方法,在结果的量和符号方面都和C保持一致。
- 实数的表示
和大多数硬件和计算机语言一样,Ruby的Float类为了高效的使用硬件,大多数浮点数都表示成二进制的,可以精确的表示1/2 和1/4这类分数,但是连0.1都无法用精确的表达。所以下面一个简单的表达式或许并不是表现的想你想象的那样。
0.4 - 0.3 == 0.1
Ruby在标准库中提供了BigDecimal类来解决这个问题,它采用十进制来表示实数。但是,针对BigDecimal对象的算术运算要比针对Float的算术运算慢上许多倍。
文本
字符串表示
- 单引号
单引号引入字符串字面量来表示字符串,其中如果需要表示单引号,只需要在之前加入一个反斜线。
'it\'s just a test!'
如果需要表示反斜线,那么只需要在反斜线之前再加入一个反斜线就可以了。
'This is a backslash: \\'
在一个单引号表示的字符串中,如果一个反斜线后边既不是单引号又不是双引号,那么该反斜线就表示反斜线。意味着,不需要成对的出现反斜线。
'a\b' == 'a\\b'
单引号字符串可以跨行,得到的字符串会包含换行符,也不能通过 反斜线来转义行尾的换行符。
'This is a long string literal \
that include a backslash and a new line.'
如果不希望加入换行符,那么只需将其划分成为相邻的字符串字面量,同时在其结尾加入反斜线转义末尾的换行符就可以了。
'The three literal are' \
'concatenated into one by interpreter' \
'The resulting string contains no newlines.'
- 双引号
双引号引用的字符串字面量则支持更多的转义,比如换行\n,制表\t和双引号"。
同时还支持Ruby特有的“字符串内插”。
"The resulte is #{8 + 9}"
表达式位于花括号中,并且前边有一个#字符。当插入的表达式只是一个全局变量、实例或类变量的引用时,花括号可以省略。
当不希望#字符被特殊处理时,只需在之前加入一个反斜线即可。
Ruby 也支持printf和sprintf这两个方法。
printf("Pi is about %.4f", Math::PI)
但是,Ruby中还有一个与sprintf等价的操作符,只需要简单的在一个格式字符串和带插入的其中的参数之间放置一个%即可。
"Pi is about %.4f" % Math::PI
- Unicode 转义序列
在Ruby1.9以后,在双引号引用的字符串中通过\u可以转义任意的Unicode字符,最简单的形式是在\u后边添加一个十六进制数字(不区分大小写),它们代表了0000到FFFF之间的Unicode码点。
"\u00D7" # => "x"
第二种形式是在它后边加一对花括号,花括号之间的数字可以表示0到10FFFF之间的任何Unicode码点。
"\u{A5}" # => same as "\u00A5"
最后一种形式允许多个码点同时转义,只需要在花括号内放入多组1到6个十六进制数字组成的序列即可,各组之间用空格或tab符号分割,在开始花括号之后和结束花括号之前均不允许有空格。
"\u{20AC A3 A5}"
- 字符串字面量分界符
Ruby除了支持单引号和双引号以外,还支持一种更一般化的语法来引用字符串字面量。以%q开头的遵循单引号引用字符串规则,以%Q(或%)开头的遵循双引号引用字符串规则。其后紧接的第一个字符为分界符,知道下一个分界符(未被转义)的内容组成了改字符串。
%q(Don't worry about ' character!)
%Q|"How are you?", he said|
%-This string ends with a newline\n-
- Here document
Here document 以 << 或 <<- 开头,后边紧跟(为了避免与左移操作符混淆,不允许有空格)一个用于指定结尾分界符的标示符或字符串,从下一行开始一直待该分界符单独出现到一行为止。
document = <<HERE
This is a string literal.
It has two lines and abruptly ends. . .
HERE
Ruby 解释器解释到 << 和分界符之后并不会停止,事实上,在读取一个 here document 的内容之后, Ruby 解释器会回到该 here document 的开头部分所在行,并继续解析后边的内容。
greeting = <<HERE + <<HTERE + 'world'
Hello
HERE
there
HTERE
结尾分界符必须单独出现在一行上,该分界符后边甚至连注释不能接注释。如果以 << 开头,那么结尾分界符必须出现在一行的开头。如果以 <<- 开头,那么在结尾分界符之前可以出现空白。除了空的 here document,每个 here document 都以一个换行符结尾。
如果采用一个没有被引号括起来的标示符作为分界符,那么 here document 就会表现的像被双引号应用的字符串一样,其中反斜线用于转义,而 # 用于内插字符串。
如采用一个双引号引用的字符串字面量来作为 here document 的分界符。除了分界符里面可以出现空格,这与使用单个分界符的情形是一样的。
如果采用单引号引用的字符串字面量作为分界符,它会表现的 比单引号引用的字符串还要严格一些。因为单引号本身不是分界符,所以反斜线不在作为转义字符,因此,反斜线也是字符串字面量的一部分。
document = <<'THIS IS THE END, MY ONLY FRIEND, THE END'
lots an lots of test goes here
THIS IS THE END, MY ONLY FRIEND, THE END
- 反引号所引用命令执行
当使用反引号来引用文本时,该文本作为一个由双引号引用的字符串字面量来处理。改文本的值将被传递给一个名为 Kernel.` 的方法,该方法将该文本的值作为一个操作系统的 shell 命令来执行。
`ls`
另外,Ruby 也支持一种泛型化的引用语法,可以用来替代反引号,类似于 %Q 的语法,这里使用 %x 来替代反引号。
%x[ls]
- 字符串字面量和可变性
Ruby 的字符串是可变的。因此,Ruby 无法用同一个对象来表达两个相同的字符串字面量。每当 Ruby 遇见一个字符串字面量的时候,它都会创建一个对象。
10.times { puts "test".object_id }
因此,为了获得更好的运行效率,应该避免在循环中使用字符串字面量。
字符字面量
在 Ruby 中, 你可以用一个字符前面加问号的方式来表示单个字符构成的字面量。
?A
因为 Ruby1.9 对字符串字面量的解释方式不同于 Ruby1.8 , 字符就是长度为一的字符串,所以上边例子完全等价于 'A',也就是说,我们完全没有必要再使用这种语法了。
字符串的操作
Ruby 通过 + 操作符连接两个字符串,不同 JavaScript,其并不会将右侧操作数自动转换成为字符串。
一般情况下,采用字符串内插比采用+操作符简单一些,并会自动调用其 to_s。
<< 操作符会将第二个参数添加到第一个参数后面,不同于+操作符,他会修改左侧的操作数,而不是返回一个对象。<<操作符同样不会对右侧的操作数做任何类型的转换,但是,如果右侧的操作数是一个整数,那么它将会被当做一个字符编码来处理。
* 操作符期待右侧操作数是一个整数。如果左侧的操作数是一个字符串字面量,那么任何内插操作都只会在重复操作被执行之前被执行一次。如下:
a = 0
"#{a=a+1} " * 3 # Returns "1 1 1 ", not "1 2 3 "
Ruby 字符串同时定义了 == 、 != 、< 、<= 、> 和 >= 来比较字符串。当且仅当两个字符串拥有相同的长度,而且所有的字符都相等的时候,它们才是相等的。字符串之间的比较会严格的基于字符编码,不会对将要比较的字符串进行任何格式化。字符串是大小写敏感的。在 ASCII 里,大写字符的编码值均小于小写字符,比如 "Z" < "a"。对于 ASCII 字符进行大小不敏感的比较可以使用 casecmp,也可以是在比较前先通过 downcase 或 upcase 方法将字符串转换成为相同的形式。记住,Ruby 关于大写或小写字符的理解仅限于 ASCII 字符集。
访问字符和子字符串
在 Ruby1.9 中,亦可以这样索引单个字符。例如:
s = "hello";
s[0] # 'h'
s[s.length -1] # 'o'
s[-1] # '0'
s[-2] # 'l'
s[s.length] # nil
而如果需要改变字符串里面的单个字符串,字需要简单地讲方括号置于表达式的左侧即可。同时也支持是一个任意长度的字符串。
s[0] = 'H'
如果希望获得一个子字符串,你只需要在方括号里使用由两个逗号分割的操作数即可。第一个操作数指定索引值(可能为负数),第二个操作数指定长度值(必须是非负值)。
s = "hello"
s[0, 2] # "he"
s[-1, 1] # "o"
s[0, 0] # ""
s[0, 10] # "hello" 返回所有可用的字符串
s[s.length, 1] # "" 在刚好超过字符串长度的地方是一个空字符串(方便赋值时在字符串末尾添加字符串)
s[s.length+1, 1] # nil
s[0, -1] # nil
同样,你也可以将任意的字符串复制给个上述方式索引的子字符串。
另一种提取、插入、删除和替换子字符串的方法是通过一个 Range 对象索引一个字符串。
s = "hello"
s[2..3] # "ll"
s[-3..-1] # "llo"
s[0..0] # "h"
s[0...0] # ""
s[2..1] # ""
s[7..10] #nil
你还可以通过一个字符串来索引另一个字符串,那么第一个被匹配的字符串将被返回,否则返回nil。事实上这种形式的字符串索引只有出现在一个赋值语句的左侧时采用用武之地。
s = "hello"
while (s["l"])
s["l"] = "L";
end
同样,你还可以使用一个正则表达式来索引一个字符串。
s[/[aeiou]/] = "*"
对字符串进行迭代
在 Ruby1.9 里定义了三个迭代字符串的方法,each_byte 按照字节对一个字符串进行迭代,each_char 按照字符进行迭代,each_line 按照行进行迭代。另外,each_char 比使用[]操作符和字符索引更加高效。
字符编码
Ruby 在1.9版之前堪称是对字符编码支持最差的语言之一,而现在变成了支持最好的语言之一。情况比较复杂,参见我的另一篇笔记《Ruby 与字符编码》。
数组
Ruby 的数组可以用负值进行索引,如果赋值操作时使用负值且超出范围,那么报错。
另外Ruby 支持一种更加通用的数组字面量特殊语法。
%w[this is a test]
%W| ( [ { < |
同时也可以通过 Array.new 构造函数来构造数组。
empty = Array.new # 返回空数组 []
nils = Array.new(3) # [nil, nil, nil]
zeros = Array.new(4, 0) # [0, 0, 0, 0]
count = Array.new(3) { |i| i + 1} # [1, 2, 3]
Ruby 还定义了其他有用的操作符。
[1, 2] + [3, 4, 5] # [1, 2, 3, 4, 5]
["a", "b", "c", "b", "a"] - ["b", "c", "d"] # ["a", "a"]
[0] * 4 # [0, 0, 0, 0]
同时,还定义了 | 和 & 操作符。不过,操作符不具有传递性,a | b 不等于 b | a 。同时,Ruby 不保证返回数组中元素的顺序。
a = [1, 1, 2, 2, 3]
b= [4, 4, 3, 3, 2]
a | b # [1, 2, 3, 4]
b | a # [4, 3, 2, 1]
a & b # [2, 3]
b & a # [3, 2]
同时数组还定义了一系列有用的 API,例如 each 等等。
哈希
Ruby 里的 hash 采用一种哈希表的数据结构来实现的。那些作为哈希键的对象必须有一个 hash 的方法,改方法返回一个 Fixnum 的哈希码。如果两个键相同,那么它们必须具有相同的哈希码。不相等的键也可以拥有相同的哈希码,但是仅当哈希表有极少的重复时,其效率才是最高的。
Hash 类采用 eql? 方法比较键之间的相等性,对于大多数 Ruby 类而言,eql? 方法与 == 操作符一样。如果你新定义的类重写了 eql? 方法,那么你必须同时重写 hash 方法,否则你的类无法作为一个哈希键。
如果使用一个可变对象作为哈希键会带来一些问题,改变一个对象的内容通常会改变其哈希码,那么内部的哈希表将会被破坏,而且该哈希的行为也将不正确。由于字符串是可变的,但是有常常作为哈希键,所以 Ruby 将它们作为特例进行了处理。对那些作为键的字符串,Ruby 会生成它们的私有拷贝。但是这是唯一的特例,如果使用任何可变对象作为哈希键时,可以考虑为这些可变对象生成私有拷贝,或者调用 freeze 方法。如果你必须使用可变的哈希键,那么请在每次更改键后,调用 Hash 类的 rehash 方法。
范围
一个 Range 对象表示位于开始值和结束值之间的一些值。begin..end 范围对象中值满足:
begin <= x <= end
begin...end 范围对象中的值满足:
begin <= x < end
所有范围的端点值都必须实现 <=> 操作符。如果端点没有实现 succ 方法,那么我们称之为连续的成员关系测试。否则,即离散的成员关系测试。该集合包含 begin、begin.succ、begin.succ.succ 等等。范围的成员关系是一种集合的关系。值得注意的是,对离散的成员关系测试开销要远大于对连续的成员关系测试开销。
在Ruby1.9中,cover? 方法使用连续的成员关系测试。include? 和 member? 是同义词,只有在范围端点是数字时使用连续的成员关系测试,否则它们就会使用离散性的成员测试。
符号
一个 Ruby 解释器的典型实现会维护一个符号表,在这个表中它存储了所知晓的所有类、方法及变量名称,是这样一个解释器可以避免大多数字符串比较:比如,它可以通过一个方法名在符号表中的位置来对其进行引用。这样一来,就将一个开销较大的字符串操作转换成了一个开销较小的整数操作。
Symbol 也有一个 %s 字面量语法。你可以使用 intern 和 to_sym 方法将一个 String 对象转换成一个 Symbol,而且你也可以使用 to_s 的方法或其别名 id2name 将一个 Symbol 转回一个字符串。
当你使用字符串的目的是在于将其作为独一无二的标识符,而不在于它文本的内容时,请改用符号。比较两个 Symbol 对象的相等性要远远快于比较两个字符串的相等性。
在 Ruby1.9 中,Symbol 类定义了一些 String 方法,比如 length、size、比较操作符,甚至 [] 和 =~ 操作符,而且符号也可以被作为一种不可变的(也是不可被垃圾回收的)字符串来使用。
True、False 和 Nil
True、False 和 Nil 分别是 TrueClass、FalseClass 和 NilClass 的单键实例。Ruby 中并没有 Boolean 类。
当 Ruby 需要一个布尔值时,nil 表现为 false,而 nil 和 false 之外的所有值都表现为 true。
判断一个值是否为 nil :
o == nil
o.nil?
对象
对象引用
Ruby 中使用对象时,都是在使用对象的引用,所有的对象都通过引用来操作。但是,在实现时,Fixnum 和 Symbol 对象实际上是“立即值”,而非引用。然而,Fixnum 和 Symbol 都没有可变的方法,也就是不可变的,因此又可以把它当做是按引用来操作的(唯一区别在于,不能为立即值定义单键方法)。
t = s = "Ruby"
s[-1] = ""
print t # "Rub"
Ruby 中方法实参是通过值而不是引用来传递的,因此,当把一个对象传递给一个方法时,只不过被传递的值正好是对象的引用罢了。
对象标识
在 Ruby1.9 中,可以通过 id 和 object_id 来获得对象独一无二的标识符。
对象的类和类型
通过 class 方法来确定一个对象所属的类。也通过 instance_of 方法来检查对象是否为某个类的实例。
o.class == string
o.instance_of? String
通过 superclass 可以确定某个类型的超类。通过 is_a? 可以确定某个对象是否为某个类和其子类的实例。
o.class.superclass
String.superclass
o.is_a? Comparable # 也可以判断 mixin 模块
o.is_a? Object
对象的相等性
Ruby 拥有很多的方法用来比较对象的相等性。
equal? 方法
equal? 方法由 Object 定义,用于确定两个值是否引用了同一个对象。
a = "Ruby"
b = c = "Ruby"
a.equal? b # false
b.equal? c #true
一般而言,子类永远不要重写 equal? 方法。
另一种检查两个值时候引用同一个对象的方法是比较他们的 object_id 。
== 操作符
在 Object 类里,他是 equal? 方法的同义词。但大多数的类都重定义了这个操作符,使得不同的实例之间也能进行相等性测试。
Array 和 Hash 都定义了 == 操作符。如果两个数组拥有相同的数量的元素,并且对应位置上的元素通过 == 比较也相等,那么两个数组通过 == 比较是相等的。如果两个哈希拥有相同数量的键值对,额况且对应的键和值也相等那么这两个值通过 == 比较之后是相等。
Number 类在它们的 == 操作符里将进行简单的数值转换,比如 Fixnum 1 和 Float 1.0 通过 == 比较之后是相等。
诸如 String 、 Array 和 Hash 类的 == 操作符,通常要求两个操作符属于同一个类。但如果右侧的操作数定义了一个 to_str、 to_ary 和 to_hash 的方法,那么原先的 == 操作符会调用右侧的操作数所定义的 ==。
class MyArray
def initialize(array)
@array = array
end
def to_ary
@array
end
def ==(array)
to_ary == array
end
end
array = [1, 2, 3]
my_array = MyArray.new([1, 2, 3])
array == my_array # true
!= 操作符会简单的使用 == 操作符并且反转其结果。但在 1.9 中,类也可以显示的定义自己的 != 操作符。
eql?
Object 将 eql? 方法定义为 equal? 方法的同义词,那些重写了 eql? 方法的类通常将其作为更加严格的 == 操作符,即不允许类型转换。
1 == 1.0 # true
1.eql? 1.0 # false
Hash 类采用 eql? 检查两个哈希键是否相等,如果两个对象通过 eql? 结果比较是真,那么它们的 hash 方法必须返回相同的值。
=== 操作符
=== 操作符被称为“条件相等性”操作符,用于测试一个 case 语句的目标值是否和某个 when 从句相匹配。
Object 类定义了一个默认的 === 操作符,它会调用 == 操作符。某些关键的类重写了 === 操作符:Range 用来测试一个值是否在范围内; Regexp 类用于测试一个字符串是否与某个正则表达式相匹配;Class 类用于测试一个对象是否该类的一个实例;Symbol 类在当其右侧操作数和左侧操作数是同一个符号对象时,或者当右侧操作数是一个持有和左侧符号对象相同文本的字符串时,改操作符返回 true。
=~ 操作符
String、Regexp 和 Symbol 定义了 =~ 操作符用于进行模式匹配。Object 也定义了这个操作符,它直接返回 false。!~ 被定义为 =~ 的反义,在 Ruby1.9 以后,你也可显式自定义。
对象的顺序
类通过实现 <=> 来定义其顺序性。当左侧操作数小于右侧操作数时,返回-1,反之返回1,相等返回0。一般情况下,定义了 <=> 操作符的类会将 Comparable 模块作为一个 mixin 包含进来,以同时获得 <、<=、==、>= 和 > 操作符。还有一个比较方法 betwee?
如果 <=> 返回 nil 。那么所有基于它的操作符都返回 false。例如 NaN这个特殊的 Float。
nan = 0.0/0.0
nan < 0 # false
nan > 0 # false
nan == 0 # false
nan == nan #false
nan.equal?(nan) # true
对象转换
显式转换
一般情况下,Ruby 中的方法不会对自动对类型进行转换,如果需要,你应该手动调用转换方法。最为常见的就是to_s、to_i、to_f 及 to_a 方法,他们分别将对象转换成为 String、Integer、Float 和 Array。
关于 # 的字符串内插,Ruby 会主动调用其 to_s 的方法。to_s 的另一个重要的选择性替代是 inspect 方法。通常而言,to_s 用于返回人类可读的对象表现形式,而 inspect 方法用于调试,它能返回一个给开发人员带来帮助的表现形式。而 Object 定义的 inspect 方法只是简单的调用 to_s。
隐式转换
Ruby 中还定义了 to_str、to_int、to_ary 和 to_hash,用于方法进行隐式的转换。不过在内建类里,这些转换方法并没有被普遍的实现。
在 Ruby1.9 里,内建的 String、Array、Hash、Regexp 及 IO 类都定义了一个名为 try_convert 的类方法。如果这个方法的实参定义了以上一个合适的隐式转换方法,那么就会对其方法进行调用,否则返回 nil。例如,对象 o 定义了 to_ary 的方法,那么 Array.try_conver(o) 将返回 o.to_ary,否则返回 nil .
转换函数
Kernel 模块还定义了4个全局转换函数——Array、Float、Integer 及 String。
Array 函数视图调用其实参的 to_ary 方法来将其转换成为一个数组。如果没有定义 to_ary 方法,或者该方法返回 nil。那么就试着调用其 to_a。如果没有定义 to_a 方法,或者改方法返回nil。这简单的返回一个数组,并把该实参作为该数组的第一个元素。
Float 对象会把 Number 的实参直接转换成一个 Float 对象。任何非 Numeric 的值,都会调用其 to_f 的方法。
Interger 函数将其实参转换成为一个 Fixnum 或 Bignum。如果其实参是一个 Numeric 它直接进行转换,浮点值被截断。如果是一个字符串,它会寻找一个基数指示符(以0开头八进制,以0x开头十六进制,以0b开头二进制),并据此对字符串进行转换。与String.to_i 不同,Integer 函数不允许末端出现非数值字符。对于任何其他类型转换,Integer 会首先尝试 to_int ,然后调用 to_i 。
String 只是简单的调用其参数的 to_s 的方法。
算数操作符的强制转换
数值类型定义了一个 coerce 的方法。意图将其实参类型转换成为其调用者类型,或者这两个对象的更一般兼容形式。coerce 返回一个数组,第一个元素由 coerce 方法转换而来,第二个元素来自其调用者(如果需要,就进行转换)。
1.1.coerce(1) # [1.0, 1.1]
requier "rational"
r = Rational(1, 3)
r.coerce(2) # [2/1, 1/3]
算数操作符会使用 coerce 方法。例如,Fixnum 的 + 操作符不知道如果操作 Rational 数字,于是右侧的操作数会调用 coerce 方法,并将左侧的操作数作为实参传进去,然后在返回数组的两个值上简单的调用 + 即可。
对象拷贝
clone 和 dup 方法都会返回一个调用它们对象的浅拷贝,即如果拷贝对象有指向其他对象的引用,那么只有这些引用被拷贝,而引用的对象本身不被拷贝。
如果拷贝对象定义了 initialize_copy (拷贝构造函数)的方法,那么 clone 和 dup 就会简单的分配一个新的实例空间(该实例所属类与被拷贝对象相同),然后在其上调用 initialize_copy 的方法。
类也可以直接重写 clone 和 dup 的方法。
clone 和 dup 的区别有两点,其一,clone 会拷贝一个对象被冻结和受污染的状态,而 dup 仅仅拷贝受污染的状态; 其二,clone 方法拷贝一个对象所有的单键方法,而 dup 不会。
编组对象
Marshal.dump 用来保存一个对象的状态,它返回一个二进制的字符串。你可以将一个 I/O 流对象作为第二个实参传递给 Marshal.dump 的方法,它会把保存的对象的状态写入这个流中。
为了恢复一个编组后的对象,你可以将一个包含了该对象的字符串或 I/O 流传递给 Marshal.load。
值得注意的是,这两个方法存在版本依赖。
我们可以利用这两个方法来创建对象的深度拷贝。
def deepcopy(o)
Marshal.load(Marshal.dump(o))
end
值得一体的是,YAML 是 Marshal 模块的一种被广泛采用的替代方案。
冻结对象
freeze 方法可以将一个对象冻结起来,一个被冻结的对象将变得不可改变——它所有的内部状态都不能被改变,而且对其可改变的方法的调用也会失败。
s = "ice"
s.freeze
s.frozen? # true
s.upcase! # RuntimeError: can't modify frozen String
污染对象
taint 方法将任何对象标记为受污染的。一旦一个对象受污染,那么任何源自它的对象都变成受污染的。可以通过 tainted? 方法测试一个对象是否受污染。
s = "untrusted"
s.taint
s.tainted? # true
s.upcase.tainted? # true
s[3, 4] # true
用户输入(命令行参数、环境变量以及 get 方法读取的字符串)都会自动成为受污染的。
一个受污染的对象可以通过 untaint 方法变成为受污染的。
网友评论