ZetCode

Ruby 中的输入 & 输出

最后修改于 2023 年 10 月 18 日

在本 Ruby 教程中,我们将讨论 Ruby 中的输入 & 输出操作。输入是程序读取的任何数据,无论是来自键盘、文件还是其他程序。输出是程序产生的数据。输出可以发送到屏幕、文件或另一个程序。

输入 & 输出是一个很大的话题。我们提供了一些例子,让您对这个主题有一个大致的了解。Ruby 中的几个类有用于执行输入 & 输出操作的方法。例如 KernelIODirFile

stones.txt
Garnet
Topaz
Opal
Amethyst
Ruby
Jasper
Pyrite
Malachite
Quartz

一些例子使用了这个文件。

Ruby 写入控制台

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

printing.rb
#!/usr/bin/ruby

print "Apple "
print "Apple\n"

puts "Orange"
puts "Orange"

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

print "Apple "
print "Apple\n"

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

puts "Orange"
puts "Orange"

puts 方法将两个字符串打印到控制台。每个字符串都在自己的行上。该方法会自动包含换行符。

$ ./printing.rb
Apple Apple
Orange
Orange

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

根据 Ruby 文档,print 方法等同于 $stdout.print$stdout 是一个全局变量,用于保存标准输出流。

printing2.rb
#!/usr/bin/ruby

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

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

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

printing3.rb
#!/usr/bin/ruby

p "Lemon"
p "Lemon"

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

putc 'K'
putc 0xA

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

p "Lemon"

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

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

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

putc 'K'
putc 0xA

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

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

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

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

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

ios = IO.new STDOUT.fileno

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

ios.write "ZetCode\n"

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

ios.close

输入流已关闭。

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

dev_tty.rb
#!/usr/bin/ruby

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

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

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

sysopen 方法打开给定的路径,返回底层的文件描述符编号。

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

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

ios.puts "ZetCode"
ios.close

我们将一个字符串写入流并关闭它。

Ruby 从控制台读取输入

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

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

reading.rb
#!/usr/bin/ruby

inp = $stdin.read
puts inp

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

inp = $stdin.read

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

$ ./reading.rb
Ruby language
Ruby language

当我们启动一个没有参数的程序时,脚本会从用户读取数据。它一直读取到我们按 Ctrl+DCtrl+Z

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

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

如果我们在重定向时做了一些操作,脚本可以从另一个程序或文件读取数据。

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

read_input.rb
#!/usr/bin/ruby

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

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

name = gets

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

puts "Hello #{name}"

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

$ ./read_input.rb
Enter your name: Jan
Hello Jan

这是一个示例输出。

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

no_chomp.rb
#!/usr/bin/ruby

print "Enter a string: "
inp = gets

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

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

$ ./no_chomp.rb
Enter a string: Ruby
The string has 5 characters

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

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

chomp.rb
#!/usr/bin/ruby

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

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

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

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

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

Ruby 文件

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

simple_write.rb
#!/usr/bin/ruby

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

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

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

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

f.puts "The Ruby tutorial"

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

f.close

最后,流被关闭。

$ ./simple_write.rb
$ cat output.txt
The Ruby tutorial

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

我们有一个类似的例子,展示了其他方法的操作。

simple_write2.rb
#!/usr/bin/ruby

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

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

end

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

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

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

$ ./simple_write2.rb
$ cat langs.txt
Ruby
Java
Python

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

在第二个例子中,我们展示了 File 类的几个方法。

testfile.rb
#!/usr/bin/ruby

puts File.exist? 'tempfile'

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

File.rename 'tempfile', 'tempfile2'

f.close

本例创建了一个名为 tempfile 的新文件并调用了一些方法。

puts File.exist? 'tempfile'

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

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

文件已创建。

puts File.mtime 'tempfile'

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

puts f.size

我们确定文件大小。该方法返回 0,因为我们还没有写入文件。

File.rename 'tempfile', 'tempfile2'

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

$ ./testfile.rb
false
2020-09-14 15:54:13 +0200
0

这是一个示例输出。

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

read_file.rb
#!/usr/bin/ruby

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

while line = f.gets do
    puts line
end

f.close

该示例打开一个名为 stones.txt 的文件,并将其内容逐行打印到终端。

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

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

while line = f.gets do
    puts line
end

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

$ ./read_file.rb
Garnet
Topaz
Opal
Amethyst
Ruby
Jasper
Pyrite
Malachite
Quartz

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

all_lines.rb
#!/usr/bin/ruby

fname = 'alllines.rb'

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

这个脚本展示了另一种读取文件内容的方法。代码示例将其自身的代码打印到终端。

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

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

$ ./all_lines.rb
#!/usr/bin/ruby

fname = 'alllines.rb'

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

Ruby 目录

在本节中,我们使用目录。我们有一个 Dir 类来处理 Ruby 中的目录。

dirs.rb
#!/usr/bin/ruby

Dir.mkdir "tmp"
puts Dir.exist? "tmp"

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

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

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

Dir.mkdir "tmp"

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

puts Dir.exist? "tmp"

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

puts Dir.pwd

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

Dir.chdir '..'

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

Dir.rmdir "tmp"
puts Dir.exist? "tmp"

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

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

all_files.rb
#!/usr/bin/ruby

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

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

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

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

第三个例子使用主目录。计算机中的每个用户都有一个为其分配的唯一目录。这被称为主目录。这是他可以放置文件并创建自己的目录层次结构的地方。

home_dir.rb
#!/usr/bin/ruby

puts Dir.home
puts Dir.home 'root'

该脚本打印了两个主目录。

puts Dir.home

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

puts Dir.home 'root'

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

$ ./homedir.rb
/home/janbodnar
/root

这是一个示例输出。

Ruby 执行外部程序

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

system.rb
#!/usr/bin/ruby

data = system 'ls'
puts data

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

data = system 'ls'

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

我们展示了在 Ruby 中运行外部程序的其他两种方法。

system2.rb
#!/usr/bin/ruby

out = `pwd`
puts out

out = %x[uptime]
puts out

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

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

out = `pwd`

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

out = %x[uptime]

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

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

我们也可以使用命令的组合。

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

system3.rb
#!/usr/bin/ruby

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

puts $?.success?

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

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

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

out = f.read
puts out

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

f.close

我们关闭文件处理程序。

puts $?.success?

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

Ruby 重定向标准输出

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

redirecting.rb
#!/usr/bin/ruby

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

puts "Ruby"
puts "Java"

$stdout.close
$stdout = STDOUT

puts "Python"

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

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

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

puts "Ruby"
puts "Java"

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

$stdout.close

处理程序已关闭。

$stdout = STDOUT

puts "Python"

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

在本 Ruby 教程中,我们学习了 Ruby 中的输入和输出操作。