Skip to content

Ruby 文件 I/O

Ruby 提供了一套全面的 I/O(输入/输出)相关方法,主要通过 IO 类及其子类(如 File)实现。许多基本的 I/O 操作也作为 Kernel 模块中的方法提供,使得它们可以全局访问。

IO 类提供了基本方法,例如 read、write、gets、puts、readline、getc 和 printf。本章介绍 Ruby 中的基本 I/O 函数以及文件/目录操作。

puts(put string)将其参数写入标准输出 (STDOUT),每个参数后跟一个换行符。如果参数是一个数组,它将每个元素写在新的一行上。如果调用时没有参数,它只打印一个换行符。

#!/usr/bin/env ruby
# frozen_string_literal: true
val1 = "This is variable one"
val2 = "This is variable two"
puts val1
puts val2
puts [1, 2, 3] # Prints each number on a new line

输出结果为:

This is variable one
This is variable two
1
2
3

gets(get string)从标准输入 (STDIN) 读取一行输入。它返回包含末尾换行符的行,如果到达文件末尾则返回 nil。使用 chomp 移除换行符。

#!/usr/bin/env ruby
# frozen_string_literal: true
puts "Enter your name:"
name = gets
puts "Hello, #{name.chomp}! You entered: #{name.inspect}" # .chomp 移除末尾的换行符
puts "Enter your age:"
age_str = gets.chomp
if age_str
age = age_str.to_i
puts "You will be #{age + 5} in 5 years."
else
puts "No age entered."
end

示例交互:

Enter your name:
Alice
Hello, Alice! You entered: "Alice\n"
Enter your age:
30
You will be 35 in 5 years.

putc(put character)写入单个字符到标准输出。如果给定字符串,它写入第一个字符。如果给定整数,它写入对应于该字节值的字符。

#!/usr/bin/env ruby
# frozen_string_literal: true
str = "Hello Ruby!"
putc str # 输出: H
putc 65 # 输出: A ('A' 的 ASCII 值)

print 类似于 puts,但不会自动附加换行符。它对其参数调用 to_s 并将它们写入 STDOUT。

#!/usr/bin/env ruby
# frozen_string_literal: true
print "Hello World"
print " Good Morning"
print "\nNext line."

输出结果为:

Hello World Good Morning
Next line.

File.open 方法用于打开文件。它可以接受一个块,这是推荐的方式,因为它确保当块完成时文件会自动关闭,即使发生错误也是如此。

# Syntax with a block (recommended)
File.open("filename.txt", "mode") do |file_object|
# ... process the file using file_object ...
end # File is automatically closed here
# Syntax without a block (manual close needed)
# aFile = File.new("filename.txt", "mode")
# ... process the file ...
# aFile.close

File.new 类似于 File.open,但不接受块。

文件模式:

模式描述
r只读。从文件开头开始(默认模式)。
r+读写。从文件开头开始。
w只写。截断现有文件长度为零,或创建一个新文件用于写入。
w+读写。截断现有文件或创建一个新文件用于读写。
a只写(追加)。如果文件存在,则从文件末尾开始,否则创建新文件。数据总是写入文件末尾。
a+读写(追加)。写入时从末尾开始。读取可以从任意位置进行。如果文件不存在则创建新文件。
b二进制文件模式(可选,可与其他模式组合,例如 “rb”, “wb+”)。在某些系统(如 Windows)上,这会影响换行符的处理。

文件打开后,可以在文件对象上使用 IO 方法,例如 read、gets、puts、write。

sysread 和 syswrite 方法(低级):

Section titled “sysread 和 syswrite 方法(低级):”

sysread 从文件直接读取指定数量的字节,绕过 Ruby 的内部缓冲。syswrite 用于写入。这些方法不如带缓冲的 I/O 方法常用。

#!/usr/bin/env ruby
# frozen_string_literal: true
# 创建一个示例文件
File.open("sample.txt", "w") { |f| f.puts "This is line one."; f.puts "This is line two." }
# Using sysread
begin
File.open("sample.txt", "r") do |file|
content_chunk = file.sysread(20) # Read up to 20 bytes
puts "First 20 bytes (sysread): #{content_chunk.inspect}"
end
rescue EOFError
puts "sysread: End of file reached prematurely."
end
# Using syswrite (overwrites beginning of file)
begin
File.open("sample.txt", "r+") do |file|
bytes_written = file.syswrite("HELLO ")
puts "Bytes written (syswrite): #{bytes_written}"
end
rescue SystemCallError => e
puts "syswrite error: #{e.message}"
end
# Check content after syswrite
puts "Content after syswrite: #{File.read('sample.txt').inspect}"

遍历文件的每个字节(作为整数)。

#!/usr/bin/env ruby
File.open("sample.txt", "r") do |file|
puts "sample.txt 中的字节:"
file.each_byte { |byte| print "#{byte.chr}." if byte < 128 } # 打印可显示的 ASCII 字符
puts
end

将整个文件读取到一个字符串数组中,数组的每个元素是文件中的一行(包含换行符)。对于非常大的文件请谨慎使用,因为它会将所有内容加载到内存中。

#!/usr/bin/env ruby
# frozen_string_literal: true
begin
lines_array = File.readlines("sample.txt") # IO.readlines 类似
puts "第 0 行: #{lines_array[0].chomp if lines_array[0]}"
puts "第 1 行: #{lines_array[1].chomp if lines_array[1]}"
rescue Errno::ENOENT
puts "文件未找到。"
end

遍历文件的每一行,并将其传递给一个块。与 readlines 相比,它逐行处理,内存效率更高,适用于大文件。

#!/usr/bin/env ruby
# frozen_string_literal: true
begin
File.foreach("sample.txt") do |line|
puts "正在处理行: #{line.chomp}"
end
rescue Errno::ENOENT
puts "文件未找到。"
end

其他常见的读取方法:File.read(filename) 将整个文件内容读取为一个字符串。file.read(length) 读取 length 个字节。

File.rename(old_name, new_name) 重命名文件。

File.delete(filename) 或 File.unlink(filename) 删除文件。

#!/usr/bin/env ruby
# Create a file to work with
File.write("original.txt", "Some content.")
# Rename the file
if File.exist?("original.txt")
File.rename("original.txt", "renamed.txt")
puts "original.txt 已重命名为 renamed.txt"
end
# Delete the file
if File.exist?("renamed.txt")
File.delete("renamed.txt")
puts "renamed.txt 已删除"
end

使用 File.chmod(mode, filename, ...) 更改一个或多个文件的权限(模式)。mode 是一个八进制数。

#!/usr/bin/env ruby
File.write("script.sh", "#!/bin/bash\necho Hello")
# 使其对所有者可执行,对所有者/组/其他用户可读
File.chmod(0755, "script.sh")
puts "script.sh 的权限已更改。"
# File.delete("script.sh") # 清理

常见的八进制权限掩码:

掩码描述(权限)
0700rwx------(所有者:读、写、执行)
0600rw-------(所有者:读、写)
0755rwxr-xr-x(所有者:rwx;组:rx;其他用户:rx)- 可执行文件/目录常用
0644rw-r—r—(所有者:rw;组:r;其他用户:r)- 普通文件常用

Ruby 提供了许多查询文件属性的方法(通常以 ? 结尾):

#!/usr/bin/env ruby
# frozen_string_literal: true
filename = "sample.txt"
File.write(filename, "Test content for inquiries.") unless File.exist?(filename)
puts "文件 '#{filename}' 是否存在? #{File.exist?(filename)}" # => true
puts "它是一个文件吗? #{File.file?(filename)}" # => true
puts "它是一个目录吗? #{File.directory?(filename)}" # => false
puts "它可读吗? #{File.readable?(filename)}" # => true (通常)
puts "它可写吗? #{File.writable?(filename)}" # => true (通常)
puts "它可执行吗? #{File.executable?(filename)}" # => false (对于文本文件通常)
puts "它大小为零吗? #{File.zero?(filename)}" # => false (如果有内容)
puts "大小: #{File.size(filename)} 字节" # => (内容大小)
puts "大小(如果为零则为 nil): #{File.size?(filename)} 字节" # => (内容大小,如果为零则为 nil)
puts "文件类型: #{File.ftype(filename)}" # => "file"
puts "创建时间: #{File.ctime(filename)}"
puts "修改时间: #{File.mtime(filename)}"
puts "最后访问时间: #{File.atime(filename)}"
# File.delete(filename) # 清理

ftype 方法可以返回:file、directory、characterSpecial、blockSpecial、fifo、link、socket 或 unknown。

Dir 类用于目录操作。

Dir.chdir(path):更改当前工作目录。

Dir.pwd:返回当前工作目录路径。

Dir.entries(path):返回指定路径中所有条目(文件和目录,包括 . 和 ..)的数组。

Dir.foreach(path) { |entry| ... }:遍历条目。

Dir.glob(pattern) 或 Dir[pattern]:返回与模式匹配的文件名数组(通配符如 *、?、[])。

#!/usr/bin/env ruby
# frozen_string_literal: true
puts "当前目录: #{Dir.pwd}"
# List entries in current directory (first 5 for brevity)
puts "\n'.' 中的条目:"
Dir.entries(".").first(5).each { |entry| puts " #{entry}" }
puts "\n'.' 中的 Ruby 文件:"
Dir.glob("*.rb").each { |file| puts " #{file}" }
# Or: Dir["*.rb"].each { |file| puts " #{file}" }

Dir.mkdir(path, mode=0777):创建目录。mode 指定权限。

Dir.delete(path) 或 Dir.rmdir(path) 或 Dir.unlink(path):删除空目录。

#!/usr/bin/env ruby
# frozen_string_literal: true
dir_name = "mynewdir"
begin
unless Dir.exist?(dir_name)
Dir.mkdir(dir_name, 0755)
puts "目录 '#{dir_name}' 已创建。"
else
puts "目录 '#{dir_name}' 已存在。"
end
# 对目录进行一些操作 ...
if Dir.exist?(dir_name)
Dir.delete(dir_name)
puts "目录 '#{dir_name}' 已删除。"
end
rescue SystemCallError => e
puts "目录操作失败: #{e.message}"
end

对于临时文件,Ruby 的标准库 tmpdir 和 tempfile 很有用。

#!/usr/bin/env ruby
# frozen_string_literal: true
require 'tmpdir' # 用于 Dir.tmpdir
require 'tempfile' # 用于 Tempfile 类
# Using Dir.tmpdir (不太常用于直接文件创建)
temp_dir_path = Dir.tmpdir
custom_temp_filename = File.join(temp_dir_path, "my_app_temp_#{Process.pid}.dat")
puts "自定义临时文件可能位于: #{custom_temp_filename}"
# 如果直接使用此方法,需要手动创建和删除。
# Using Tempfile (推荐用于临时文件)
begin
temp_file = Tempfile.new('app_data_prefix') # 创建一个具有唯一名称的临时文件
temp_file.puts "This is some temporary data."
temp_file.puts "当对象终结或关闭时,它将自动删除。"
puts "Tempfile 路径: #{temp_file.path}"
temp_file.rewind # 返回开头以便读取
puts "Tempfile 内容: #{temp_file.read.chomp}"
ensure
# Tempfile 在垃圾回收或显式关闭和解除链接时自行处理删除
temp_file.close # 关闭文件
temp_file.unlink # 从文件系统删除文件(可选,关闭通常会完成此操作)
puts "Tempfile 已关闭并解除链接。"
end

Ruby 的 File 和 Dir 类,以及 FileUtils(一个标准库模块,需要 require 'fileutils')中的方法,提供了丰富的 API,用于所有常见的文件和目录操作。请查阅官方 Ruby 文档以获取完整的方法列表及其选项。