1 文件读写
Ruby 的文件操作 API 简洁优雅,File 类提供了丰富的类方法和实例方法。使用 File.open 配合块是最推荐的方式——块结束时文件自动关闭,无需手动管理。
一次性读写(适合小文件)
content = File.read('config.txt')
puts content
content = File.read('config.txt', encoding: 'UTF-8')
File.write('output.txt', "Hello, Ruby!\n第二行内容\n")
File.write('log.txt', "新日志条目\n", mode: 'a')
File.open 块(推荐方式)
File.open('data.txt', 'r') do |file|
content = file.read
puts content
end
File.open('output.txt', 'w') do |file|
file.puts '第一行'
file.puts '第二行'
file.print '不带换行'
file.write "手动换行\n"
end
File.open('log.txt', 'a') do |file|
file.puts "[#{Time.now}] 应用启动"
end
文件打开模式
| 模式 | 说明 | 文件不存在时 |
|---|---|---|
| 'r' | 只读(默认) | 报错 |
| 'w' | 只写(清空已有内容) | 创建 |
| 'a' | 追加写入 | 创建 |
| 'r+' | 读写 | 报错 |
| 'w+' | 读写(清空已有内容) | 创建 |
| 'a+' | 读写追加 | 创建 |
逐行读取(适合大文件)
File.foreach('large_log.txt') do |line|
puts line if line.include?('ERROR')
end
lines = File.readlines('data.txt')
puts "共 #{lines.size} 行"
puts "第一行: #{lines.first.chomp}"
IO.foreach('data.txt').with_index do |line, index|
puts "#{index + 1}: #{line.chomp}"
end
File.open('huge_file.txt') do |file|
file.lazy.each_line.select { |line| line.include?('WARNING') }.first(10).each do |line|
puts line
end
end
⚡ 读写要点
- 始终使用
File.open { }块形式,确保文件自动关闭 File.read一次性读入内存,大文件用foreach逐行处理puts自动加换行,print/write不加chomp去除行尾换行符,strip去除首尾空白
2 目录操作
Dir 基础操作
puts Dir.pwd
Dir.chdir('/tmp') do
puts Dir.pwd
end
Dir.mkdir('new_folder')
Dir.mkdir('nested/deep/folder') rescue nil
entries = Dir.entries('.')
puts entries.reject { |e| e.start_with?('.') }
Dir.children('.').each { |name| puts name }
rb_files = Dir.glob('**/*.rb')
puts "找到 #{rb_files.size} 个 Ruby 文件"
Dir.glob('app/**/*.{rb,erb}').each { |f| puts f }
Dir.glob('*.log').sort_by { |f| File.mtime(f) }.reverse.each do |f|
puts "#{f} (#{File.mtime(f)})"
end
FileUtils 工具模块
require 'fileutils'
FileUtils.mkdir_p('path/to/nested/directory')
FileUtils.cp('source.txt', 'backup.txt')
FileUtils.cp_r('src_dir', 'backup_dir')
FileUtils.mv('old_name.txt', 'new_name.txt')
FileUtils.rm('temp.txt')
FileUtils.rm_f('maybe_exists.txt')
FileUtils.rm_rf('temp_directory')
FileUtils.touch('marker.txt')
Pathname 类(推荐)
require 'pathname'
path = Pathname.new('/Users/dev/project/src/main.rb')
puts path.dirname
puts path.basename
puts path.extname
puts path.basename('.rb')
config = Pathname.new('config')
db_config = config / 'database.yml'
puts db_config
path = Pathname.new('src/../config/./database.yml')
puts path.cleanpath
project = Pathname.new('/Users/dev/project')
project.children.each do |child|
type = child.directory? ? '📁' : '📄'
puts "#{type} #{child.basename}"
end
project.glob('**/*.rb') { |f| puts f }
💡 Dir vs FileUtils vs Pathname
Dir— 目录遍历和 glob 匹配FileUtils— 文件/目录的复制、移动、删除等操作Pathname— 面向对象的路径操作,推荐用于路径拼接和解析
3 CSV 处理
Ruby 标准库的 csv 模块提供了完善的 CSV 读写能力,支持 header 映射、类型转换和自定义分隔符。
读取 CSV
require 'csv'
table = CSV.read('users.csv', headers: true, encoding: 'UTF-8')
table.each do |row|
puts "#{row['name']} - #{row['email']} (#{row['age']}岁)"
end
CSV.foreach('large_data.csv', headers: true) do |row|
next if row['status'] == 'inactive'
puts "#{row['id']}: #{row['name']}"
end
rows = CSV.read('data.csv')
rows.each { |row| puts row.inspect }
写入 CSV
CSV.open('output.csv', 'w', encoding: 'UTF-8') do |csv|
csv << ['姓名', '邮箱', '年龄']
csv << ['张三', 'zhangsan@example.com', 28]
csv << ['李四', 'lisi@example.com', 32]
csv << ['王五', 'wangwu@example.com', 25]
end
csv_string = CSV.generate do |csv|
csv << ['id', 'name', 'score']
csv << [1, '张三', 95]
csv << [2, '李四', 88]
end
puts csv_string
高级用法
CSV.foreach('data.csv', headers: true, converters: :numeric) do |row|
puts row['age'].class
end
CSV.foreach('data.tsv', col_sep: "\t", headers: true) do |row|
puts row.to_h
end
users = CSV.read('users.csv', headers: true)
active_users = users.select { |row| row['status'] == 'active' }
sorted = users.sort_by { |row| row['name'] }
sorted.each { |row| puts row['name'] }
CSV.open('filtered.csv', 'w') do |out|
out << ['name', 'email']
CSV.foreach('users.csv', headers: true) do |row|
out << [row['name'], row['email']] if row['age'].to_i >= 25
end
end
4 JSON 文件处理
读取 JSON 文件
require 'json'
data = JSON.parse(File.read('config.json'))
puts data['database']['host']
data = JSON.parse(File.read('config.json'), symbolize_names: true)
puts data[:database][:host]
begin
data = JSON.parse(File.read('data.json'))
rescue Errno::ENOENT
puts '文件不存在'
data = {}
rescue JSON::ParserError => e
puts "JSON 格式错误: #{e.message}"
data = {}
end
写入 JSON 文件
config = {
database: {
host: 'localhost',
port: 5432,
name: 'myapp_production'
},
redis: {
url: 'redis://localhost:6379/0'
},
features: {
dark_mode: true,
notifications: false
}
}
File.write('config.json', JSON.pretty_generate(config))
File.write('data.json', JSON.generate(data))
实用模式:JSON 配置管理
class JsonStore
def initialize(path)
@path = path
@data = File.exist?(path) ? JSON.parse(File.read(path), symbolize_names: true) : {}
end
def get(key)
@data[key.to_sym]
end
def set(key, value)
@data[key.to_sym] = value
save
end
def delete(key)
@data.delete(key.to_sym)
save
end
def all
@data.dup
end
private
def save
File.write(@path, JSON.pretty_generate(@data))
end
end
store = JsonStore.new('settings.json')
store.set(:theme, 'dark')
store.set(:language, 'zh-CN')
puts store.get(:theme)
5 YAML 处理
YAML 是 Ruby 生态的「母语」配置格式——Rails 的 database.yml、routes.yml、GitHub Actions、Docker Compose 都使用 YAML。Ruby 标准库内置了 yaml 模块。
读取 YAML
require 'yaml'
config = YAML.load_file('config.yml')
puts config['database']['host']
puts config['database']['port']
config = YAML.load_file('config.yml', permitted_classes: [Symbol, Date, Time])
yaml_string = <<~YAML
name: 我的应用
version: 1.0
features:
- 用户管理
- 权限控制
YAML
data = YAML.safe_load(yaml_string)
puts data['features']
写入 YAML
config = {
'database' => {
'adapter' => 'postgresql',
'host' => 'localhost',
'port' => 5432,
'database' => 'myapp_production',
'pool' => 5
},
'redis' => {
'url' => 'redis://localhost:6379/0'
}
}
File.write('config.yml', YAML.dump(config))
多环境配置模式
# database.yml
development:
adapter: sqlite3
database: db/development.db
test:
adapter: sqlite3
database: db/test.db
production:
adapter: postgresql
host: db.example.com
port: 5432
database: myapp_production
username: deploy
password: <%= ENV['DB_PASSWORD'] %>
pool: 25
require 'yaml'
require 'erb'
env = ENV['RACK_ENV'] || 'development'
raw = File.read('config/database.yml')
parsed = ERB.new(raw).result
all_configs = YAML.safe_load(parsed)
db_config = all_configs[env]
puts "当前环境: #{env}"
puts "数据库: #{db_config['adapter']}://#{db_config['host']}/#{db_config['database']}"
🔄 配置文件格式对比
| 格式 | 可读性 | 注释 | 数据类型 | 常见用途 |
|---|---|---|---|---|
| YAML | 优秀 | 支持 # | 丰富(日期、多行字符串) | Rails 配置、CI/CD |
| JSON | 一般 | 不支持 | 基础类型 | API 数据交换 |
| TOML | 优秀 | 支持 # | 丰富 | Rust/Go 配置 |
| INI | 简单 | 支持 ; # | 仅字符串 | PHP/Python 配置 |
6 文件元信息
文件检查
puts File.exist?('config.yml')
puts File.file?('config.yml')
puts File.directory?('app')
puts File.symlink?('link')
puts File.readable?('data.txt')
puts File.writable?('data.txt')
puts File.executable?('script.sh')
puts File.empty?('log.txt')
puts File.zero?('log.txt')
文件属性
puts File.size('data.bin')
def human_size(bytes)
units = %w[B KB MB GB TB]
return '0 B' if bytes == 0
exp = (Math.log(bytes) / Math.log(1024)).to_i
exp = units.size - 1 if exp >= units.size
"%.1f %s" % [bytes.to_f / 1024**exp, units[exp]]
end
puts human_size(File.size('large_file.dat'))
puts File.mtime('data.txt')
puts File.atime('data.txt')
puts File.ctime('data.txt')
stat = File.stat('script.sh')
puts "大小: #{stat.size}"
puts "权限: #{stat.mode.to_s(8)}"
puts "所有者 UID: #{stat.uid}"
puts "硬链接数: #{stat.nlink}"
路径操作
puts File.basename('/path/to/file.rb')
puts File.basename('/path/to/file.rb', '.rb')
puts File.dirname('/path/to/file.rb')
puts File.extname('archive.tar.gz')
puts File.join('path', 'to', 'file.rb')
puts File.expand_path('~/projects')
puts File.expand_path('../config', __FILE__)
puts File.absolute_path?('/usr/local')
puts File.absolute_path?('relative/path')
实用示例:目录大小统计
require 'pathname'
def dir_size(path)
Pathname.new(path)
.glob('**/*')
.select(&:file?)
.sum(&:size)
end
def dir_summary(path)
root = Pathname.new(path)
stats = Hash.new(0)
root.glob('**/*').select(&:file?).each do |file|
ext = file.extname.downcase
ext = '(无扩展名)' if ext.empty?
stats[ext] += 1
end
stats.sort_by { |_, count| -count }.first(10).each do |ext, count|
puts " #{ext}: #{count} 个文件"
end
end
puts "项目大小: #{human_size(dir_size('.'))}"
puts "文件类型分布:"
dir_summary('.')
⚡ 文件权限与安全
File.chmod(0644, 'file.txt')— 设置文件权限File.chown(uid, gid, 'file.txt')— 更改所有者- 写入敏感文件时先写临时文件再重命名,避免部分写入导致数据损坏
- 使用
Tempfile创建安全的临时文件,自动清理
7 本章要点
📖 文件读写
File.read/File.write 快捷操作,File.open { } 块自动关闭,foreach 逐行处理大文件
📁 目录操作
Dir.glob 模式匹配,FileUtils 复制移动删除,Pathname 面向对象路径操作
📊 CSV
标准库内置,headers: true 自动映射列名,支持流式处理和自定义分隔符
📋 JSON
JSON.parse/JSON.generate,pretty_generate 美化输出,symbolize_names 转 Symbol
⚙️ YAML
Ruby 生态首选配置格式,YAML.load_file 读取,支持 ERB 模板和多环境配置
🔍 元信息
File.exist?/File.size/File.mtime 检查文件属性,File.stat 获取完整信息
下一章预告:第七章将介绍 Ruby 的常用库与工具——Bundler 深入用法、RSpec 测试框架、Rake 构建工具,以及热门 Gem 推荐,完善你的 Ruby 工具箱。