Ruby术语集
--按英文排序
A
AWK
由Alfred Aho(A)、Peter Weinberger(W)和Brian Kernighan(K)共通创建的一种小型脚本语言。
B
blade
ml archive (blade/ruby)
C
Coerce
进行数据类型变换的方法。如果某数值计算方法从参数那里获得了一个类型不明的实例时, 它会调用coerce方法来进行数据类型变换。coerce方法会返回参数中的数值和本身。
Ruby库的数值类型变换顺序为
Fixnum -> Bignum -> Rational -> Float -> Complex
D
Data
该类可将C中的指针处理成Ruby对象。由C指针、mark函数和free函数构成。如果您想使用C语言来扩充Ruby的功能的话, 就必须掌握该类的使用方法。相反地, 如果您没有这个打算的话, 它也就没什么用了。
defined?
该操作符可以检查某对象(表达式)是否已经被定义。若未被定义就返回nil,若已被定义,就以字符串形式返回它的种类。虽然defined?看似一个方法,实际上它是Ruby语法中的操作符, 它不会对参数进行计算。因此下面的表达式
defined? print("abc\n")
不会输出任何内容。
E
Eiffel
面向对象的编程语言。据说,早先matz在读了该语言作者所撰写的《Object-oriented Software Construction》之后顿觉恍然大悟。但奇怪的是Ruby与Eiffel却并不相似。如果硬要找出雷同之处的话, 可能只有两点: 1.Ruby的块以end结尾;2.Ruby也有rescue这个保留字。
end
该保留字表明了块的结束。据统计,大约有33%的人在看到这个end时会联想起Pascal(纯属杜撰)。但它并不和begin连用,因此它可能更接近于Ada或Eiffel。
Ruby之所以不使用C和Perl中的{},主要是因为以下原因
*
避开单句·复句的问题
例如在C语言中, 若想向下例中添加语句时
if (a==b)
c();
如果写成这样的话
if (a==b)
c();
d();
就会造成难以发现的bug。即使是Pascal也存在这个问题。
*
回避else的问题
与上例类似, 如果写出下面这样的语句的话
if (a==b)
if (c==d) foo();
else bar();
就会引起混乱。其实它的本意应该是
if (a==b) {
if (c==d) foo();
else bar();
}
这个样子。
*
提高可读性
可能您对此持有异议, 但有的人认为:用end来结束块的做法可以提高程序的可读性。
*
begin和case语法表达上的问题
说句实话,matz曾多次想把end用作变量。甚至探讨过在ruby语法中添加{ }的问题,但因为无法完美地解决begin和case的语法表达的问题,最终不得不放弃这个念头。恐怕这才是最大的理由吧。
ENV
该对象的运作方式与访问环境变量的Hash相同。实际上,它就是添加了特殊方法的Object类的实例。使用该对象来修改环境变量后,其变化将会影响到Ruby的子进程。
F
FAQ
Frequently Asked Questions
常见的问题和解答。Ruby FAQ尚处于不断完善的阶段,问题和解答是随时更新的。
G
goto
Ruby中没有该语句。这并不是因为“我们觉得不应该使用”goto,而是“实现其功能实在是太麻烦了”。实际上,您可以使用catch/throw或异常来实现goto的功能。
H
I
J
JARH
Just another Ruby hacker,
K
L
M
main
顶层中的self。因为self是必不可少的,所以它只是表明在顶层中有个Object类的实例--self而已。另外为了操作Object类,还特别定义了若干特殊方法。
已定义的特殊方法
* private
* public
* include
matz
Ruby的作者,也叫松本 行弘。请参考<URL:http://www.st.rim.or.jp/~fuku/cmail/>,另外,他还是3个孩子的父亲。
Mix-in
混合插入、糅合
就像在冰淇淋中混合多种配料可以做成美味的混合冰淇淋一样,在类中混合插入各种模块就可以添加相应的功能。请参考继承。
matz坚信滥用多重继承会导致继承关系的混乱,因此Ruby中不允许使用多重继承。同时为充分发挥继承功能的优势,Ruby支持两种继承关系:1.使用is-a语句的继承;2.使用Mix-in来共享并继承模块中的功能。
N
O
P
Perl
不必多说了罢
POLS
Principle of least surprise
Python
Ruby的劲敌。其功力深厚,可谓“千年蛇妖”。但matz认为Python的功能仍不完美,不然就不会创造Ruby了。最要命的是Python限定了名称长度(6个字符)。
Q
R
RAA
Ruby Application Archive(RAA)
RCR
Ruby Change Request
RD
Ruby Document
Ruby
面向对象的脚本语言。Ruby的意思是“紧跟在Perl(pearl是6月的诞生石,Ruby则是7月的诞生石)之后”。Ruby并不是其他单词的缩写。
S
Sather
面向对象的编程语言。其实matz钟爱Sather胜过Eiffel。但Ruby与Sather一点都不像。
self
表示被调的表达式。那为什么把它叫做self呢?因为如果把方法看作动词的话,那么被调就是该动作的主语,从方法的角度来看,被调当然就是自己了。一般人认为,Ruby对此并未深究,只不过是模仿Smalltalk的做法罢了。
Smalltalk
面向对象的编程语言。它奠定了现代面向对象 理论体系的基础。
super
在重定义的方法中调用上级方法。省略参数时,将使用主调方方法的参数来进行调用。
*
问题:
修改参数中给出的数值后再调用super的话,将会使用原来的值还是修改后的值呢?
def foo(a)
print a
end
def self.foo(a)
a=25
super
end
foo(5) # 5 or 25??
*
答案:
使用修改后的值(25)
T
Thread
原为Thread of control的缩略语,意指一系列的控制流。在Ruby中,一个程序内可以同时存在若干线程。
U
undef
将方法设置为未定义状态。继承 和Mix-in的功能都是在类中添加方法,而undef则可以取消方法。但是,如果取消了类所必需的方法(被其他方法所调用的方法)的话,其后果不堪设想。
V
W
X
Y
Z
--按拼音排序
A
B
被调
Receiver
方法的执行主体。也就是方法调用表达式的`.'中的左边部分。在方法内,可以使用self来表示它。另外,您可以使用@变量名的形式来访问被调的实例变量。
变量
Variable
贴在对象上的标签。Ruby的变量包括全局变量、局部变量和实例变量。因为常数的值不能改变,所以不是变量。但它也是一种标签,因此在这一点上它与变量是相同的。
C
常数
Constant
一旦定义了就不能再改变的变量。这个定义本身似乎就有点矛盾。
重定义
Override
即指再定义。重新定义超类或include模块中已经定义的方法。使用super即可调用原来的方法。
抽象数据类型
Abstract Data Type
将数据构造和对其进行操作的过程封装在一起,就形成了抽象数据类型。对抽象数据进行操作时,必须使用封装内的操作才行。其结果是不能从外部直接使用数据构造,同时一旦内部构造发生变化也不会对外界造成不良影响。我们把这个过程叫做封装。
初始化
Initialize
使对象(或“某事物”)变为“就绪”状态。对实例进行初始化操作时,需要重定义Object#initialize方法。类方法Class#new的默认定义就是对新生成的实例执行initialize方法。传给new的参数会被原封不动地传给initialize。另外,若带块调用时,该块会被传给initialize。
因此,不必对Class#new进行重定义。
辞典
Dictionary
根据给出的条目即可查出对应的定义。它是哈希表的别名。面向对象的始作俑者Smalltalk把类似哈希表的数据构造称作“辞典”,所以时至今日仍然有一些人把哈希表称作辞典。
D
大Endian
Big Endian
美洲大陆的原住民是Indian而并非Endian,那么这个Endian的词源是什么呢?其实它出自Jonathan Swift写的《格列佛游记》。这本书中的人们因为吃鸡蛋的方法不同而分成两类,从圆头开始吃的叫大Endian,从尖头开始吃的叫小Endian。在计算机业界,该词表示CPU等排列数据的一种方式,据说网络一族的人们喜欢大Endian。请参考字节顺序。
大规模退出
Non-Local Exit
它并不是那种发生在break, next, redo, retry, return等方法的范围内的普通退出,而是一种跨度极大的退出。只要没被捕捉到,它甚至会跳出方法调用的牢笼来引发中断。Ruby的大规模退出包括由异常引起的退出和catch/throw。
大多数的异常(包括由exit所引发的SystemExit在内)都会被rescue 捕捉到。但是若该异常不值得捕捉(例:内存分配失败/解释器的bug)的话,就会放他一马。
在catch/throw中,通常会从被throw的地方起一直跳转到与throw部分具有相同标签的catch部分为止。
迭代器
Iterator
即指调用带块方法。当初为了进行迭代操作而设置了带块方法,现在它仍然常被称作迭带器。虽然可以将那些进行迭代操作的方法叫做迭代器,但如果将所有带块方法的调用过程都看作迭带器的话,势必会引起混乱。
调用带块方法
我们把那些可接受代码段(块)的方法叫做带块方法。调用带块方法就是指调用这些带块方法的过程。
在带块方法中使用yield就可以执行块的内容。
当然了,如何处理给出的块,这完全取决于方法。所以,如果硬是把块传给一个不能带块的方法的话,也不会有什么结果,而且也不会发生错误。
动态绑定
Dynamic Binding
指在运行时根据操作对象的数据类型的不同来选择合适的过程(方法)。它可以提高程序的灵活性。它是面向对象的必要条件之一。在Ruby中变量是没有类型的,因此必然可以进行动态绑定。
动态局部变量
Dynamic Local Variable
它是一种特殊的局部变量。Ruby的局部变量的作用域是固定的,因此在编译时就会生成局部变量。动态局部变量则有所不同,每次执行时才会生成变量。在块中首次被赋值的局部变量就是动态局部变量,其作用域仅限于块的内部。这主要是为了让各个Thread都能拥有自己独立的变量而设的。
对象
Object
即指物体。举个例子,“爱”可能不是对象,但“情书”却是对象。甄别某事物是否属于对象,这可能是个哲学问题。或许正因为如此,面向对象也变得扑朔迷离起来。在计算机业界有人认为对象就是指内存中的特定空间。到底何谓对象,还真是个难题。另外,请参考封装和抽象数据类型。
多态
多态, Polymorphism
根据对象的不同选择合适的操作。在Ruby中的实现方法是,根据被调的对象的不同来选择不同的方法。
*
例
obj = "abc"
print obj.length, "\n" # => 3
obj = [1,2,3,4]
print obj.length, "\n" # => 4
F
方法
Method
对对象进行的操作。操作对象(被调)以self来表示。在Ruby中,除去内部类的对象以外,通常对象的构造都是动态确定的。某对象的性质由其内部定义的方法所决定。
封装
Encapsulation
将内部结构和算法隐藏起来,以确保只有特定的过程(也叫方法)才能直接操作数据,这种隔离方法就叫做封装。请参考抽象数据类型。
在Ruby中,只有方法可以操作实例变量,因此可以说Ruby中的封装是强制性的。
G
关联数组
Associative Array
哈希表的别名。因为哈希表可以使用任意的键来引出值,这就是“关联”特性。另外,可以将哈希表看作是使用非数字形式索引的数组,这是它的“数组”特性,因此它也叫做“关联数组”。以前是使用硬件来实现关联数组(也叫关联记忆)的功能的,但是随着计算速度的攀升以及关键算法(叫做“哈希表”,它是现在的哈希表的词源)的成功研发,现在只用软件就可以实现其功能了。
H
哈希表
Hash
Ruby中的一种从键到值的映像(mapping)。也叫做关联数组或辞典。哈希表之所以得此名,是因为在实现其功能时使用了一种叫做“哈希表”的算法。哈希的意思是“切碎”,是“hashed beef”中的“hash”。
函数
Function
严格地讲,Ruby中没有函数。但那些省略被调的方法调用看来确实很像函数,而且有的方法根本不需要self或实例变量等被调信息,事实上后者已成为函数了。所以有时也就网开一面地把这样的方法叫成函数了。
通常将这种函数(式的方法)的方法可视性设成了private,这样就只能以省略被调的形式来调用它们了。这类方法中比较有代表性的是 模块函数。
环境变量
Environment Variable
父进程传给子进程的值。使用ENV就可以访问环境变量。传给子进程只是环境变量的副本,因此子进程无法通过环境变量来向父进程传递信息。这就好比老子不会听小孩的话一样。
J
继承
Inheritance
主要依赖于从祖先或亲戚那里继承的功能,而自己只做一些补充性的工作。在现实世界中,这种行为是要遭到唾弃的,但在计算机世界里这却是个很经济的做法。继承也可以指在某类中添加新功能后生成一个新的类。继承可以用is-a的关系来加以诠释。例如,如果您要生成一个“理科学生”类的话,需要首先继承描述一般学生特征的“学生” 类,然后再添加“整天忙于应付实验”等特征后即可生成该类。若不存在is-a关系,而只想共享某些特性或功能时,我们推荐您使用Mix-in。
脚本
Script
脚本,特指由解释器进行处理的较短的程序。当然了,其中也不乏大作。
脚本语言
Script Language
脚本语言。
局部变量
Local Variable
只能在特定范围内使用的变量。该范围就是作用域。Ruby的作用域包括
* 程序整体
* 类·模块定义
* 方法定义
* 块
几种。只有块能访问外侧作用域的局部变量。局部变量的作用域 从该变量首次被赋值的位置起 直到该赋值位置所在的作用域结束为止。这个优先范围是静态决定的,与具体的运行情况无关。
K
块
Block
可用来构成循环或打家劫舍。
L
类方法
Class Method
就是类的方法。可分为两种:第一种是在所有的类的超类Class中定义的,且被所有的类所共享的方法;第二种是各个类所特有的特殊方法。这倒没什么问题。重要的是类方法中的self指的是类本身,这点需要牢记。
立即值
Immediate Value
实际的数值就保存在变量之中,这和引用是不同的。目前,Ruby中只有Fixnum、Symbol和nil/true/false是立即值。然而Ruby的某些版本并未将Fixnum算做立即值,这也无关紧要。在理论模型层面上,可以将所有的值都看作是对某对象的引用。
理论体系
Paradigm
类似于“思维方式”,这个词很难说得清楚。
M
面向对象
以对象为中心的理论体系。英语中的"Object-Oriented"是个形容词,可是到了日语中就变成名词了。似乎只要将对象置于思考的中心点就万事大吉了,但也要兼顾一下几点
* 继承
* 封装
*
多态
(或者动态绑定)
有人甚至把它看作是包治百病的“魔法”,但事实上世界并非如此简单。面对对象概念诞生至今已逾20个年头,它已经磨砺成为一把实用的利剑。
面向对象设计
Object-Oriented Design
以对象作为出发点的系统设计
面向对象编程
Object-Oriented Programming
以对象作为编程的中心。
面向对象分析
Object-Oriented Analysis
以对象为根本的系统分析。
模块函数
Module Function
在那些函数式的方法中,模块函数既可用作模块的方法,又可用作特殊方法。例如Math模块中的大部分方法都是模块函数。您既可以这样
Math.sqrt(2)
又可以这样
include Math
sqrt(2)
来使用它们。
N
内部类
Built-In Class
这些内部类被嵌入Ruby解释器内部,其实例的结构与普通对象有所不同。我们不建议您继承内部类。Ruby的内部类如下所示(实际上远不止这些,更多详情请参考内部类/模块/异常类)
* Array
* Bignum
* Class
* Data
* FalseClass
* File
* Fixnum
* Float
* Hash
* IO
* MatchData
* Module
* NilClass
* Proc
* Regexp
* String
* Struct
* Thread
* TrueClass
P
排序
Sort
将对象依次排列。只要元素是可数的(include了Enumerable)、且已定义了顺序(定义了<=>)的话,Ruby就可以对这些元素的集合进行排序。这并不仅限于数组,也适用于其他复杂对象的集合。
破坏性的
Destructive
因为String#chop!, Array#concat这种方法会直接改变被调的状态,因而会产生“破环性的作用”。不过您不必担心,因为它们不会损坏您的计算机。
Q
全局变量
Global Variable
在程序的各个角落中都可以使用的变量。比较危险,少用为佳。
R
S
实例
Instance
即指对象。在强调对象归属于某类时,常使用实例这个词。据说有好多人因为不了解对象和实例的关系,因而搞不懂面对对象到底是怎么一回事儿。
实例变量
Instance Variable
对象所特有的变量。Ruby实例变量名前都有一个@符号,您只能在方法内部使用它。
T
特殊方法
Singleton Method
专属于某特定对象的方法。请参考方法。在下列情况下,其他对象也可以继承该特殊方法。
* Kernel#clone时
* 定义了子类时
若在特殊方法中重定义了原先类中的方法时,可以使用super来调用原来的方法。
特殊类
Singleton Class
只对应于某特定对象的假想类。
W
文档
Document
matz最头疼的就是写文档了。他平时总是说“源代码就是文档。连bug也写得清清楚楚”,当然了谁都不以为然。
X
小Endian
Little Endian
开始有10个人,后来越来越少。在计算机业界中,它是表示一种排列数据的形式。据说有一家大的CPU制造商很喜欢小Endian。请参考字节顺序。
Y
异常
Exception
遇到非正常情况就会引发异常。发生异常时,只要没使用begin中的rescue进行捕捉的话,它将跨越方法调用的阻拦,进而中断程序(thread)的运行。有了异常处理功能之后,我们就不必再逐一检查Ruby程序中的每个异常情况了。发生异常的地点信息被保存在$@中,而异常本身的信息被保存在$!中。
Z
再定义
Redefinition
即指重定义。
字节顺序
Byte Order
将0x01020304这4个字节数据按照1,2,3,4或是4,3,2,1的顺序排列。前者叫做大Endian,而后者叫做小Endian。人们一直在争论哪种方法更好,但至今尚无定论。
Ruby的运行平台
在各方有志之士的努力下,Ruby已经被移植到多种平台。下面,就从OS或开发环境等方面对Ruby的运行环境做一个简要介绍。
关于安装和编译问题,请参考Ruby 安装指南。
* Unix
* Windows(Win32)
o Win32 native版
+ mswin32
+ MinGW (mingw, mingw32)
+ bccwin32
o Cygwin (cygwin)
* Mac
o Mac OS X
* BeOS
* MS-DOS
o DJGPP (djgpp)
* OS2 (os2_emx), (emx)
* VMS
* human68k
* GNU (GNU Hurd)
* WindowsCE
o mswince
pack模板字符串
下面就是Array#pack、String#unpack中所用到的模板字符的一览表。模板字符后面可以跟上表示"长度"的数字。若使用'*'来取代"长度"的话, 则表示"剩下的所有字符"之意。
长度的定义因模板字符的不同而有所差异, 大体上像
"iiii"
这样的连续字符可以写成
"i4"
这个样子。
在下面的说明中, short和long分别表示长度为2和4字节的数值(也就是通常32位机器所指的short和long的大小), 这与具体的系统无关。 若`s', `S', `l', `L'后面出现`_'或`!'(如"s!")的话, 则表示这个short或long的大小取决于具体的系统。
请注意: `i', `I' (int)的大小总是取决于系统的, 而`n', `N', `v', `V'的大小则是系统无关的(不能添加`!')。
模板字符串中的空格会被忽略。 ruby 1.7 特性: 另外,从`#'开始到换行处或者到模板字符串结尾之间的部分会被看做是注释。
在下面的说明中, 若针对某问题Array#pack和String#unpack有不同的解释时, 就使用/将两者分开, 即采用 "Array#pack的说明部分/String#unpack的说明部分" 的形式加以说明.
*
a
ASCII字符串(塞入null字符/保留后续的null字符或空格)
["abc"].pack("a") => "a"
["abc"].pack("a*") => "abc"
["abc"].pack("a4") => "abc\0"
"abc\0".unpack("a4") => ["abc\0"]
"abc ".unpack("a4") => ["abc "]
*
A
ASCII字符串(塞入空格/删除后续的null字符和空格)
["abc"].pack("A") => "a"
["abc"].pack("A*") => "abc"
["abc"].pack("A4") => "abc "
"abc ".unpack("A4") => ["abc"]
"abc\0".unpack("A4") => ["abc"]
*
Z
null终点字符串(与a相同 / 删除后续的null字符)
["abc"].pack("Z") => "a"
["abc"].pack("Z*") => "abc"
["abc"].pack("Z4") => "abc\0"
"abc\0".unpack("Z4") => ["abc"]
"abc ".unpack("Z4") => ["abc "]
*
b
位串(从下级位到上级位)
"\001\002".unpack("b*") => ["1000000001000000"]
"\001\002".unpack("b3") => ["100"]
["1000000001000000"].pack("b*") => "\001\002"
*
B
位串(从上级位到下级位)
"\001\002".unpack("B*") => ["0000000100000010"]
"\001\002".unpack("B9") => ["000000010"]
["0000000100000010"].pack("B*") => "\001\002"
*
h
16进制字符串(下级半字节(nibble)在先)
"\x01\xfe".unpack("h*") => ["10ef"]
"\x01\xfe".unpack("h3") => ["10e"]
["10ef"].pack("h*") => "\001\376"
*
H
16进制字符串(上级半字节在先)
"\x01\xfe".unpack("H*") => ["01fe"]
"\x01\xfe".unpack("H3") => ["01f"]
["01fe"].pack("H*") => "\001\376"
*
c
char (8bit 有符号整数)
"\001\376".unpack("c*") => [1, -2]
[1, -2].pack("c*") => "\001\376"
[1, 254].pack("c*") => "\001\376"
*
C
unsigned char (8bit 无符号整数)
"\001\376".unpack("C*") => [1, 254]
[1, -2].pack("C*") => "\001\376"
[1, 254].pack("C*") => "\001\376"
*
s
short (16bit 有符号整数, 取决于Endian) (s! 并非16bit, 它取决于short的大小)
小Endian:
"\001\002\376\375".unpack("s*") => [513, -514]
[513, 65022].pack("s*") => "\001\002\376\375"
[513, -514].pack("s*") => "\001\002\376\375"
大Endian:
"\001\002\376\375".unpack("s*") => [258, -259]
[258, 65277].pack("s*") => "\001\002\376\375"
[258, -259].pack("s*") => "\001\002\376\375"
*
S
unsigned short (16bit 无符号整数, 取决于Endian) (S!并非16bit,它取决于short 的大小)
小Endian:
"\001\002\376\375".unpack("S*") => [513, 65022]
[513, 65022].pack("s*") => "\001\002\376\375"
[513, -514].pack("s*") => "\001\002\376\375"
大Endian:
"\001\002\376\375".unpack("S*") => [258, 65277]
[258, 65277].pack("S*") => "\001\002\376\375"
[258, -259].pack("S*") => "\001\002\376\375"
*
i
int (有符号整数, 取决于Endian和int的大小)
小Endian, 32bit int:
"\001\002\003\004\377\376\375\374".unpack("i*") => [67305985, -50462977]
[67305985, 4244504319].pack("i*") => RangeError
[67305985, -50462977].pack("i*") => "\001\002\003\004\377\376\375\374"
大Endian, 32bit int:
"\001\002\003\004\377\376\375\374".unpack("i*") => [16909060, -66052]
[16909060, 4294901244].pack("i*") => RangeError
[16909060, -66052].pack("i*") => "\001\002\003\004\377\376\375\374"
*
I
unsigned int (无符号整数, 取决于Endian和int的大小)
小Endian, 32bit int:
"\001\002\003\004\377\376\375\374".unpack("I*") => [67305985, 4244504319]
[67305985, 4244504319].pack("I*") => "\001\002\003\004\377\376\375\374"
[67305985, -50462977].pack("I*") => "\001\002\003\004\377\376\375\374"
大Endian, 32bit int:
"\001\002\003\004\377\376\375\374".unpack("I*") => [16909060, 4294901244]
[16909060, 4294901244].pack("I*") => "\001\002\003\004\377\376\375\374"
[16909060, -66052].pack("I*") => "\001\002\003\004\377\376\375\374"
*
l
long (32bit 有符号整数, 取决于Endian) (l! 并非32bit, 它取决于long的大小)
小Endian, 32bit long:
"\001\002\003\004\377\376\375\374".unpack("l*") => [67305985, -50462977]
[67305985, 4244504319].pack("l*") => RangeError
[67305985, -50462977].pack("l*") => "\001\002\003\004\377\376\375\374"
*
L
unsigned long (32bit 无符号整数, 取决于Endian) (L! 并非32bit, 它取决于long的大小)
小Endian, 32bit long:
"\001\002\003\004\377\376\375\374".unpack("L*") => [67305985, 4244504319]
[67305985, 4244504319].pack("L*") => "\001\002\003\004\377\376\375\374"
[67305985, -50462977].pack("L*") => "\001\002\003\004\377\376\375\374"
*
q
ruby 1.7 特性: long long (有符号整数, 取决于Endian和long long 的大小) (在C中无法处理long long时, 就是64bit)
小Endian, 64bit long long:
"\001\002\003\004\005\006\007\010\377\376\375\374\373\372\371\370".unpack("q*")
=> [578437695752307201, -506097522914230529]
[578437695752307201, -506097522914230529].pack("q*")
=> "\001\002\003\004\005\006\a\010\377\376\375\374\373\372\371\370"
[578437695752307201, 17940646550795321087].pack("q*")
=> "\001\002\003\004\005\006\a\010\377\376\375\374\373\372\371\370"
*
Q
ruby 1.7 特性: unsigned long long (无符号整数, 取决于Endian和 long long 的大小) (在C中无法处理long long时, 就是64bit)
小Endian, 64bit long long:
"\001\002\003\004\005\006\007\010\377\376\375\374\373\372\371\370".unpack("Q*")
=> [578437695752307201, 17940646550795321087]
[578437695752307201, 17940646550795321087].pack("Q*")
=> "\001\002\003\004\005\006\a\010\377\376\375\374\373\372\371\370"
[578437695752307201, -506097522914230529].pack("Q*")
=> "\001\002\003\004\005\006\a\010\377\376\375\374\373\372\371\370"
*
m
被base64编码过的字符串。每隔60个八位组(或在结尾)添加一个换行代码。
Base64是一种编码方法, 它只使用ASCII码中的65个字符(包括[A-Za-z0-9+/]这64字符和用来padding的'='),将3个八位组(8bits * 3 = 24bits)中的二进制代码转为4个(6bits * 4 = 24bits)可印刷的字符。具体细节请参考RFC2045。
[""].pack("m") => ""
["\0"].pack("m") => "AA==\n"
["\0\0"].pack("m") => "AAA=\n"
["\0\0\0"].pack("m") => "AAAA\n"
["\377"].pack("m") => "/w==\n"
["\377\377"].pack("m") => "//8=\n"
["\377\377\377"].pack("m") => "////\n"
["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"].pack("m")
=> "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJT\nVFVWV1hZWg==\n"
["abcdefghijklmnopqrstuvwxyz"].pack("m3")
=> "YWJj\nZGVm\nZ2hp\namts\nbW5v\ncHFy\nc3R1\ndnd4\neXo=\n"
"".unpack("m") => [""]
"AA==\n".unpack("m") => ["\000"]
"AA==".unpack("m") => ["\000"]
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJT\nVFVWV1hZWg==\n".unpack("m")
=> ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"]
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg==\n".unpack("m")
=> ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"]
*
M
经过quoted-printable encoding编码的字符串
["a b c\td \ne"].pack("M") => "a b c\td =\n\ne=\n"
"a b c\td =\n\ne=\n".unpack("M") => ["a b c\td \ne"]
*
n
网络字节顺序(大Endian)的unsigned short (16bit 无符号整数)
[0,1,-1,32767,-32768,65535].pack("n*")
=> "\000\000\000\001\377\377\177\377\200\000\377\377"
"\000\000\000\001\377\377\177\377\200\000\377\377".unpack("n*")
=> [0, 1, 65535, 32767, 32768, 65535]
*
N
网络字节顺序(大Endian)的unsigned long (32bit 无符号整数)
[0,1,-1].pack("N*") => "\000\000\000\000\000\000\000\001\377\377\377\377"
"\000\000\000\000\000\000\000\001\377\377\377\377".unpack("N*") => [0, 1, 4294967295]
*
v
"VAX"字节顺序(小Endian)的unsigned short (16bit 无符号整数)
[0,1,-1,32767,-32768,65535].pack("v*")
=> "\000\000\001\000\377\377\377\177\000\200\377\377"
"\000\000\001\000\377\377\377\177\000\200\377\377".unpack("v*")
=> [0, 1, 65535, 32767, 32768, 65535]
*
V
"VAX"字节顺序(小Endian)的unsigned long (32bit 无符号整数)
[0,1,-1].pack("V*") => "\000\000\000\000\001\000\000\000\377\377\377\377"
"\000\000\000\000\001\000\000\000\377\377\377\377".unpack("V*") => [0, 1, 4294967295]
*
f
单精度浮点数(取决于系统)
IA-32 (x86) (IEEE754 单精度 小Endian):
[1.0].pack("f") => "\000\000\200?"
sparc (IEEE754 单精度 大Endian):
[1.0].pack("f") => "?\200\000\000"
*
d
双精度浮点数(取决于系统)
IA-32 (IEEE754 双精度 小Endian):
[1.0].pack("d") => "\000\000\000\000\000\000\360?"
sparc (IEEE754 双精度 大Endian):
[1.0].pack("d") => "?\360\000\000\000\000\000\000"
*
e
小Endian的单精度浮点数(取决于系统)
IA-32:
[1.0].pack("e") => "\000\000\200?"
sparc:
[1.0].pack("e") => "\000\000\200?"
*
E
小Endian的双精度浮点数(取决于系统)
IA-32:
[1.0].pack("E") => "\000\000\000\000\000\000\360?"
sparc:
[1.0].pack("E") => "\000\000\000\000\000\000\360?"
*
g
大Endian的单精度浮点数(取决于系统)
IA-32:
[1.0].pack("g") => "?\200\000\000"
sparc:
[1.0].pack("g") => "?\200\000\000"
*
G
大Endian的双精度浮点数(取决于系统)
IA-32:
[1.0].pack("G") => "?\360\000\000\000\000\000\000"
sparc:
[1.0].pack("G") => "?\360\000\000\000\000\000\000"
*
p
指向null终点字符串的指针
[""].pack("p") => "\310\037\034\010"
["a", "b", "c"].pack("p3") => " =\030\010\340^\030\010\360^\030\010"
[nil].pack("p") => "\000\000\000\000"
*
P
指向结构体(定长字符串)的指针
[nil].pack("P") => "\000\000\000\000"
["abc"].pack("P3") => "x*\024\010"
["abc"].pack("P4") => ArgumentError: too short buffer for P(3 for 4)
[""].pack("P") => ArgumentError: too short buffer for P(0 for 1)
*
u
被uuencode编码的字符串
[""].pack("u") => ""
["a"].pack("u") => "!80``\n"
["abc"].pack("u") => "#86)C\n"
["abcd"].pack("u") => "$86)C9```\n"
["a"*45].pack("u") => "M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A\n"
["a"*46].pack("u") => "M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A\n!80``\n"
["abcdefghi"].pack("u6") => "&86)C9&5F\n#9VAI\n"
*
U
utf-8
[0].pack("U") => "\000"
[1].pack("U") => "\001"
[0x7f].pack("U") => "\177"
[0x80].pack("U") => "\302\200"
[0x7fffffff].pack("U") => "\375\277\277\277\277\277"
[0x80000000].pack("U") => ArgumentError
[0,256,65536].pack("U3") => "\000\304\200\360\220\200\200"
"\000\304\200\360\220\200\200".unpack("U3") => [0, 256, 65536]
"\000\304\200\360\220\200\200".unpack("U") => [0]
"\000\304\200\360\220\200\200".unpack("U*") => [0, 256, 65536]
*
w
BER压缩整数
用7位来表现1字节, 这样就能以最少的字节数来表现任意大小的0以上的整数。各字节的最高位中除了数据的末尾以外,肯定还有个1(也就是说, 最高位可以表示数据伸展到的位置)。
BER是Basic Encoding Rules的缩略语(BER并非只能处理整数。ASN.1的编码中也用到了它)
*
x
读入null字节/1字节
*
X
后退1字节
*
@
向绝对位置移动
用例
下面是一些pack/unpack的用例。
其实有的问题并不需要使用pack, 但我们还是给了出它的例子。主要是因为pack很容易进行加密, 我们想向不愿使用pack的人提供一点新思路。
*
将数值(字符代码)的数组变为字符串的例子
p [82, 117, 98, 121].pack("cccc")
=> "Ruby"
p [82, 117, 98, 121].pack("c4")
=> "Ruby"
p [82, 117, 98, 121].pack("c*")
=> "Ruby"
s = ""
[82, 117, 98, 121].each {|c| s << c}
p s
=> "Ruby"
p [82, 117, 98, 121].collect {|c| sprintf "%c", c}.join
=> "Ruby"
p [82, 117, 98, 121].inject("") {|s, c| s << c}
=> "Ruby"
*
将字符串变为数值(字符代码)的数组的例子
p "Ruby".unpack('C*')
=> [82, 117, 98, 121]
a = []
"Ruby".each_byte {|c| a << c}
p a
=> [82, 117, 98, 121]
*
可以用"x"来处理null字节
p [82, 117, 98, 121].pack("ccxxcc")
=> "Ru\000\000by"
*
可以用"x"来读取字符
p "Ru\0\0by".unpack('ccxxcc')
=> [82, 117, 98, 121]
*
将Hex dump变为数值数组的例子
p "61 62 63 64 65 66".delete(' ').to_a.pack('H*').unpack('C*')
=> [97, 98, 99, 100, 101, 102]
p "61 62 63 64 65 66".split.collect {|c| c.hex}
=> [97, 98, 99, 100, 101, 102]
*
在二进制和16进制数的pack中, 指定的长度并不是指生成的字节数, 而是指位或半字节的个数
p [0b01010010, 0b01110101, 0b01100010, 0b01111001].pack("C4")
=> "Ruby"
p ["01010010011101010110001001111001"].pack("B32") # 8 bits * 4
=> "Ruby"
p [0x52, 0x75, 0x62, 0x79].pack("C4")
=> "Ruby"
p ["52756279"].pack("H8") # 2 nybbles * 4
=> "Ruby"
*
模板字符'a'的长度指定 只适用于一个字符串
p ["RUBY", "u", "b", "y"].pack("a4")
=> "RUBY"
p ["RUBY", "u", "b", "y"].pack("aaaa")
=> "Ruby"
p ["RUBY", "u", "b", "y"].pack("a*aaa")
=> "RUBYuby"
*
在模板字符"a"中, 若长度不够时, 就用null字符进行填充
p ["Ruby"].pack("a8")
=> "Ruby\000\000\000\000"
*
小Endian和大Endian
p [1,2].pack("s2")
=> "\000\001\000\002" # 在大Endian的系统中的输出
=> "\001\000\002\000" # 在小Endian的系统中的输出
p [1,2].pack("n2")
=> "\000\001\000\002" # 系统无关的大Endian
p [1,2].pack("v2")
=> "\001\000\002\000" # 系统无关的小Endian
*
网络字节顺序的 signed long
s = "\xff\xff\xff\xfe"
n = s.unpack("N")[0]
if n[31] == 1
n = -((n ^ 0xffff_ffff) + 1)
end
p n
=> -2
*
网络字节顺序的 signed long(第2个)
s = "\xff\xff\xff\xfe"
p n = s.unpack("N").pack("l").unpack("l")[0]
=> -2
*
IP地址
require 'socket'
p Socket.gethostbyname("localhost")[3].unpack("C4").join(".")
=> "127.0.0.1"
p "127.0.0.1".split(".").collect {|c| c.to_i}.pack("C4")
=> "\177\000\000\001"
*
sockaddr_in 结构体
require 'socket'
p [Socket::AF_INET,
Socket.getservbyname('echo'),
127, 0, 0, 1].pack("s n C4 x8")
=> "\002\000\000\a\177\000\000\001\000\000\000\000\000\000\000\000"
ruby 1.7 特性: 除了pack/unpack以外, 您还可以使用Socket.pack_sockaddr_in 和 Socket.unpack_sockaddr_in方法。
*
'\0'终点字符串的地址
模板字符 "p" 和 "P"是为了处理C语言层的接口而存在的(例如ioctl)。
p ["foo"].pack("p")
=> "8\266\021\010"
结果字符串看起来乱七八糟, 实际上它表示的是字符串"foo\0"的地址(二进制形式)。您可以像下面这样,把它变成您熟悉的形式
printf "%#010x\n", "8\266\021\010".unpack("L")[0]
=> 0x0811b638
在pack的结果被GC回收之前, 地址所指的对象(在本例中是"foo\0")保证不会被GC所回收.
您只能使用pack的结果来unpack("p")和unpack("P")。
p ["foo"].pack("p").unpack("p")
=> ["foo"]
p "8\266\021\010".unpack("p")
=> -:1:in `unpack': no associated pointer (ArgumentError)
from -:1
ruby 1.7 特性: "p"和"P"被解释为NULL指针, 它负责对nil进行特殊的处理。(下面是在普通的32bit机器上的结果)
p [nil].pack("p") #=> "\000\000\000\000"
p "\0\0\0\0".unpack("p") #=> [nil]
*
结构体的地址
例如, 表示
struct {
int a;
short b;
long c;
} v = {1,2,3};
的字符串是
v = [1,2,3].pack("i!s!l!")
(考虑到byte alignment的问题, 可能需要进行适当的padding才行)
您可以使用
p [v].pack("P")
=> "\300\265\021\010"
来获得指向该结构体的地址。
sprintf格式
Ruby的sprintf格式与C语言的sprintf(3)基本相同。但还是有些差别: 它没有针对C特有类型的修饰符,如short或long等; 它包含2进制数的指示符(%b); 它不支持sprintf的方言式的语法。
下面就对ruby的sprintf格式进行详细的说明。
sprintf格式的规格如下所示。[]中的部分是可选的。
%[指定参数$][标识符][宽度][.精度]指示符
若想输出`%'本身时, 请这样`%%'处理。
下面就分别介绍一下各元素的用法。
标识符
标识符包括`#', `+', ` '(空格), `-'和`0'这5个。
#
使用2进制、8进制、16进制的指示符(`b', `o', `x', `X')时, 会分别添加"0b", "0", "0x", "0X"前缀。
p sprintf("%#b", 10) # => "0b1010"
p sprintf("%#o", 10) # => "012"
p sprintf("%#x", 10) # => "0xa"
p sprintf("%#X", 10) # => "0XA"
对于浮点数 (`f', `e', `E', `g', `G'), 则必定在输出中添加"."。
p sprintf("%.0f", 10) # => "10"
p sprintf("%#.0f", 10) # => "10."
p sprintf("%.0e", 10) # => "1e+01"
p sprintf("%#.0e", 10) # => "1.e+01"
`g', `G'除了具有上述特性外, 还会在末尾添加多余的0。
p sprintf("%.05g", 10) # => "10"
p sprintf("%#.05g", 10) # => "10.000"
+
使输出字符串带上符号。如果是正数的话, 就会添加`+'。它只对数值指示符(`d', `i', `b', `o', `x', `X', `u', `f', `e', `E', `g', `G')起作用。另外, 如果是`b', `o', `x', `X', `u'的话, 则会为负数添加`-'。
p sprintf("%d", 1) # => "1"
p sprintf("%+d", 1) # => "+1"
p sprintf("%x", -1) # => "..f" # ".." 表示f无限延续
p sprintf("%+x", -1) # => "-1"
' '(空格)
与`+'相同, 用空格来代替正号`+'。它只对数值指示符(`d', `i', `b', `o', `x', `X', `u', `f', `e', `E', `g', `G')起作用。
p sprintf("%d", 1) # => "1"
p sprintf("%+d", 1) # => "+1"
p sprintf("% d", 1) # => " 1"
p sprintf("%x", -1) # => "..f"
p sprintf("% x", 1) # => " 1"
p sprintf("% x", -1) # => "-1"
-
使输出内容靠左. 若尚未指定宽度的话,则不起作用。
0
当输出内容靠右时, 使用`0'而并非空格来填充多余部分。
它只对数值指示符(`d', `i', `b', `o', `x', `X', `u', `f', `g', `G')起作用(对`e', `E'无效)
p sprintf("%010d", 10)
# => "0000000010"
与`#'一起使用时, 输出情况如下。
p sprintf("%#010x", 10) # => "0x0000000a"
p sprintf("%#010o", 10) # => "0000000012"
p sprintf("%#010b", 10) # => "0b00001010"
它等同于下例。
p sprintf("%#10.8x", 10) # => "0x0000000a"
p sprintf("%#10.9o", 10) # => "0000000012"
p sprintf("%#10.8b", 10) # => "0b00001010"
通常情况下, 会输出如下内容。
p sprintf("%#10x", 10) # => " 0xa"
p sprintf("%#10o", 10) # => " 012"
p sprintf("%#10b", 10) # => " 0b1010"
宽度
以非0数字开头的数串负责指定宽度。宽度是指生成字符串的宽度, 它不受后文中的精度的限制。
确定宽度时, 也会考虑标识符中附加的" ", "+","-", "0b", "0", "0x", "0X"的长度。
p sprintf("%#05x", 10) # => "0x00a"
宽度是指"必要的最小宽度". 若结果字符串的宽度超过指定宽度时, 指定宽度就会失效。
若将宽度指定为`*'时, 将从参数中取得宽度值。
p sprintf("%10s", "foo") # => " foo"
p sprintf("%*s", 10, "foo") # => " foo"
精度
紧跟在"."后面的数串表示精度(若只有"."的话,则为".0")。若遇到整数的指示符(`d', `i', `b', `o', `x', `X', `u')的话,精度表示数值部分的长度。
p sprintf("%10.5d", 1) # => " 00001"
p sprintf("%#10.5x", 1) # => " 0x00001"
p sprintf("%+10.5x", 1) # => " +00001"
若遇到浮点数的指示符(`f')的话,它表示小数部分的位数。
p sprintf("%10.5f", 1) # => " 1.00000"
p sprintf("%10.5f", 10) # => " 10.00000"
若遇到浮点数的指示符(`e', `E', `g', `G')的话,它表示有效位数。
p sprintf("%10.5e", 1) # => "1.00000e+00"
p sprintf("%10.5e", 10) # => "1.00000e+01"
p sprintf("%10.5g", 10) # => " 10"
p sprintf("%#10.5G", 10) # => " 10.000"
如果是字符串指示符(`s', `p')的话,将会按照精度的规定来检查参数中的字符串长度,并切除多余部分。若将宽度和精度设为同值的话,则只输出参数字符串中的符合精度规定的部分。
p sprintf("%10.2s", "foo") # => " fo"
p sprintf("%5.5s", "foo") # => # => " foo"
p sprintf("%5.5s", "foobar") # => # => "fooba"
若将精度设为`*'的话,将从参数中提取精度的值。
p sprintf("%.5s", "foobar") # => "fooba"
p sprintf("%.*s", 5, "foobar") # => "fooba"
指示符
指示符指出参数的类型,且是必选的。大体说来它包括:
* 表示字符串的指示符: `c', `s', `p'
* 表示整数的指示符: `d', `i', `u', `b', `o', `x', `X',
* 表示浮点数的指示符: `f', `g', `e', `E', `G'
这几类。
c
将参数的数值(0×255)看作是字符代码,并输出对应的字符。若参数并非数值、String、 nil, true或false的话,将尝试用to_int方法进行变换。
此时,只有标识符`-'和"宽度"的设定是有效的。
s
输出字符串。
若参数并非String对象的话,将使用to_s方法对其进行变换。
p
ruby 1.8 特性: 输出Object#inspect的结果。
p sprintf("%s", [1, 2, 3]) # => "123"
p sprintf("%p", [1, 2, 3]) # => "[1, 2, 3]"
d
i
以10进制整数的形式输出参数中的数值。
若参数并非整数,则使用与Integer函数相同的规则将其变为整数。
u
将参数的数值看作是无符号整数,并以10进制整数的形式输出它。
p sprintf("%u", -1) # => "..4294967295"
上面的代码会输出 p ".." + 0xffff_ffff.to_s。
ruby 1.7 特性: 在version 1.7中,不会附加".."。若是'%u'的话,则将参数看作是定长整数。此时,对于负整数n来说
printf("%u", n)
与
printf("%d", n & ~(-1 << n.size*8))
是一个意思。
b
o
x
X
分别以2进制、8进制、16进制、16进制(大写字母)字符串的形式输出整数。
若使用了`#' 标识符的话,则分别在前面添加"0b", "0", "0x", "0X"。
若没有使用`+', ` ' 标识符时,将在负数的前面(若有`#' 标识符,则在"0x"等的后面)添加".."。这表示最高位字符无限延伸,它采用了2的补数形式来表现负数。
p sprintf("%#b", 10) # => "0b1010"
p sprintf("%#o", 10) # => "012"
p sprintf("%#x", 10) # => "0xa"
# 对负数添加".."
p sprintf("%#b", -1) # => "0b..1"
p sprintf("%#o", -1) # => "0..7"
p sprintf("%#x", -1) # => "0x..f"
p sprintf("%10x", -1) # => " ..f"
p sprintf("%-10x", -1) # => "..f "
# 若指定了"精度"的话,则不会添加".."
p sprintf("%.10x", -1) # => "ffffffffff"
f
e
E
g
G
`f' 以小数点形式(xxx.xxx)输出数值。
`e' 以指数形式(x.xxxe+xx)输出数值。
`g' 的情况比较特殊。当指数小于-4或者超出精度范围时,它采用`e'方式进行输出。除此之外,它采用`f'方式进行输出。另外,它会删除小数部分尾部的0。
大写字母指示符(`E', `G')会将输出中的字母变为大写形式。
p sprintf("%f", 1.0) # => "1.000000"
p sprintf("%e", 1.0) # => "1.000000e+00"
p sprintf("%g", 1.0) # => "1"
p sprintf("%f", 10.1) # => "10.100000"
p sprintf("%e", 10.1) # => "1.010000e+01"
p sprintf("%g", 10.1) # => "10.1"
p sprintf("%g", 10 ** 6) # => "1e+06"
p sprintf("%g", 10 ** -5) # => "1e-05"
精度的缺省值为6。
若遇到无限大值或NaN(Not a Number)时,输出情况如下。
p sprintf("%f", 1.0/0) # => "inf"
p sprintf("%f", -1.0/0) # => "-inf"
p sprintf("%f", 0.0/0) # => "nan"
p sprintf("%E", 1.0/0) # => "INF"
p sprintf("%E", -1.0/0) # => "-INF"
p sprintf("%E", 0.0/0) # => "NAN"
指定参数
这部分的利用频率最低,所以放在最后。
nth$
表示将使用第nth个参数进行格式化操作。
p sprintf("%1$d, %1$x, %1$o", 10)
=> "10, a, 12"
p sprintf("%3$d, %2$x, %1$o", 1, 2, 3)
=> "3, 2, 1"
若您不想改变参数的顺序而只想改变格式的话,也可以使用它。
case ENV['LC_TIME']
when /^ja_JP/
fmt = "%1$d年%2$d月%3$d日"
else
fmt = "%2$02d/%03$2d/%1$02d"
end
p sprintf(fmt, 1, 4, 22)
=> "04/22/01"
您也可以先插入"*",然后借用参数来设定"宽度"和"精度"的值。
p sprintf("%5.2f", 1); # => " 1.00"
p sprintf("%*.*f", 5, 2, 1); # => " 1.00"
p sprintf("%1$*2$.*3$f", 1, 5, 2); # => " 1.00"
Marshal格式
2002-04-04 草稿....
*
以4.8(对应于1.8)版的格式为蓝本
# 截至2003-05-02为止的格式版本如下所示
p Marshal.Dump(Object.new).unpack("cc").join(".")
=> ruby 1.6.0 (2000-09-19) [i586-linux]
"4.4"
=> ruby 1.6.1 (2000-09-27) [i586-linux]
"4.4"
=> ruby 1.6.2 (2000-12-25) [i586-linux]
"4.5"
=> ruby 1.6.3 (2001-03-19) [i586-linux]
"4.5"
=> ruby 1.6.4 (2001-06-04) [i586-linux]
"4.5"
=> ruby 1.6.5 (2001-09-19) [i586-linux]
"4.6"
=> ruby 1.6.6 (2001-12-26) [i586-linux]
"4.6"
=> ruby 1.6.7 (2002-03-01) [i586-linux]
"4.6"
=> ruby 1.6.7 (2002-09-06) [i586-linux]
"4.6"
=> ruby 1.7.3 (2002-09-06) [i586-linux]
"4.7"
=> ruby 1.7.3 (2002-09-20) [i586-linux]
"4.8"
=> ruby 1.8.0 (2003-08-03) [i586-linux]
"4.8"
* 本文兼顾了以前的版本,同时也指出了兼容性问题
* 还提到了Ruby的Marshal中的BUG(?)
nil
true
false
分别是'0', 'T', 'F'
p Marshal.Dump(nil).unpack("x2 a*")
# => ["0"]
此时,即使设置了实例变量也无法Dump。
class NilClass
attr_accessor :foo
end
nil.foo = 1
p nil.foo # => 1
p Marshal.Dump(nil).unpack("x2 a*") # => ["0"]
Fixnum
在'i'之后是表示Fixnum的数据结构。
以数值n为例,在表示数值部分的形式中(不仅限于Fixnum,在其它地方也是如此),保存着
形式 1:
n == 0: 0
0 < n < 123: n + 5
-124 < n < 0: n - 5
这样的数值(1 byte)。之所以加减5,是为了有别于下面的形式。
例:
p Marshal.Dump(-1).unpack("x2 a*") # => "i\372"
p Marshal.Dump(0).unpack("x2 a*") # => "i\000"
p Marshal.Dump(1).unpack("x2 a*") # => "i\006"
p Marshal.Dump(2).unpack("x2 a*") # => "i\a" ("i\007")
若数值N超出形式1的范围时,则有下面的形式。
形式 2:
| len | n1 | n2 | n3 | n4 |
<-1-> <- len ->
byte bytes
len的值是-4 ~ -1, 1 ~ 4。这表示符号和后续的数据存在于n1 ~ n|len|。
# 举个更好的例子...
def foo(len, n1, n2 = 0, n3 = 0, n4 = 0)
case len
when -3; n4 = 255
when -2; n3 = n4 = 255
when -1; n2 = n3 = n4 = 255
end
n = (0xffffff00 | n1) &
(0xffff00ff | n2 * 0x100) &
(0xff00ffff | n3 * 0x10000) &
(0x00ffffff | n4 * 0x1000000)
# p "%x" % n
n = -((n ^ 0xffff_ffff) + 1) if len < 0
n
end
p Marshal.Dump(-125).unpack("x2 acC*") # => ["i", -1, 131]
p foo(-1, 131)
p Marshal.Dump(-255).unpack("x2 acC*") # => ["i", -1, 1]
p foo(-1, 1)
p Marshal.Dump(-256).unpack("x2 acC*") # => ["i", -1, 0]
p foo(-1, 0)
p Marshal.Dump(-257).unpack("x2 acC*") # => ["i", -2, 255, 254]
p foo(-2, 255, 254)
p Marshal.Dump(124).unpack("x2 acC*") # => ["i", 1, 124]
p foo(1, 124)
p Marshal.Dump(256).unpack("x2 acC*") # => ["i", 2, 0, 1]
p foo(2, 0, 1)
即使设定了实例变量,也无法Dump。
class Fixnum
attr_accessor :foo
end
99.foo = 1
p 99.foo # => 1
p 999.foo # => nil
p Marshal.Dump(99).unpack("x2 ac") # => ["i", 104]
instance of the user class
'C': String, Regexp, Array, Hash 的子类的实例变量
| 'C' | 类名(Symbol)的 Dump | 父类的实例的 Dump |
例 1:
class Foo < String # (or Regexp, Array, Hash)
end
p Marshal.Dump(Foo.new("foo")).unpack("x2 a a c a3 aca*")
# => ["C", ":", 8, "Foo", "\"", 8, "foo"]
^^^ (or '/', '[', '{')
例 2: 有实例变量(请参考instance variable)
class Foo < String # (or Regexp, Array, Hash)
def initialize(obj)
@foo = obj
super(obj)
end
end
p Marshal.Dump(Foo.new("foo")).unpack("x2 a a a c a3 aca3 caca4 aca*")
# => ["I", "C", ":", 8, "Foo", "\"", 8, "foo", 6, ":", 9, "@foo", "\"", 8, "foo"]
除此以外,将变为'o'。这是因为内部结构有所差异所致(请参考Object)
例:
class Foo
end
p Marshal.Dump(Foo.new).unpack("x2 a a c a*")
# => ["o", ":", 8, "Foo\000"]
'u'
若定义了_Dump、_load的话,就是'u'。因为无法Dump实例变量,所以必须使用_Dump/_load进行处理。
| 'u' | 类名(Symbol)的 Dump | _Dump 的结果的长度(Fixnum形式) |
| _Dump 的返回值 |
例:
class Foo
def self._load
end
def _Dump(obj)
"hogehoge"
end
end
p Marshal.Dump(Foo.new).unpack("x2 a aca3 c a*")
# => ["u", ":", 8, "Foo", 13, "hogehoge"]
'U' ruby 1.8 特性
若定义了marshal_Dump、marshal_load的话,就是'U'。因为无法Dump实例变量,所以必须使用marshal_Dump/marshal_load来处理。
| 'U' | 类名(Symbol)的 Dump | marshal_Dump 方法的返回值的 Dump |
例:
class Foo
def marshal_Dump
"hogehoge"
end
def marshal_load(obj)
end
end
p Marshal.Dump(Foo.new).unpack("x2 a aca3 a c a*")
# => ["U", ":", 8, "Foo", "\"", 13, "hogehoge"]
Object
'o'
| 'o' | 类名(Symbol)的 Dump | 实例变量的数量(Fixnum形式) |
| 实例变量名(Symbol) 的Dump(1) | 值(1) |
:
:
| 实例变量名(Symbol) 的Dump(n) | 值(n) |
例 1:
p Marshal.Dump(Object.new).unpack("x2 a a c a*")
# => ["o", ":", 11, "Object\000"]
例 2: 有实例变量
class Foo
def initialize
@foo = "foo"
@bar = "bar"
end
end
p Marshal.Dump(Foo.new).unpack("x2 a a c a3 c aca4 aca3 aca4 aca3")
# => ["o", ":", 8, "Foo", 7,
":", 9, "@bar", "\"", 8, "bar",
":", 9, "@foo", "\"", 8, "foo"]
Float
'f'
| 'f' | 数串的长度(Fixnum形式) | "%.16g" 的字符串 |
例:
p Marshal.Dump(Math::PI).unpack("x2 a c a*")
# => ["f", 22, "3.141592653589793"]
p Marshal.Dump(0.0/0).unpack("x2 a c a*") # => ["f", 8, "nan"]
p Marshal.Dump(1.0/0).unpack("x2 a c a*") # => ["f", 8, "inf"]
p Marshal.Dump(-1.0/0).unpack("x2 a c a*") # => ["f", 9, "-inf"]
p Marshal.Dump(-0.0).unpack("x2 a c a*") # => ["f", 9, "-0"]
Bignum
'l'
| 'l' | '+'/'-' | short的个数(Fixnum形式) | ... |
例:
p Marshal.Dump(2**32).unpack("x2 a a c a*")
# => ["l", "+", 8, "\000\000\000\000\001\000"]
# => ["l", "+", 8, "\000\000\001\000"] <- BUG: ruby version 1.6.3
String
'"'
| '"' | 长度(Fixnum形式) | 字符串 |
例:
p Marshal.Dump("hogehoge").unpack("x2 a c a*")
# => ["\"", 13, "hogehoge"]
Regexp
'/'
| '/' | 长度(Fixnum形式) | source字符串 | 选项 |
选项是 options的结果+汉字代码的flag值。
例:
p Marshal.Dump(/(hoge)*/).unpack("x2 a c a7 c")
# => ["/", 12, "(hoge)*", 0]
p Marshal.Dump(/hogehoge/m).unpack("x2 a c a8 c")
# => ["/", 13, "hogehoge", 4]
p Marshal.Dump(/hogehoge/e).unpack("x2 a c a8 c")
# => ["/", 13, "hogehoge", 32]
Array
'['
| '[' | 元素数(Fixnum形式) | 元素的 Dump | ... |
例:
p Marshal.Dump(["hogehoge", /hogehoge/]).unpack("x2 a c aca8 aca*")
# => ["[", 7, "\"", 13, "hogehoge", "/", 13, "hogehoge\000"]
Hash
'{'
| '{' | 元素数(Fixnum形式) | 键的 Dump | 值的 Dump | ... |
例:
p Marshal.Dump({"hogehoge", /hogehoge/}).unpack("x2 a c aca8 aca*")
# => ["{", 6, "\"", 13, "hogehoge", "/", 13, "hogehoge\000"]
Hash with default value ( not Proc )
'}'
| '}' | 元素数(Fixnum形式) | 键的 Dump | 值的 Dump | ... | 默认值 |
例:
h = Hash.new(true)
h["foo"] = "bar"
p Marshal.Dump(h).unpack("x2 a c aca3 aca*")
# => ["}", 6, "\"", 8, "foo", "\"", 8, "barT"]
若某Hash的默认对象是Proc的话,则无法Dump该Hash
h = Hash.new { }
Marshal.Dump(h)
=> -:2:in `Dump': cannot Dump hash with default proc (TypeError)
Struct
'S': 结构体类的实例的Dump
| 'S' | 类名(Symbol) 的 Dump | 成员数量(Fixnum形式) |
| 成员名(Symbol) 的 Dump | 值 | ... |
例:
Struct.new("XXX", :foo, :bar)
p Marshal.Dump(Struct::XXX.new).unpack("x2 a ac a11 c aca3a aca3a")
# => ["S", ":", 16, "Struct::XXX", 7,
":", 8, "foo", "0",
":", 8, "bar", "0"]
Class/Module (old format)
'M'
| 'M' | 长度(Fixnum形式) | 模块/类名 |
例: 因为已经无法dump这种形式,所以使用load进行说明。
class Mod
end
p Marshal.load([4,7, 'M', 3+5, 'Mod'].pack("ccaca*"))
# => Mod
Class/Module
'c', 'm'
| 'c'/'m' | 类名的长度(Fixnum 形式) | 类名 |
例:
class Foo
end
p Marshal.Dump(Foo).unpack("x2 a c a*") # => ["c", 8, "Foo"]
例 2: 无法dump类/模块的实例变量
module Bar
@bar = 1
end
p Bar.instance_eval { @bar }
Marshal.Dump(Bar, open("/tmp/foo", "w"))
# => 1
module Bar
end
p bar = Marshal.load(open("/tmp/foo"))
p bar.instance_eval { @bar }
# => nil
例 3: 无法dump类变量
module Baz
@@baz = 1
def self.baz
@@baz
end
end
p Baz.baz
Marshal.Dump(Baz, open("/tmp/foo", "w"))
# => 1
module Baz
def self.baz
@@baz
end
end
p baz = Marshal.load(open("/tmp/foo"))
baz.baz
# => Baz
-:3:in `baz': uninitialized class variable @@baz in Baz (NameError)
from -:7
Symbol
':'
| ':' | 符号名的长度(Fixnum形式) | 符号名 |
例:
p Marshal.Dump(:foo).unpack("x2 a c a*")
# => [":", 8, "foo"]
Symbol (link)
';'
| ';' | 表明Symbol实际状态的号码(Fixnum形式) |
在相应符号名已被dump/load时使用。该号码是内部管理的号码。(在dump/load时,会生成哈希表以便对Symbol进行管理。它表示记录位置)
例:
p Marshal.Dump([:foo, :foo]).unpack("x2 ac aca3 aC*")
# => ["[", 7, ":", 8, "foo", ";", 0]
p Marshal.Dump([:foo, :foo, :bar, :bar]).
unpack("x2 ac aca3 aC aca3 aC*")
# => ["[", 9, ":", 8, "foo", ";", 0, ":", 8, "bar", ";", 6]
instance variable
'I': Object, Class, Module 的实例以外的对象
| 'I' | 对象的 Dump | 实例变量的数量(Fixnum形式) |
| 实例变量名(Symbol) 的Dump(1) | 值(1) |
:
:
| 实例变量名(Symbol) 的Dump(n) | 值(n) |
因为Object的实例中包含实例变量,所以会采用其他的形式进行Dump(请参考Object)。该形式只针对Array 或 String 的实例。
例:
obj = String.new
obj.instance_eval { @foo = "bar" }
p Marshal.Dump(obj).unpack("x2 a ac c a c a4 aca*")
# => ["I", "\"", 0, 6, ":", 9, "@foo", "\"", 8, "bar"]
类或模块(Class/Module的实例)不会dump实例变量的信息。(请参考Class/Module)
link
'@'
| '@' | 表明对象实际状态的号码(Fixnum形式 |
在相应对象已被dump/load时使用。该号码是内部管理的号码。(在dump/load时,会生成哈希表以便对对象进行管理。它表示记录位置)
例:
obj = Object.new
p Marshal.Dump([obj, obj]).unpack("x2 ac aaca6c aca*")
# => ["[", 7, "o", ":", 11, "Object", 0, "@", 6, ""]
ary = []
ary.push ary
p Marshal.Dump(ary).unpack("x2 acac")
# => ["[", 6, "@", 0]
Marshal 的BUG
在ruby version 1.6中发现了下列BUG。括号()中列出的是正确的运作方式(1.7的运作方式)。
<= 1.6.7
* 类的clone中的实例是可以Dump的,但却不能加载(因为是无名类的对象,所以无法Dump)
* 当某对象通过include/extend无名Module而定义了特殊方法后,仍可以Dump/加载该对象(若某对象include了无名模块的话,则不能Dump该对象)
1.6.6, 1.6.7
* 拥有实例变量的Array和String是可以Dump的,但却不能加载(既能Dump,又能加载)
<= 1.6.5
* 类的clone中的实例是可以Dump的,但却不能正常加载。否则就会生成奇怪的对象(?)
* 特殊类被dump成为普通类了(特殊类是无法Dump的)
* 无名类是可以Dump的,但却不能加载(无名类是无法Dump的)
<= 1.6.4
* 模块可以Dump却不能加载(可以加载)
* 无名模块可以Dump却不能加载(无名模块是不能Dump的)
<= 1.6.3
* dump Float时,其保存精度偏低
<= 1.6.2
* dump时,无法保存正则表达式中/m, /x 选项的状态
1.6.2, 1.6.3
* 在1.6.2, 1.6.3中,Bignum可以Dump却不能加载。按理说其他版本也会有这个BUG,但因为没有测试脚本,所以无法证实。
<= 1.6.1
* dump时无法保存Range中的特定标识,该标识表明该范围中是否包含终点
下面就是测试脚本(请参考[RAA:RubyUnit])
# test for Marshal for ruby version 1.6
require 'rubyunit'
$version_dependent_behavior = true
# for test_userClass, test_userModule
module UserModule
def foo
end
end
class UserClass
def foo
end
end
class TestMarshal < RUNIT::TestCase
def assert_no_Dumpable(obj)
ex = assert_exception(TypeError) {
begin
# Marshal.Dump will cause TypeError or ArgumentError
Marshal.Dump obj
rescue ArgumentError
case $!.message
when /can't Dump anonymous/,
/cannot Dump hash with default proc/
raise TypeError
else
raise "unknown error"
end
end
}
end
def assert_Dumpable_but_not_equal(obj)
obj2 = Marshal.load(Marshal.Dump(obj))
assert(obj != obj2)
assert_equals(obj.type, obj2.type)
end
def assert_Dumpable_and_equal(obj)
obj2 = Marshal.load(Marshal.Dump(obj))
assert_equals(obj, obj2)
assert_equals(obj.type, obj2.type)
# check values of instance variable
ivars = obj.instance_variables
ivars2 = obj2.instance_variables
assert_equals(ivars, ivars2)
while ivars.size != 0
assert_equals(obj.instance_eval(ivars.shift),
obj2.instance_eval(ivars2.shift))
end
end
def test_Object
assert_Dumpable_but_not_equal Object.new
end
# object with singleton method
def test_Object_with_singleton_method
obj = Object.new
# On ruby version 1.6.0 - 1.6.2, cause parse error (nested method)
class <<obj
def foo
end
end
# object has singleton method can't be Dumped
assert_no_Dumpable obj
end
# object with singleton method (with named module)
def test_Object_with_singleton_method2
obj = Object.new
# On ruby version 1.6.0 - 1.6.2, cause parse error (nested method)
class <<obj
include UserModule
end
# On ruby version 1.6.0 - 1.6.7, no consider the singleton
# method with Mix-in.
# On ruby version 1.7, Dumpable object which is extended by
# named module.
assert_Dumpable_but_not_equal obj
end
# object with singleton method (with anonymous module)
def test_Object_with_singleton_method3
obj = Object.new
# On ruby version 1.6.0 - 1.6.2, cause parse error (nested method)
class <<obj
include Module.new
end
if $version_dependent_behavior and RUBY_VERSION <= "1.6.7"
# On ruby version 1.6.0 - 1.6.7, no consider the singleton method with Mix-in.
assert_Dumpable_but_not_equal obj
else
# object has singleton method (wi