如果您希望报告此 FAQ 中的错误或提出改进建议,请访问我们的 GitHub 仓库 并提交 issue 或 pull request。
内置库
instance_methods(false)
返回什么?
方法 instance_methods
返回一个数组,其中包含接收类或模块中实例方法的名称。 这将包括超类和混合模块中的方法。
instance_methods(false)
或 instance_methods(nil)
仅返回接收器中定义的方法的名称。
随机数种子如何工作?
如果调用 rand
时没有事先调用 srand
,Ruby 的伪随机数生成器将使用一个随机(ish)种子,该种子除其他外还会使用操作系统提供的熵源(如果可用)。不使用 srand
的程序连续运行将生成不同的随机数序列。
出于测试目的,您可以通过每次在程序运行时使用常量种子调用 srand
来获得可预测的行为,即每次都产生相同的数字序列。
我读取了一个文件并对其进行了更改,但磁盘上的文件没有更改。
File.open("example", "r+").readlines.each_with_index do |line, i|
line[0,0] = "#{i+1}: "
end
此程序不会将行号添加到文件 example
。它确实读取了文件的内容,并且对于读取的每一行,都会在行号前添加行号,但数据永远不会写回。下面的代码确实会更新文件(尽管有点危险,因为它在开始更新之前没有进行备份)
File.open("example", "r+") do |f|
lines = f.readlines
lines.each_with_index {|line, i| line[0,0] = "#{i+1}: " }
f.rewind
f.puts lines
end
如何处理文件并更新其内容?
使用命令行选项 -i
或内置变量 $-i
,您可以读取文件并替换它。
前面问题中向文件添加行号的代码最好使用此技术编写
$ ruby -i -ne 'print "#$.: #$_"' example
如果要保留原始文件,请使用 -i.bak
创建备份。
我写了一个文件,复制了它,但副本的末尾似乎丢失了。
此代码将无法正常工作
require "fileutils"
File.open("file", "w").puts "This is a file."
FileUtils.cp("file", "newfile")
由于 I/O 是缓冲的,因此在将其内容写入磁盘之前正在复制 file
。newfile
可能为空。但是,当程序终止时,缓冲区将被刷新,并且文件具有预期的内容。
如果您确保在复制之前关闭 file
,则不会出现此问题
require "fileutils"
File.open("file", "w") {|f| f.puts "This is a file." }
FileUtils.cp("file", "newfile")
如何获取当前输入文件中的行号?
当您从文件读取时,Ruby 会在全局变量 $.
中增加行号计数器。 这也可以使用 File
对象的 lineno
属性获得。
特殊常量 ARGF
是一个类似文件的对象,可用于读取在命令行上指定的所有输入文件(如果没有文件,则为标准输入)。 ARGF
由以下代码隐式使用
while gets
print $_
end
在这种情况下,$.
将是跨所有输入文件读取的累计行数。 要获取当前文件中的行号,请使用
ARGF.file.lineno
您还可以使用 ARGF.file.path
获取当前文件的名称。
如何使用 less
显示程序的输出?
我尝试了以下操作,但没有任何输出
open("|less", "w").puts "abc"
这是因为程序会立即结束,而 less
永远没有机会看到您写入它的内容,更不用说显示它了。确保 IO 正确关闭,它将等待直到 less
结束。
open("|less", "w") {|f| f.puts "abc" }
如果不再引用 File
对象会发生什么?
不再引用的 File
对象将有资格进行垃圾回收。当垃圾回收 File
对象时,文件将自动关闭。
如果不关闭文件,我会感到不安。
至少有四种确保关闭文件的好方法
# (1)
f = File.open("file")
begin
f.each {|line| print line }
ensure
f.close
end
# (2)
File.open("file") do |f|
f.each {|line| print line }
end
# (3)
File.foreach("file") {|line| print line }
# (4)
File.readlines("file").each {|line| print line }
如何按修改时间对文件进行排序?
Dir.glob("*").sort {|a, b| File.mtime(b) <=> File.mtime(a) }
虽然这可行(返回按时间倒序排列的列表),但效率不高,因为它在每次比较时都会从操作系统获取文件的修改时间。
可以通过一些额外的复杂性来提高效率
Dir.glob("*").map {|f| [File.mtime(f), f] }.
sort {|a, b| b[0] <=> a[0] }.map(&:last)
如何计算文件中单词的频率?
freq = Hash.new(0)
File.read("example").scan(/\w+/) {|word| freq[word] += 1 }
freq.keys.sort.each {|word| puts "#{word}: #{freq[word]}" }
产生
and: 1
is: 3
line: 3
one: 1
this: 3
three: 1
two: 1
如何按字母顺序对字符串进行排序?
如果您希望字符串按 “AAA”, “BBB”, …, “ZZZ”, “aaa”, “bbb” 排序,则内置比较将正常工作。
如果要排序时忽略大小写区别,请在排序块中比较字符串的小写版本
array = %w( z bB Bb bb Aa BB aA AA aa a A )
array.sort {|a, b| a.downcase <=> b.downcase }
# => ["a", "A", "Aa", "aA", "AA", "aa", "bB", "Bb", "bb", "BB", "z"]
如果您希望排序时使 “A” 和 “a” 放在一起,但 “a” 被认为大于 “A”(因此 “Aa” 在 “AA” 之后但在 “AB” 之前),请使用
array.sort {|a, b| (a.downcase <=> b.downcase).nonzero? || a <=> b }
# => ["A", "a", "AA", "Aa", "aA", "aa", "BB", "Bb", "bB", "bb", "z"]
如何将制表符展开为空格?
如果 a
保存要展开的字符串,则可以使用以下方法之一
1 while a.sub!(/(^[^\t]*)\t(\t*)/){$1+" "*(8-$1.size%8+8*$2.size)}
# or
1 while a.sub!(/\t(\t*)/){" "*(8-$~.begin(0)%8+8*$1.size)}
# or
a.gsub!(/([^\t]{8})|([^\t]*)\t/n){[$+].pack("A8")}
如何在正则表达式中转义反斜杠?
Regexp.quote('\\')
转义反斜杠。
如果您使用 sub
和 gsub
,则会变得更加棘手。假设您编写 gsub(/\\/, '\\\\')
,希望将每个反斜杠替换为两个。在语法分析中,第二个参数转换为 '\\'
。当发生替换时,正则表达式引擎将其转换为 '\'
,因此最终效果是将每个单反斜杠替换为另一个单反斜杠。您需要编写 gsub(/\\/, '\\\\\\')
!
但是,利用 \&amp;
包含匹配字符串的事实,您也可以编写 gsub(/\\/, '\&amp;\&amp;')
。
如果您使用 gsub
的块形式,即 gsub(/\\/) { '\\\\' }
,则替换字符串仅分析一次(在语法传递期间),结果是您想要的。
sub
和 sub!
之间有什么区别?
在 sub
中,会生成接收器的副本,进行替换并返回。
在 sub!
中,如果找到任何匹配项,则会更改并返回接收器。 否则,返回 nil
。
像 sub!
这样更改接收器属性的方法称为破坏性方法。通常,如果有两个类似的方法,并且一个是破坏性的,则破坏性方法会带有后缀 !
。
def foo(str)
str.sub(/foo/, "baz")
end
obj = "foo"
foo(obj) # => "baz"
obj # => "foo"
def foo(str)
str.sub!(/foo/, "baz")
end
foo(obj) # => "baz"
obj # => "baz"
\Z
在哪里匹配?
如果字符串以 \n
结尾,则 \Z
匹配最后一个 \n
(换行符)之前的位置,否则它匹配字符串的末尾。
thread
和 fork
之间有什么区别?
本节或其部分内容可能已过时或需要确认。
Ruby 线程在解释器内部实现,而 fork
调用操作系统来创建单独执行的子进程。
线程和 fork 具有以下特征
fork
很慢,thread
则不是。fork
不共享内存空间。thread
不会导致抖动。thread
可以在 DOS 上工作。- 当
thread
进入死锁时,整个进程都会停止。 fork
可以利用等待 I/O 完成的暂停,thread
则不能(至少没有一些帮助)。
您可能不应该混合使用 fork
和 thread
。
如何使用 Marshal
?
Marshal
用于将对象存储在文件或字符串中,并在以后重新构成它。可以使用以下方法存储对象
Marshal.dump( obj [, io ] [, lev] )
io
是一个可写的 IO
对象,lev
指定对象被取消引用和存储的级别。如果完成了 lev
级别的取消引用并且对象引用仍然存在,则 dump
仅存储引用,而不存储引用的对象。这是不好的,因为这些引用的对象无法随后重建。
如果省略 io
,则封送的对象将在字符串中返回。
您可以使用以下方法加载对象
obj = Marshal.load(io)
# or
obj = Marshal.load(str)
其中 io
是一个可读的 IO
对象,str
是转储的字符串。
如何使用 trap
?
trap
将代码块与外部事件(信号)相关联。
trap("PIPE") { raise "SIGPIPE" }