如果您希望报告此 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" }