Ruby 文件处理及I/O
文件处理是编程中的基本操作之一,Ruby提供了强大而灵活的文件I/O功能。无论是读取配置文件、处理日志、操作数据文件,还是进行文件系统操作,Ruby都提供了丰富的内置方法来简化这些任务。本章将详细介绍Ruby中文件处理和I/O操作的各种方法和最佳实践。
🎯 文件基础操作
文件打开和关闭
ruby
# 基本文件打开方式
file = File.open("example.txt", "r")
content = file.read
file.close
# 使用块自动关闭文件(推荐方式)
File.open("example.txt", "r") do |file|
content = file.read
puts content
end # 文件自动关闭
# 不同的文件模式
# "r" - 只读模式(默认)
# "w" - 写入模式(覆盖文件内容)
# "a" - 追加模式
# "r+" - 读写模式
# "w+" - 读写模式(覆盖文件内容)
# "a+" - 读写模式(追加)
# 指定编码
File.open("example.txt", "r:utf-8") do |file|
content = file.read
puts content
end文件读取操作
ruby
# 读取整个文件内容
File.open("example.txt", "r") do |file|
content = file.read
puts content
end
# 读取文件的指定字节数
File.open("example.txt", "r") do |file|
chunk = file.read(100) # 读取前100个字节
puts chunk
end
# 逐行读取文件
File.open("example.txt", "r") do |file|
file.each_line do |line|
puts "行: #{line.chomp}" # chomp移除换行符
end
end
# 读取所有行到数组
File.open("example.txt", "r") do |file|
lines = file.readlines
puts lines.inspect
end
# 读取单行
File.open("example.txt", "r") do |file|
first_line = file.gets
puts "第一行: #{first_line}"
end
# 使用File.read快捷方法
content = File.read("example.txt")
puts content
# 使用File.readlines快捷方法
lines = File.readlines("example.txt")
puts lines.inspect✍️ 文件写入操作
基本写入操作
ruby
# 覆盖写入文件
File.open("output.txt", "w") do |file|
file.write("Hello, World!\n")
file.write("这是第二行\n")
end
# 使用puts写入(自动添加换行符)
File.open("output.txt", "w") do |file|
file.puts("Hello, World!")
file.puts("这是第二行")
end
# 追加写入文件
File.open("output.txt", "a") do |file|
file.puts("这是追加的内容")
end
# 使用File.write快捷方法
File.write("output.txt", "Hello, World!\n", mode: "w")
# 追加写入
File.write("output.txt", "追加内容\n", mode: "a")
# 格式化写入
data = { name: "张三", age: 25, city: "北京" }
File.open("user.txt", "w") do |file|
file.puts "用户信息:"
data.each { |key, value| file.puts "#{key}: #{value}" }
end高级写入操作
ruby
# 写入二进制文件
image_data = File.read("input.jpg", mode: "rb")
File.open("output.jpg", "wb") do |file|
file.write(image_data)
end
# 使用print方法写入
File.open("output.txt", "w") do |file|
file.print("Hello")
file.print(" World!") # 不添加换行符
file.puts # 添加换行符
end
# 写入数组内容
lines = ["第一行", "第二行", "第三行"]
File.open("output.txt", "w") do |file|
lines.each { |line| file.puts(line) }
end
# 使用<<操作符
File.open("output.txt", "w") do |file|
file << "Hello, World!\n"
file << "这是第二行\n"
end
# 写入哈希数据
config = {
database: { host: "localhost", port: 5432 },
logging: { level: "info", file: "app.log" }
}
File.open("config.txt", "w") do |file|
config.each do |section, settings|
file.puts "[#{section}]"
settings.each { |key, value| file.puts "#{key} = #{value}" }
file.puts
end
end📁 文件系统操作
文件和目录检查
ruby
# 检查文件是否存在
puts File.exist?("example.txt") # true/false
puts File.exist?("nonexistent.txt") # false
# 检查是否为文件
puts File.file?("example.txt") # true/false
# 检查是否为目录
puts File.directory?("docs") # true/false
# 检查文件可读性
puts File.readable?("example.txt") # true/false
# 检查文件可写性
puts File.writable?("example.txt") # true/false
# 检查文件可执行性
puts File.executable?("script.rb") # true/false
# 获取文件大小
puts File.size("example.txt") # 字节数
# 检查文件是否为空
puts File.zero?("empty.txt") # true/false
# 获取文件修改时间
puts File.mtime("example.txt") # 2023-12-25 14:30:45 +0800
# 获取文件创建时间
puts File.ctime("example.txt") # 2023-12-25 14:30:45 +0800
# 获取文件访问时间
puts File.atime("example.txt") # 2023-12-25 14:30:45 +0800目录操作
ruby
require 'fileutils'
# 列出目录内容
puts Dir.entries(".") # 当前目录的所有文件和目录
# 获取当前工作目录
puts Dir.pwd # D:/Workspace/Coding/...
# 更改当前工作目录
Dir.chdir("docs") do
puts Dir.pwd # 更改后的目录
# 在此目录中执行操作
end
# 创建目录
Dir.mkdir("new_directory")
# 创建多级目录
FileUtils.mkdir_p("parent/child/grandchild")
# 删除空目录
Dir.rmdir("empty_directory")
# 删除目录及其内容
FileUtils.rm_rf("directory_to_delete")
# 复制目录
FileUtils.cp_r("source_directory", "destination_directory")
# 移动目录
FileUtils.mv("old_directory", "new_directory")文件操作
ruby
require 'fileutils'
# 复制文件
FileUtils.cp("source.txt", "destination.txt")
# 复制多个文件
FileUtils.cp(["file1.txt", "file2.txt"], "destination_directory")
# 移动文件
FileUtils.mv("old_name.txt", "new_name.txt")
# 删除文件
FileUtils.rm("unwanted.txt")
# 删除多个文件
FileUtils.rm(["file1.txt", "file2.txt"])
# 安全删除(忽略不存在的文件)
FileUtils.rm_f("maybe_exists.txt")
# 强制删除(不提示确认)
FileUtils.rm_rf("directory_or_file")
# 创建硬链接
FileUtils.ln("source.txt", "hard_link.txt")
# 创建符号链接
FileUtils.ln_s("source.txt", "soft_link.txt")
# 更改文件权限
FileUtils.chmod(0644, "file.txt")
# 更改文件所有者(需要管理员权限)
# FileUtils.chown("user", "group", "file.txt")
# 更改文件时间戳
FileUtils.touch("file.txt")🔍 文件查找和遍历
Glob模式匹配
ruby
# 查找所有.rb文件
ruby_files = Dir.glob("*.rb")
puts ruby_files.inspect
# 递归查找所有.rb文件
all_ruby_files = Dir.glob("**/*.rb")
puts all_ruby_files.inspect
# 查找特定目录下的文件
docs_files = Dir.glob("docs/*")
puts docs_files.inspect
# 使用多种模式
mixed_files = Dir.glob(["*.rb", "*.md", "config/*.yml"])
puts mixed_files.inspect
# 查找隐藏文件
hidden_files = Dir.glob(".*")
puts hidden_files.inspect
# 查找特定扩展名的文件
image_files = Dir.glob("*.{jpg,png,gif}")
puts image_files.inspect
# 查找数字命名的文件
numbered_files = Dir.glob("[0-9]*.txt")
puts numbered_files.inspect目录遍历
ruby
# 递归遍历目录
def traverse_directory(dir)
Dir.foreach(dir) do |entry|
next if entry == "." || entry == ".."
path = File.join(dir, entry)
if File.directory?(path)
puts "目录: #{path}"
traverse_directory(path) # 递归遍历子目录
else
puts "文件: #{path}"
end
end
end
# traverse_directory(".")
# 使用Find模块遍历
require 'find'
Find.find(".") do |path|
if FileTest.directory?(path)
puts "目录: #{path}"
else
puts "文件: #{path}"
end
end
# 按深度遍历
def traverse_by_depth(dir, depth = 0)
indent = " " * depth
Dir.foreach(dir) do |entry|
next if entry == "." || entry == ".."
path = File.join(dir, entry)
if File.directory?(path)
puts "#{indent}目录: #{entry}"
traverse_by_depth(path, depth + 1)
else
puts "#{indent}文件: #{entry}"
end
end
end
# traverse_by_depth(".")🎯 实用文件处理示例
配置文件处理器
ruby
class ConfigFileHandler
# 读取简单的键值对配置文件
def self.read_config(filename)
config = {}
File.open(filename, "r") do |file|
file.each_line do |line|
line = line.strip
next if line.empty? || line.start_with?("#") # 跳过空行和注释
if line.include?("=")
key, value = line.split("=", 2).map(&:strip)
config[key] = value
end
end
end
config
end
# 写入键值对配置文件
def self.write_config(filename, config, comments = {})
File.open(filename, "w") do |file|
config.each do |key, value|
file.puts "# #{comments[key]}" if comments[key]
file.puts "#{key} = #{value}"
file.puts
end
end
end
# 更新配置文件
def self.update_config(filename, updates)
config = read_config(filename)
config.merge!(updates)
write_config(filename, config)
end
end
# 使用配置文件处理器
# config = ConfigFileHandler.read_config("app.conf")
# puts config.inspect
# new_config = {
# "database_host" => "localhost",
# "database_port" => "5432",
# "log_level" => "info"
# }
#
# comments = {
# "database_host" => "数据库主机地址",
# "database_port" => "数据库端口",
# "log_level" => "日志级别"
# }
#
# ConfigFileHandler.write_config("app.conf", new_config, comments)日志文件处理器
ruby
class LogFileHandler
def initialize(log_file)
@log_file = log_file
end
# 写入日志
def log(level, message)
timestamp = Time.now.strftime("%Y-%m-%d %H:%M:%S")
log_entry = "[#{timestamp}] #{level.upcase}: #{message}\n"
File.open(@log_file, "a") do |file|
file.write(log_entry)
end
end
# 读取最近的日志条目
def recent_entries(count = 10)
return [] unless File.exist?(@log_file)
lines = File.readlines(@log_file)
lines.last(count)
end
# 按级别过滤日志
def filter_by_level(level)
return [] unless File.exist?(@log_file)
File.readlines(@log_file).select do |line|
line.include?("[#{level.upcase}]")
end
end
# 清空日志文件
def clear
File.open(@log_file, "w") { |file| file.truncate(0) }
end
# 获取日志文件大小
def size
File.size(@log_file) if File.exist?(@log_file)
end
# 轮转日志文件
def rotate(max_size = 1024 * 1024) # 1MB
return unless File.exist?(@log_file)
return if File.size(@log_file) < max_size
# 重命名当前日志文件
timestamp = Time.now.strftime("%Y%m%d_%H%M%S")
backup_name = "#{@log_file}.#{timestamp}"
File.rename(@log_file, backup_name)
# 创建新的日志文件
File.open(@log_file, "w") { |file| file.write("# 新的日志文件\n") }
end
end
# 使用日志文件处理器
# logger = LogFileHandler.new("app.log")
# logger.log("info", "应用程序启动")
# logger.log("error", "发生错误")
# logger.log("debug", "调试信息")
#
# puts "最近的日志:"
# logger.recent_entries(5).each { |entry| puts entry }
#
# puts "错误日志:"
# logger.filter_by_level("error").each { |entry| puts entry }CSV文件处理器
ruby
require 'csv'
class CSVHandler
# 读取CSV文件
def self.read_csv(filename)
data = []
CSV.foreach(filename, headers: true) do |row|
data << row.to_h
end
data
end
# 写入CSV文件
def self.write_csv(filename, data, headers = nil)
headers ||= data.first.keys if data.first.is_a?(Hash)
CSV.open(filename, "w") do |csv|
csv << headers
data.each do |row|
if row.is_a?(Hash)
csv << headers.map { |header| row[header] }
else
csv << row
end
end
end
end
# 追加数据到CSV文件
def self.append_to_csv(filename, row)
CSV.open(filename, "a") do |csv|
csv << row
end
end
# 按条件筛选CSV数据
def self.filter_csv(filename, &block)
filtered_data = []
CSV.foreach(filename, headers: true) do |row|
filtered_data << row.to_h if block.call(row.to_h)
end
filtered_data
end
# CSV数据统计
def self.csv_stats(filename, column)
values = []
CSV.foreach(filename, headers: true) do |row|
values << row[column]
end
{
count: values.length,
unique_count: values.uniq.length,
min: values.min,
max: values.max
}
end
end
# 使用CSV处理器
# data = [
# { "姓名" => "张三", "年龄" => "25", "城市" => "北京" },
# { "姓名" => "李四", "年龄" => "30", "城市" => "上海" },
# { "姓名" => "王五", "年龄" => "28", "城市" => "广州" }
# ]
#
# CSVHandler.write_csv("users.csv", data)
#
# read_data = CSVHandler.read_csv("users.csv")
# puts read_data.inspect
#
# filtered = CSVHandler.filter_csv("users.csv") { |row| row["年龄"].to_i > 25 }
# puts filtered.inspect📊 文件处理性能优化
大文件处理
ruby
# 逐行处理大文件
def process_large_file(filename)
line_count = 0
File.open(filename, "r") do |file|
file.each_line do |line|
line_count += 1
# 处理每一行
process_line(line)
end
end
line_count
end
def process_line(line)
# 处理单行数据
puts "处理行: #{line.chomp}"
end
# 使用缓冲区处理大文件
def process_large_file_with_buffer(filename, buffer_size = 1024)
File.open(filename, "r") do |file|
while buffer = file.read(buffer_size)
# 处理缓冲区数据
process_buffer(buffer)
end
end
end
def process_buffer(buffer)
# 处理缓冲区数据
puts "处理缓冲区: #{buffer.length} 字节"
end
# 分块处理文件
def process_file_in_chunks(filename, chunk_size = 1000)
File.open(filename, "r") do |file|
chunk = []
file.each_line do |line|
chunk << line.chomp
if chunk.length >= chunk_size
process_chunk(chunk)
chunk = []
end
end
# 处理最后一个块
process_chunk(chunk) unless chunk.empty?
end
end
def process_chunk(chunk)
# 处理数据块
puts "处理块: #{chunk.length} 行"
end文件流处理
ruby
# 使用IO流处理
class FileStreamProcessor
def self.process_stream(input_stream, output_stream, &block)
input_stream.each_line do |line|
processed_line = block.call(line)
output_stream.write(processed_line)
end
end
# 文件到文件的流处理
def self.process_file_to_file(input_file, output_file, &block)
File.open(input_file, "r") do |input|
File.open(output_file, "w") do |output|
process_stream(input, output, &block)
end
end
end
# 文件到标准输出的流处理
def self.process_file_to_stdout(input_file, &block)
File.open(input_file, "r") do |input|
process_stream(input, STDOUT, &block)
end
end
end
# 使用流处理器
# FileStreamProcessor.process_file_to_file("input.txt", "output.txt") do |line|
# line.upcase
# end🛡️ 文件处理安全最佳实践
安全文件操作
ruby
class SafeFileHandler
# 安全读取文件
def self.safe_read(filename, max_size = 10 * 1024 * 1024) # 10MB限制
return nil unless File.exist?(filename)
return nil if File.size(filename) > max_size
File.read(filename)
rescue => e
puts "读取文件出错: #{e.message}"
nil
end
# 安全写入文件
def self.safe_write(filename, content, max_size = 10 * 1024 * 1024)
return false if content.length > max_size
File.write(filename, content)
true
rescue => e
puts "写入文件出错: #{e.message}"
false
end
# 验证文件路径安全性
def self.safe_path?(path, base_dir = Dir.pwd)
# 解析绝对路径
abs_path = File.expand_path(path, base_dir)
base_abs_path = File.expand_path(base_dir)
# 检查是否在基础目录内
abs_path.start_with?(base_abs_path)
end
# 安全删除文件
def self.safe_delete(filename)
return false unless File.exist?(filename)
File.delete(filename)
true
rescue => e
puts "删除文件出错: #{e.message}"
false
end
# 备份文件
def self.backup_file(filename)
return false unless File.exist?(filename)
backup_name = "#{filename}.backup.#{Time.now.to_i}"
FileUtils.cp(filename, backup_name)
backup_name
rescue => e
puts "备份文件出错: #{e.message}"
false
end
end
# 使用安全文件处理器
# if SafeFileHandler.safe_path?("data.txt", "/safe/directory")
# content = SafeFileHandler.safe_read("data.txt")
# puts content
# end
# success = SafeFileHandler.safe_write("output.txt", "Hello, World!")
# puts "写入成功: #{success}"
# backup = SafeFileHandler.backup_file("important.txt")
# puts "备份文件: #{backup}"文件权限和所有权
ruby
class FilePermissionManager
# 检查文件权限
def self.check_permissions(filename)
{
readable: File.readable?(filename),
writable: File.writable?(filename),
executable: File.executable?(filename),
owned: File.owned?(filename),
owned_by_group: File.grpowned?(filename)
}
end
# 设置文件权限(Unix/Linux系统)
def self.set_permissions(filename, permissions)
File.chmod(permissions, filename)
rescue => e
puts "设置权限出错: #{e.message}"
end
# 获取文件详细信息
def self.file_info(filename)
stat = File.stat(filename)
{
size: stat.size,
mtime: stat.mtime,
ctime: stat.ctime,
atime: stat.atime,
uid: stat.uid,
gid: stat.gid,
mode: "%o" % stat.mode,
readable: stat.readable?,
writable: stat.writable?,
executable: stat.executable?
}
rescue => e
puts "获取文件信息出错: #{e.message}"
nil
end
end
# 使用文件权限管理器
# permissions = FilePermissionManager.check_permissions("example.txt")
# puts permissions.inspect
# FilePermissionManager.set_permissions("example.txt", 0644)
# info = FilePermissionManager.file_info("example.txt")
# puts info.inspect🎯 文件处理最佳实践
1. 资源管理
ruby
# 始终使用块语法确保文件正确关闭
# 推荐
File.open("example.txt", "r") do |file|
content = file.read
# 处理内容
end # 文件自动关闭
# 不推荐
file = File.open("example.txt", "r")
content = file.read
# 忘记关闭文件
# file.close
# 使用ensure确保资源清理
def manual_file_handling
file = File.open("example.txt", "r")
begin
content = file.read
# 处理内容
ensure
file.close
end
end2. 错误处理
ruby
# 处理文件操作异常
def robust_file_operation(filename)
File.open(filename, "r") do |file|
content = file.read
process_content(content)
end
rescue Errno::ENOENT
puts "文件 #{filename} 不存在"
rescue Errno::EACCES
puts "没有权限访问文件 #{filename}"
rescue => e
puts "处理文件时出错: #{e.message}"
end
# 检查文件存在性
def safe_file_read(filename)
unless File.exist?(filename)
puts "文件 #{filename} 不存在"
return nil
end
File.read(filename)
rescue => e
puts "读取文件出错: #{e.message}"
nil
end
# 处理编码问题
def read_with_encoding(filename, encoding = "UTF-8")
File.open(filename, "r:#{encoding}") do |file|
file.read
end
rescue Encoding::InvalidByteSequenceError
puts "文件编码不正确"
rescue => e
puts "读取文件时出错: #{e.message}"
end3. 性能优化
ruby
# 对于大文件,使用流式处理
def process_large_file_efficiently(filename)
File.open(filename, "r") do |file|
file.each_line do |line|
# 处理每一行,而不是读取整个文件
process_line(line)
end
end
end
# 批量操作文件
def batch_file_operations(filenames)
filenames.each do |filename|
begin
process_file(filename)
rescue => e
puts "处理文件 #{filename} 时出错: #{e.message}"
end
end
end
# 使用临时文件
def process_with_temp_file(data)
temp_file = Tempfile.new("processing")
begin
temp_file.write(data)
temp_file.close
# 处理临时文件
result = process_file(temp_file.path)
result
ensure
temp_file.unlink # 删除临时文件
end
end4. 实际应用场景
ruby
# 日志分析器
class LogAnalyzer
def initialize(log_file)
@log_file = log_file
end
def analyze
stats = {
total_lines: 0,
error_count: 0,
warning_count: 0,
info_count: 0,
unique_ips: Set.new,
requests_by_hour: Hash.new(0)
}
File.open(@log_file, "r") do |file|
file.each_line do |line|
stats[:total_lines] += 1
# 解析日志行
if parsed_data = parse_log_line(line)
# 统计不同级别的日志
case parsed_data[:level]
when "ERROR"
stats[:error_count] += 1
when "WARNING"
stats[:warning_count] += 1
when "INFO"
stats[:info_count] += 1
end
# 收集唯一IP
stats[:unique_ips] << parsed_data[:ip] if parsed_data[:ip]
# 按小时统计请求
if parsed_data[:timestamp]
hour = parsed_data[:timestamp].hour
stats[:requests_by_hour][hour] += 1
end
end
end
end
stats
end
private
def parse_log_line(line)
# 简化的日志解析
# 实际应用中可能需要更复杂的正则表达式
{
timestamp: Time.now, # 简化处理
level: line.include?("ERROR") ? "ERROR" :
line.include?("WARNING") ? "WARNING" : "INFO",
ip: line.match(/\d+\.\d+\.\d+\.\d+/)&.to_s,
message: line
}
end
end
# 数据导入器
class DataImporter
def self.import_csv_to_database(csv_file, database)
imported_count = 0
failed_count = 0
CSV.foreach(csv_file, headers: true) do |row|
begin
# 转换数据
record = convert_row_to_record(row)
# 插入数据库
database.insert(record)
imported_count += 1
rescue => e
puts "导入行失败: #{e.message}"
failed_count += 1
end
end
{
imported: imported_count,
failed: failed_count,
total: imported_count + failed_count
}
end
private
def self.convert_row_to_record(row)
# 根据需要转换数据
row.to_h
end
end📚 下一步学习
掌握了Ruby文件处理和I/O操作后,建议继续学习:
- Ruby 异常处理 - 掌握错误处理机制
- Ruby 面向对象 - 深入理解面向对象编程
- Ruby 数据库访问 - 学习数据库操作
- Ruby 网络编程 - 了解Socket编程
继续您的Ruby学习之旅吧!