论坛首页 入门技术论坛

ruby参考手册

浏览 7241 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2007-01-04  
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
论坛首页 入门技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics