# Ruby 输入&输出

> 原文： [https://zetcode.com/lang/rubytutorial/io/](https://zetcode.com/lang/rubytutorial/io/)

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

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

## Ruby 输出到控制台

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

```ruby
#!/usr/bin/ruby

print "Apple "
print "Apple\n"

puts "Orange"
puts "Orange"

```

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

```ruby
print "Apple "
print "Apple\n"

```

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

```ruby
puts "Orange"
puts "Orange"

```

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

```ruby
$ ./printing.rb
Apple Apple
Orange
Orange

```

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

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

```ruby
#!/usr/bin/ruby

$stdout.print "Ruby language\n"
$stdout.puts "Python language"

```

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

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

```ruby
#!/usr/bin/ruby

p "Lemon"
p "Lemon"

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

putc 'K'
putc 0xA

```

在示例中，我们介绍了`p`，`printf`和`putc`方法。

```ruby
p "Lemon"

```

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

```ruby
printf "There are %d apples\n", 3

```

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

```ruby
putc 'K'
putc 0xA

```

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

```ruby
$ ./printing3.rb 
"Lemon"
"Lemon"
There are 3 apples
K

```

这是`printing3.rb`程序的输出。

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

```ruby
ios = IO.new STDOUT.fileno
ios.write "ZetCode\n"
ios.close

```

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

```ruby
ios = IO.new STDOUT.fileno

```

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

```ruby
ios.write "ZetCode\n"

```

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

```ruby
ios.close

```

输入流已关闭。

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

```ruby
#!/usr/bin/ruby

fd = IO.sysopen "/dev/tty", "w"
ios = IO.new(fd, "w")
ios.puts "ZetCode"
ios.close

```

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

```ruby
fd = IO.sysopen "/dev/tty", "w"

```

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

```ruby
ios = IO.new(fd, "w")

```

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

```ruby
ios.puts "ZetCode"
ios.close

```

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

## Ruby 从控制台读取输入

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

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

```ruby
#!/usr/bin/ruby

inp = $stdin.read
puts inp

```

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

```ruby
inp = $stdin.read

```

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

```ruby
$ ./reading.rb
Ruby language
Ruby language

```

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

```ruby
$ echo "ZetCode" | ./reading.rb
ZetCode

$ ./input.rb < stones 
Garnet
Topaz
Opal
Amethyst
Ruby
Jasper
Pyrite
Malachite
Quartz

```

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

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

```ruby
#!/usr/bin/ruby

print "Enter your name: "
name = gets
puts "Hello #{name}"

```

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

```ruby
name = gets

```

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

```ruby
puts "Hello #{name}"

```

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

```ruby
$ ./readline.rb
Enter your name: Jan
Hello Jan

```

样本输出。

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

```ruby
#!/usr/bin/ruby

print "Enter a string: "
inp = gets

puts "The string has #{inp.size} characters"

```

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

```ruby
$ ./nochomp.rb 
Enter a string: Ruby
The string has 5 characters

```

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

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

```ruby
#!/usr/bin/ruby

print "Enter a string: "
inp = gets.chomp

puts "The string has #{inp.size} characters"

```

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

```ruby
$ ./chomp.rb 
Enter a string: Ruby
The string has 4 characters

```

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

## Ruby 文件

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

```ruby
#!/usr/bin/ruby

f = File.open('output.txt', 'w')
f.puts "The Ruby tutorial"
f.close

```

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

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

```

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

```ruby
f.puts "The Ruby tutorial"

```

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

```ruby
f.close

```

最后，流关闭。

```ruby
$ ./simplewrite.rb
$ cat output.txt 
The Ruby tutorial

```

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

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

```ruby
#!/usr/bin/ruby

File.open('langs', 'w') do |f|

    f.puts "Ruby"
    f.write "Java\n"
    f << "Python\n"

end

```

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

```ruby
f.puts "Ruby"
f.write "Java\n"
f << "Python\n"

```

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

```ruby
$ ./simplewrite2.rb
$ cat langs 
Ruby
Java
Python

```

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

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

```ruby
#!/usr/bin/ruby

puts File.exists? 'tempfile'

f = File.new 'tempfile', 'w'
puts File.mtime 'tempfile'
puts f.size

File.rename 'tempfile', 'tempfile2'

f.close

```

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

```ruby
puts File.exists? 'tempfile'

```

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

```ruby
f = File.new 'tempfile', 'w'

```

文件已创建。

```ruby
puts File.mtime 'tempfile'

```

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

```ruby
puts f.size

```

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

```ruby
File.rename 'tempfile', 'tempfile2'

```

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

```ruby
$ ./testfile.rb
false
2011-11-05 16:19:36 +0100
0

```

这是一个示例输出。

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

```ruby
#!/usr/bin/ruby

f = File.open("stones")

while line = f.gets do
    puts line
end

f.close

```

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

```ruby
f = File.open("stones")

```

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

```ruby
while line = f.gets do
    puts line
end

```

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

```ruby
$ ./readlines2.rb
Garnet
Topaz
Opal
Amethyst
Ruby
Jasper
Pyrite
Malachite
Quartz

```

这是示例的输出。

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

```ruby
#!/usr/bin/ruby

fname = 'alllines.rb'

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

```

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

```ruby
File.readlines(fname).each do |line|
    puts line
end

```

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

```ruby
$ ./alllines.rb
#!/usr/bin/ruby

fname = 'alllines.rb'

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

```

示例的输出。

## Ruby 目录

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

```ruby
#!/usr/bin/ruby

Dir.mkdir "tmp"
puts Dir.exists? "tmp"

puts Dir.pwd
Dir.chdir "tmp"
puts Dir.pwd

Dir.chdir '..'
puts Dir.pwd
Dir.rmdir "tmp"
puts Dir.exists? "tmp"

```

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

```ruby
Dir.mkdir "tmp"

```

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

```ruby
puts Dir.exists? "tmp"

```

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

```ruby
puts Dir.pwd

```

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

```ruby
Dir.chdir '..'

```

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

```ruby
Dir.rmdir "tmp"
puts Dir.exists? "tmp"

```

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

```ruby
$ ./dirs.rb
true
/home/vronskij/programming/ruby/io
/home/vronskij/programming/ruby/io/tmp
/home/vronskij/programming/ruby/io
false

```

这是示例的输出。

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

```ruby
#!/usr/bin/ruby

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

```

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

```ruby
fls = Dir.entries '.'
puts fls.inspect

```

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

```ruby
$ ./allfiles.rb
["putc.rb", "simplewrite.rb", "readlines2.rb", "fileexists.rb~" ...

```

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

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

```ruby
#!/usr/bin/ruby

puts Dir.home
puts Dir.home 'root'

```

该脚本打印两个主目录。

```ruby
puts Dir.home

```

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

```ruby
puts Dir.home 'root'

```

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

```ruby
$ ./homedir.rb
/home/vronskij
/root

```

这是一个示例输出。

## Ruby 执行外部程序

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

```ruby
#!/usr/bin/ruby

data = system 'ls'
puts data

```

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

```ruby
data = system 'ls'

```

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

```ruby
$ ./system.rb
allfiles.rb   characters.rb  fileexists.rb   homedir.rb~ ...

```

This is a sample output.

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

```ruby
#!/usr/bin/ruby

out = `pwd`
puts out

out = %x[uptime]
puts out

out = %x[ls | grep 'readline']
puts out

```

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

```ruby
out = `pwd`

```

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

```ruby
out = %x[uptime]

```

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

```ruby
out = %x[ls | grep 'readline']

```

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

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

```

This is a sample output.

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

```ruby
#!/usr/bin/ruby

f = open("|ls -l |head -3")
out = f.read
puts out
f.close

puts $?.success?

```

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

```ruby
f = open("|ls -l |head -3")

```

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

```ruby
out = f.read
puts out

```

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

```ruby
f.close

```

我们关闭文件处理器。

```ruby
puts $?.success?

```

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

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

```

This is a sample output.

## Ruby 重定向标准输出

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

```ruby
#!/usr/bin/ruby

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

puts "Ruby"
puts "Java"

$stdout.close
$stdout = STDOUT

puts "Python"

```

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

```ruby
$stdout = File.open "output.log", "a"

```

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

```ruby
puts "Ruby"
puts "Java"

```

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

```ruby
$stdout.close

```

处理器已关闭。

```ruby
$stdout = STDOUT

puts "Python"

```

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

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