浏览 7241 次
锁定老帖子 主题:ruby参考手册
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2007-01-04
--按英文排序 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 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |