原文: https://zetcode.com/lang/rubytutorial/io/

在 Ruby 教程的这一部分中,我们将讨论 Ruby 中的输入&输出操作。 输入是程序从键盘,文件或其他程序读取的任何数据。 输出是程序产生的数据。 输出可能会转到屏幕,文件或其他程序。

输入&输出是一个大话题。 我们提出一些示例,以使您对该主题有一个总体了解。 Ruby 中的几个类具有执行输入&输出操作的方法。 例如KernelIODirFile

Ruby 输出到控制台

Ruby 有几种方法可以在控制台上打印输出。 这些方法是Kernel模块的一部分。 Kernel的方法可用于 Ruby 中的所有对象。

  1. #!/usr/bin/ruby
  2. print "Apple "
  3. print "Apple\n"
  4. puts "Orange"
  5. puts "Orange"

printputs方法在控制台上产生文本输出。 两者之间的区别在于后者添加了新的换行符。

  1. print "Apple "
  2. print "Apple\n"

打印方法将两个连续的"Apple"字符串打印到终端。 如果要创建新行,则必须显式包含换行符。 换行符为\n。 在后台,print方法实际上调用了要打印对象的to_s方法。

  1. puts "Orange"
  2. puts "Orange"

puts方法将两个字符串打印到控制台。 每个人都有自己的路由。 该方法自动包括换行符。

  1. $ ./printing.rb
  2. Apple Apple
  3. Orange
  4. Orange

这是printing.rb脚本文件的输出。

根据 Ruby 文档,print方法与$stdout.print等效。 $stdout是保存标准输出流的全局变量。

  1. #!/usr/bin/ruby
  2. $stdout.print "Ruby language\n"
  3. $stdout.puts "Python language"

我们使用$stdout变量打印两行。

Ruby 还有另外三种打印输出的方法。

  1. #!/usr/bin/ruby
  2. p "Lemon"
  3. p "Lemon"
  4. printf "There are %d apples\n", 3
  5. putc 'K'
  6. putc 0xA

在示例中,我们介绍了pprintfputc方法。

  1. p "Lemon"

p在打印对象时调用inspect方法。 该方法对于调试很有用。

  1. printf "There are %d apples\n", 3

printf方法从 C 编程语言中是众所周知的。 它启用字符串格式。

  1. putc 'K'
  2. putc 0xA

putc方法将一个字符打印到控制台。 第二行打印换行符。 0xA是换行符的十六进制代码。

  1. $ ./printing3.rb
  2. "Lemon"
  3. "Lemon"
  4. There are 3 apples
  5. K

这是printing3.rb程序的输出。

使用内核方法将数据打印到控制台是一个快捷方式:一种打印数据的简便方法。 以下示例显示了一种将数据打印到终端的更正式的方法。

  1. ios = IO.new STDOUT.fileno
  2. ios.write "ZetCode\n"
  3. ios.close

在示例中,我们打开一个标准输出流,并将一个字符串写入其中。

  1. ios = IO.new STDOUT.fileno

new方法返回一个我们可以向其写入数据的流。 该方法采用数字文件描述符。 STDOUT.fileno为我们提供了标准输出流的文件描述符。 我们也可以简单地写 2。

  1. ios.write "ZetCode\n"

我们向打开的流中写入一个字符串。

  1. ios.close

输入流已关闭。

在 Unix 系统上,标准终端输出连接到名为/dev/tty的特殊文件。 通过打开并写入,我们将写入控制台。

  1. #!/usr/bin/ruby
  2. fd = IO.sysopen "/dev/tty", "w"
  3. ios = IO.new(fd, "w")
  4. ios.puts "ZetCode"
  5. ios.close

一个小的例子,我们写入/dev/tty文件。 这仅适用于 Unix。

  1. fd = IO.sysopen "/dev/tty", "w"

sysopen方法打开给定路径,并返回基础文件描述符号。

  1. ios = IO.new(fd, "w")

文件描述符号用于打开流。

  1. ios.puts "ZetCode"
  2. ios.close

我们向流中写入一个字符串并将其关闭。

Ruby 从控制台读取输入

在本节中,我们将创建一些代码示例,这些示例将处理从控制台读取的内容。

$stdin是一个全局变量,其中包含标准输入的流。 它可用于从控制台读取输入。

  1. #!/usr/bin/ruby
  2. inp = $stdin.read
  3. puts inp

在上面的代码中,我们使用read方法从控制台读取输入。

  1. inp = $stdin.read

read方法从标准输入读取数据,直到到达文件末尾。 通过在 Unix 上按Ctrl + D以及在 Windows 上按Ctrl + Z可以产生 EOF。

  1. $ ./reading.rb
  2. Ruby language
  3. Ruby language

当我们启动不带参数的程序时,脚本将从用户读取数据。 读取直到我们按 Ctrl + DCtrl + Z 为止。

  1. $ echo "ZetCode" | ./reading.rb
  2. ZetCode
  3. $ ./input.rb < stones
  4. Garnet
  5. Topaz
  6. Opal
  7. Amethyst
  8. Ruby
  9. Jasper
  10. Pyrite
  11. Malachite
  12. Quartz

如果我们进行一些重定向,脚本可以从另一个程序或文件中读取数据。

从控制台读取数据的常用方法是使用gets方法。

  1. #!/usr/bin/ruby
  2. print "Enter your name: "
  3. name = gets
  4. puts "Hello #{name}"

我们使用gets方法从用户读取一行。

  1. name = gets

gets方法从标准输入读取一行。 数据被分配给名称变量。

  1. puts "Hello #{name}"

我们已读取的数据将打印到控制台。 我们使用插值将变量包括在字符串中。

  1. $ ./readline.rb
  2. Enter your name: Jan
  3. Hello Jan

样本输出。

在以下两个脚本中,我们将讨论chomp方法。 这是一个字符串方法,可从字符串末尾删除空格。 在执行输入操作时很有用。 方法名称和用法来自 Perl 语言。

  1. #!/usr/bin/ruby
  2. print "Enter a string: "
  3. inp = gets
  4. puts "The string has #{inp.size} characters"

我们从用户那里读取一个字符串,然后计算输入字符串的长度。

  1. $ ./nochomp.rb
  2. Enter a string: Ruby
  3. The string has 5 characters

消息说该字符串有 5 个字符。 这是因为它也计入换行符。

为了获得正确的答案,我们需要删除换行符。 这是chomp方法的工作。

  1. #!/usr/bin/ruby
  2. print "Enter a string: "
  3. inp = gets.chomp
  4. puts "The string has #{inp.size} characters"

这次我们使用chomp方法剪切换行符。

  1. $ ./chomp.rb
  2. Enter a string: Ruby
  3. The string has 4 characters

Ruby 字符串确实有 4 个字符。

Ruby 文件

从正式的 Ruby 文档中,我们了解到IO类是 Ruby 中所有输入和输出的基础。 File类是IO类的唯一子类。 这两个类别紧密相关。

  1. #!/usr/bin/ruby
  2. f = File.open('output.txt', 'w')
  3. f.puts "The Ruby tutorial"
  4. f.close

在第一个示例中,我们打开一个文件并向其中写入一些数据。

  1. f = File.open('output.txt', 'w')

我们以写模式打开文件"output.txt"open方法返回一个 io 流。

  1. f.puts "The Ruby tutorial"

我们使用上面打开的流来写入一些数据。 puts方法也可以用于将数据写入文件。

  1. f.close

最后,流关闭。

  1. $ ./simplewrite.rb
  2. $ cat output.txt
  3. The Ruby tutorial

我们执行脚本并显示output.txt文件的内容。

我们将有一个类似的示例,该示例将展示其他有效的方法。

  1. #!/usr/bin/ruby
  2. File.open('langs', 'w') do |f|
  3. f.puts "Ruby"
  4. f.write "Java\n"
  5. f << "Python\n"
  6. end

如果open方法之后有一个块,则 Ruby 将打开的流传递到该块。 在该块的末尾,文件将自动关闭。

  1. f.puts "Ruby"
  2. f.write "Java\n"
  3. f << "Python\n"

我们使用三种不同的方法写入文件。

  1. $ ./simplewrite2.rb
  2. $ cat langs
  3. Ruby
  4. Java
  5. Python

我们执行脚本并检查langs文件的内容。

在第二个示例中,我们显示File类的一些方法。

  1. #!/usr/bin/ruby
  2. puts File.exists? 'tempfile'
  3. f = File.new 'tempfile', 'w'
  4. puts File.mtime 'tempfile'
  5. puts f.size
  6. File.rename 'tempfile', 'tempfile2'
  7. f.close

该示例创建一个名为tempfile的新文件,并调用一些方法。

  1. puts File.exists? 'tempfile'

