ruby参考手册

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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值