exists?方法检查具有给定名称的文件是否已经存在。 该行返回false,因为我们尚未创建文件。

  1. f = File.new 'tempfile', 'w'

文件已创建。

  1. puts File.mtime 'tempfile'

mtime方法为我们提供了文件的最后修改时间。

  1. puts f.size

我们确定文件大小。 由于尚未写入文件,因此该方法返回 0。

  1. File.rename 'tempfile', 'tempfile2'

最后,我们使用rename方法重命名文件。

  1. $ ./testfile.rb
  2. false
  3. 2011-11-05 16:19:36 +0100
  4. 0

这是一个示例输出。

接下来,我们将从磁盘上的文件中读取数据。

  1. #!/usr/bin/ruby
  2. f = File.open("stones")
  3. while line = f.gets do
  4. puts line
  5. end
  6. f.close

这是一个简单的脚本,它将打开一个名为stone的文件,并将其内容逐行打印到终端。

  1. f = File.open("stones")

我们打开一个stones文件。 默认模式是读取模式。 stones文件包含九个有价值的宝石名称,每个名称都位于单独的行上。

  1. while line = f.gets do
  2. puts line
  3. end

gets方法从 I/O 流中读取一行。 当我们到达文件末尾时,while块结束。

  1. $ ./readlines2.rb
  2. Garnet
  3. Topaz
  4. Opal
  5. Amethyst
  6. Ruby
  7. Jasper
  8. Pyrite
  9. Malachite
  10. Quartz

这是示例的输出。

下一个示例将从文件中读取数据。

  1. #!/usr/bin/ruby
  2. fname = 'alllines.rb'
  3. File.readlines(fname).each do |line|
  4. puts line
  5. end

该脚本显示了读取文件内容的另一种方法。 该代码示例将在终端上打印其自己的代码。

  1. File.readlines(fname).each do |line|
  2. puts line
  3. end

readlines从指定文件读取所有行,并以数组形式返回它们。 我们使用each方法遍历数组并将行打印到终端。

  1. $ ./alllines.rb
  2. #!/usr/bin/ruby
  3. fname = 'alllines.rb'
  4. File.readlines(fname).each do |line|
  5. puts line
  6. end

示例的输出。

Ruby 目录

在本节中,我们使用目录。 我们有一个Dir类,可以在 Ruby 中使用目录。

  1. #!/usr/bin/ruby
  2. Dir.mkdir "tmp"
  3. puts Dir.exists? "tmp"
  4. puts Dir.pwd
  5. Dir.chdir "tmp"
  6. puts Dir.pwd
  7. Dir.chdir '..'
  8. puts Dir.pwd
  9. Dir.rmdir "tmp"
  10. puts Dir.exists? "tmp"

在脚本中,我们使用Dir类的四种方法。

  1. Dir.mkdir "tmp"

mkdir方法创建一个名为tmp的新目录。

  1. puts Dir.exists? "tmp"

使用exists?方法,我们检查文件系统中是否存在具有给定名称的目录。

  1. puts Dir.pwd

pwd方法打印当前工作目录。 这是我们启动脚本的目录。

  1. Dir.chdir '..'

chdir方法更改到另一个目录。 ..目录是当前工作目录的父目录。

  1. Dir.rmdir "tmp"
  2. puts Dir.exists? "tmp"

最后,我们使用rmdir方法删除目录。 这次exists?方法返回false

  1. $ ./dirs.rb
  2. true
  3. /home/vronskij/programming/ruby/io
  4. /home/vronskij/programming/ruby/io/tmp
  5. /home/vronskij/programming/ruby/io
  6. false

这是示例的输出。

在第二个示例中,我们检索目录的所有条目,包括其文件和子目录。

  1. #!/usr/bin/ruby
  2. fls = Dir.entries '.'
  3. puts fls.inspect

entries方法返回给定目录的所有条目。

  1. fls = Dir.entries '.'
  2. puts fls.inspect

我们得到当前目录的文件和目录数组。 .字符在此上下文中表示当前工作目录。 inspect方法为我们提供了更可读的数组表示形式。

  1. $ ./allfiles.rb
  2. ["putc.rb", "simplewrite.rb", "readlines2.rb", "fileexists.rb~" ...

在输出中,我们可以看到文件和目录的数组。

第三个示例使用主目录。 计算机中的每个用户都有一个分配给他的唯一目录。 它称为主目录。 在这里,他可以放置文件并创建自己的目录层次结构。

  1. #!/usr/bin/ruby
  2. puts Dir.home
  3. puts Dir.home 'root'

该脚本打印两个主目录。

  1. puts Dir.home

如果未指定用户名,则返回当前用户的主目录。 当前用户是脚本文件的所有者。 有人启动了脚本。

  1. puts Dir.home 'root'

在这里,我们打印特定用户的主目录:在本例中为超级用户。

  1. $ ./homedir.rb
  2. /home/vronskij
  3. /root

这是一个示例输出。

Ruby 执行外部程序

Ruby 有几种执行外部程序的方法。 我们将处理其中一些问题。 在我们的示例中,我们将使用众所周知的 Linux 命令。 Windows 或 Mac 的阅读器可以使用特定于其系统的命令。

  1. #!/usr/bin/ruby
  2. data = system 'ls'
  3. puts data

我们调用ls命令列出目录内容。

  1. data = system 'ls'

system命令在子 Shell 中执行外部程序。 该方法属于Kernel Ruby 模块。

  1. $ ./system.rb
  2. allfiles.rb characters.rb fileexists.rb homedir.rb~ ...

This is a sample output.

我们展示了另外两种在 Ruby 中运行外部程序的方式。

  1. #!/usr/bin/ruby
  2. out = `pwd`
  3. puts out
  4. out = %x[uptime]
  5. puts out
  6. out = %x[ls | grep 'readline']
  7. puts out

要运行外部程序,我们可以使用反引号``或%x[]`字符。

  1. out = `pwd`

在这里,我们使用反引号执行pwd命令。 该命令返回当前工作目录。

  1. out = %x[uptime]

在这里,我们获得uptime命令的输出,该命令指示系统运行了多长时间。

  1. out = %x[ls | grep 'readline']

我们也可以结合使用命令。

  1. $ ./system2.rb
  2. /home/vronskij/programming/ruby/io
  3. 22:50:50 up 5:32, 1 user, load average: 0.46, 0.44, 0.45
  4. readline.rb
  5. readline.rb~
  6. readlines2.rb
  7. readlines2.rb~

This is a sample output.

我们可以使用open方法执行命令。 该方法属于Kernel模块。 它创建一个连接到给定流,文件或子流程的 IO 对象。 如果要连接到子进程,请使用管道字符|来启动open的路径。

  1. #!/usr/bin/ruby
  2. f = open("|ls -l |head -3")
  3. out = f.read
  4. puts out
  5. f.close
  6. puts $?.success?

在示例中,我们打印ls -l | head -3命令的结果。 这两个命令的组合返回ls -l命令的前三行。 我们还检查子进程的状态。

  1. f = open("|ls -l |head -3")

我们连接到由这两个命令创建的子流程。

  1. out = f.read
  2. puts out

我们从子流程读取并打印数据。

  1. f.close

我们关闭文件处理器。

  1. puts $?.success?

$?是一个特殊的 Ruby 变量,它设置为最后执行的子进程的状态。 如果子进程以 OK 结束,则success?方法返回true

  1. $ ./system3.rb
  2. total 148
  3. -rwxr-xr-x 1 vronskij vronskij 57 2011-10-30 23:33 allfiles.rb
  4. -rwxr-xr-x 1 vronskij vronskij 58 2011-10-30 23:33 allfiles.rb~
  5. true

This is a sample output.

Ruby 重定向标准输出

Ruby 具有用于标准输入,标准输出和标准错误输出的预定义全局变量。 $stdout是标准输出的变量的名称。

  1. #!/usr/bin/ruby
  2. $stdout = File.open "output.log", "a"
  3. puts "Ruby"
  4. puts "Java"
  5. $stdout.close
  6. $stdout = STDOUT
  7. puts "Python"

在上面的示例中,我们将标准输出重定向到output.log文件。

  1. $stdout = File.open "output.log", "a"

此行创建一个新的标准输出。 现在,标准输出将流到ouput.log文件。 该文件以附加模式打开。 如果该文件尚不存在,则会创建它。 否则,它将被打开并将数据写入文件的末尾。

  1. puts "Ruby"
  2. puts "Java"

我们打印两个字符串。 字符串将不会像往常一样显示在终端中。 相反,它们将被附加到output.log文件中。

  1. $stdout.close

处理器已关闭。

  1. $stdout = STDOUT
  2. puts "Python"

我们使用预定义的标准常量STDOUT重新创建正常的标准输出。 "Python"字符串被打印到控制台。

在 Ruby 教程的这一部分中,我们使用 Ruby 进行输入和输出操作。