Ruby文件和目录操作及输入输出全解析
1. 文件和目录操作
在进行文件和目录操作时,有多种方法可以获取文件列表。使用
Dir.[]
操作符可以获取匹配特定模式的文件列表,示例如下:
Dir['*.data'] # Files with the "data" extension
Dir['ruby.*'] # Any filename beginning with "ruby."
Dir['?'] # Any single-character filename
Dir['*.[ch]'] # Any file that ends with .c or .h
Dir['*.{java,rb}'] # Any file that ends with .java or .rb
Dir['*/*.rb'] # Any Ruby program in any direct sub-directory
Dir['**/*.rb'] # Any Ruby program in any descendant directory
更强大的替代方法是
Dir.glob
,默认情况下它的工作方式与
Dir[]
类似,但如果传递一个块,它会逐个产生匹配的文件名,而不是返回一个数组。此外,
glob
方法接受一个可选的第二个参数,如果将常量
File::FNM_DOTMATCH
作为第二个参数传递,结果将包括以“.”开头的文件(在Unix系统中,这些文件是隐藏的,默认情况下不会返回),示例如下:
Dir.glob('*.rb') {|f| ... } # Iterate all Ruby files
Dir.glob('*') # Does not include names beginning with '.'
Dir.glob('*',File::FNM_DOTMATCH) # Include . files, just like Dir.entries
目录列表方法和所有解析相对路径名的
File
和
Dir
方法都是相对于“当前工作目录”进行的,可以使用
getwd
和
chdir
方法查询和设置当前工作目录,示例如下:
puts Dir.getwd # Print current working directory
Dir.chdir("..") # Change CWD to the parent directory
Dir.chdir("../sibling") # Change again to a sibling directory
Dir.chdir("/home") # Change to an absolute directory
Dir.chdir # Change to user's home directory
home = Dir.pwd # pwd is an alias for getwd
如果将一个块传递给
chdir
方法,当块退出时,目录将恢复到其原始值。但需要注意的是,虽然目录更改的持续时间有限,但它的作用域仍然是全局的,会影响其他线程,两个线程不能同时使用块调用
Dir.chdir
。
2. 文件测试
File
类定义了许多方法来获取指定文件或目录的元数据,以下是一些常用的简单方法,用于返回文件的基本信息,大多数是返回
true
或
false
的谓词:
f = "/usr/bin/ruby" # A filename for the examples below
# File existence and types.
File.exist?(f) # Does the named file exist? Also: File.exists?
File.file?(f) # Is it an existing file?
File.directory?(f) # Or is it an existing directory?
File.symlink?(f) # Either way, is it a symbolic link?
# File size methods. Use File.truncate to set file size.
File.size(f) # File size in bytes.
File.size?(f) # Size in bytes or nil if empty file.
File.zero?(f) # True if file is empty.
# File permissions. Use File.chmod to set permissions (system dependent).
File.readable?(f) # Can we read the file?
File.writable?(f) # Can we write the file? No "e" in "writable"
File.executable?(f) # Can we execute the file?
File.world_readable?(f) # Can everybody read it? Ruby 1.9.
File.world_writable?(f) # Can everybody write it? Ruby 1.9.
# File times/dates. Use File.utime to set the times.
File.mtime(f) # => Last modification time as a Time object
File.atime(f) # => Last access time as a Time object
另一种确定文件名类型(文件、目录、符号链接等)的方法是使用
ftype
,它返回一个表示类型的字符串,示例如下:
File.ftype("/usr/bin/ruby") # => "link"
File.ftype("/usr/bin/ruby1.9") # => "file"
File.ftype("/usr/lib/ruby") # => "directory"
File.ftype("/usr/bin/ruby3.0") # SystemCallError: No such file or directory
如果对文件的多个信息感兴趣,调用
stat
或
lstat
可能更高效(
stat
会跟随符号链接,
lstat
返回关于链接本身的信息),这些方法返回一个
File::Stat
对象,该对象具有与
File
类方法同名(但无参数)的实例方法,示例如下:
s = File.stat("/usr/bin/ruby")
s.file? # => true
s.directory? # => false
s.ftype # => "file"
s.readable? # => true
s.writable? # => false
s.executable? # => true
s.size # => 5492
s.atime # => Mon Jul 23 13:20:37 -0700 2007
最后一个通用的文件测试方法是
Kernel.test
,它主要用于与Unix shell命令
test
保持历史兼容性,示例如下:
# Testing single files
test ?e, "/usr/bin/ruby" # File.exist?("/usr/bin/ruby")
test ?f, "/usr/bin/ruby" # File.file?("/usr/bin/ruby")
test ?d, "/usr/bin/ruby" # File.directory?("/usr/bin/ruby")
test ?r, "/usr/bin/ruby" # File.readable?("/usr/bin/ruby")
test ?w, "/usr/bin/ruby" # File.writeable?("/usr/bin/ruby")
test ?M, "/usr/bin/ruby" # File.mtime("/usr/bin/ruby")
test ?s, "/usr/bin/ruby" # File.size?("/usr/bin/ruby")
# Comparing two files f and g
test ?-, f, g # File.identical(f,g)
test ?<, f, g # File(f).mtime < File(g).mtime
test ?>, f, g # File(f).mtime > File(g).mtime
test ?=, f, g # File(f).mtime == File(g).mtime
3. 创建、删除和重命名文件及目录
创建文件时,
File
类没有定义特殊的方法,只需打开文件进行写入,写入零个或多个字节,然后再次关闭它。如果不想覆盖现有文件,可以以追加模式打开它,示例如下:
# Create (or overwrite) a file named "test"
File.open("test", "w") {}
# Create (but do not clobber) a file named "test"
File.open("test", "a") {}
要更改文件的名称,可以使用
File.rename
,示例如下:
File.rename("test", "test.old") # Current name, then new name
要创建文件的符号链接,可以使用
File.symlink
,示例如下:
File.symlink("test.old", "oldtest") # Link target, link name
在支持的系统上,可以使用
File.link
创建“硬链接”,示例如下:
File.link("test.old", "test2") # Link target, link name
最后,要删除文件或链接,可以使用
File.delete
或其同义词
File.unlink
,示例如下:
File.delete("test2") # May also be called with multiple args
File.unlink("oldtest") # to delete multiple named files
在支持的系统上,可以使用
File.truncate
将文件截断为指定数量(可能为零)的字节,使用
File.utime
设置文件的访问和修改时间,使用依赖于平台的方法
File.chmod
更改文件的权限,示例如下:
f = "log.messages" # Filename
atime = mtime = Time.now # New access and modify times
File.truncate(f, 0) # Erase all existing content
File.utime(atime, mtime, f) # Change times
File.chmod(0600, f) # Unix permissions -rw-------; note octal arg
创建新目录可以使用
Dir.mkdir
,删除目录可以使用
Dir.rmdir
或其同义词
Dir.delete
或
Dir.unlink
,但目录必须为空才能删除,示例如下:
Dir.mkdir("temp") # Create a directory
File.open("temp/f", "w") {} # Create a file in it
File.open("temp/g", "w") {} # Create another one
File.delete(*Dir["temp/*"]) # Delete all files in the directory
Dir.rmdir("temp") # Delete the directory
4. 输入/输出
IO
对象是一个流,是字节或字符的可读源或可写汇,
File
类是
IO
的子类,
IO
对象还代表用于从控制台读取和写入控制台的“标准输入”和“标准输出”流。标准库中的
stringio
模块允许我们围绕字符串对象创建一个流包装器,网络中使用的套接字对象也是
IO
对象。
5. 打开流
在进行输入或输出之前,必须有一个
IO
对象用于读取或写入。以下是几种常见的获取
IO
对象的方法:
-
打开文件
:使用
File.open
(或
File.new
)打开文件,第一个参数是文件名,通常指定为字符串,但在Ruby 1.9中,可以使用任何具有
to_path
方法的对象。第二个参数是一个短字符串,指定文件的打开方式,示例如下:
f = File.open("data.txt", "r") # Open file data.txt for reading
out = File.open("out.txt", "w") # Open file out.txt for writing
File.open
的第二个参数指定“文件模式”,必须以以下表格中的值之一开头:
| 模式 | 描述 |
| ---- | ---- |
| “r” | 打开用于读取,默认模式 |
| “r+” | 打开用于读写,从文件开头开始,如果文件不存在则失败 |
| “w” | 打开用于写入,创建新文件或截断现有文件 |
| “w+” | 类似于”w”,但也允许读取文件 |
| “a” | 打开用于写入,但如果文件已存在则追加到文件末尾 |
| “a+” | 类似于”a”,但也允许读取 |
如果
File.open
后面跟随一个块,它不会返回
File
对象,而是将其传递给块,并在块退出时自动关闭它,块的返回值成为
File.open
的返回值,示例如下:
File.open("log.txt", "a") do |log| # Open for appending
log.puts("INFO: Logging a message") # Output to the file
end # Automatically closed
-
Kernel.open
:
Kernel方法open的工作方式与File.open类似,但更灵活。如果文件名以|开头,它将被视为操作系统命令,返回的流用于从该命令进程读取和写入,示例如下:
# How long has the server been up?
uptime = open("|uptime") {|f| f.gets }
如果加载了
open-uri
库,
open
还可以用于从http和ftp URL读取,就像它们是文件一样,示例如下:
require "open-uri" # Required library
f = open("http://www.davidflanagan.com/") # Webpage as a file
webpage = f.read # Read it as one big string
f.close # Don't forget to close!
在Ruby 1.9中,如果传递给
open
的参数有一个名为
to_open
的方法,则会调用该方法并应返回一个已打开的
IO
对象。
-
StringIO
:使用
stringio
库可以从字符串读取或写入字符串来获取
IO
对象,示例如下:
require "stringio"
input = StringIO.open("now is the time") # Read from this string
buffer = ""
output = StringIO.open(buffer, "w") # Write into buffer
StringIO
类不是
IO
的子类,但它定义了许多与
IO
相同的方法,通常可以使用
StringIO
对象代替
IO
对象。
-
预定义流
:Ruby预定义了许多可以直接使用而无需创建或打开的流,如全局常量
STDIN
、
STDOUT
和
STDERR
分别是标准输入流、标准输出流和标准错误流。另一个预定义流是
ARGF
或
$<
,用于简化读取命令行指定的文件或标准输入的脚本编写。最后,
DATA
流用于读取Ruby脚本末尾之后出现的文本,但脚本必须包含单独一行的
__END__
标记。
6. 流和编码
Ruby 1.9的一个重大变化是支持多字节字符编码,每个流可以关联两种编码,即外部编码和内部编码,可以使用
external_encoding
和
internal_encoding
方法获取。如果外部编码也是所需的内部编码,则无需指定内部编码;否则,可以指定内部编码,Ruby将在读取时从外部编码转换为内部编码,在写入时转换回外部编码。
可以使用
set_encoding
方法指定任何
IO
对象的编码,示例如下:
f.set_encoding("iso-8859-1", "utf-8") # Latin-1, transcoded to UTF-8
f.set_encoding("iso-8859-1:utf-8") # Same as above
f.set_encoding(Encoding::UTF-8) # UTF-8 text
对于文件,通常在打开文件时指定编码更容易,可以通过将编码名称追加到文件模式字符串来实现,示例如下:
in = File.open("data.txt", "r:utf-8"); # Read UTF-8 text
out = File.open("log", "a:utf-8"); # Write UTF-8 text
in = File.open("data.txt", "r:iso8859-1:utf-8"); # Latin-1 transcoded to UTF-8
如果不指定任何编码,Ruby在从文件读取时默认使用默认外部编码,在写入文件或从管道和套接字读写时默认不使用编码(即
ASCII-8BIT/BINARY
编码)。要读取二进制数据,必须明确指定要读取未编码的字节,可以使用模式
"r:binary"
打开文件或在打开文件后将
Encoding::BINARY
传递给
set_encoding
,在Windows上,应使用模式
"rb:binary"
打开二进制文件或调用
binmode
方法。
综上所述,掌握Ruby中的文件和目录操作以及输入输出和编码处理是非常重要的,通过合理运用这些方法和工具,可以高效地完成各种文件处理任务。
Ruby文件和目录操作及输入输出全解析
7. 操作总结与流程梳理
为了更清晰地理解上述文件和目录操作以及输入输出的流程,下面通过几个流程图和表格进行总结。
7.1 文件操作流程
graph LR
A[开始] --> B{选择操作}
B --> |创建文件| C[使用File.open以"w"或"a"模式打开文件]
C --> D[写入内容]
D --> E[关闭文件]
B --> |重命名文件| F[使用File.rename指定新旧文件名]
B --> |创建符号链接| G[使用File.symlink指定目标和链接名]
B --> |创建硬链接| H[使用File.link指定目标和链接名]
B --> |删除文件| I[使用File.delete或File.unlink指定文件名]
C --> J{是否需要截断文件}
J --> |是| K[使用File.truncate指定文件名和字节数]
J --> |否| E
E --> L[结束]
F --> L
G --> L
H --> L
I --> L
K --> L
7.2 目录操作流程
graph LR
A[开始] --> B{选择操作}
B --> |创建目录| C[使用Dir.mkdir指定目录名]
C --> D{是否需要在目录中创建文件}
D --> |是| E[使用File.open在目录中创建文件]
E --> F[写入内容]
F --> G[关闭文件]
D --> |否| H{是否需要删除目录}
E --> H
H --> |是| I[使用File.delete删除目录下所有文件]
I --> J[使用Dir.rmdir删除目录]
J --> K[结束]
B --> |删除目录| I
H --> |否| K
7.3 输入输出操作总结
| 操作类型 | 方法 | 说明 | 示例代码 |
|---|---|---|---|
| 打开文件 | File.open | 打开本地文件,指定文件名和模式 |
f = File.open("data.txt", "r")
|
| Kernel.open | 更灵活,可处理命令和URL |
uptime = open("|uptime") {|f| f.gets }
require "open-uri"; f = open("http://www.davidflanagan.com/")
| |
| StringIO.open | 围绕字符串创建流 |
require "stringio"; input = StringIO.open("now is the time")
| |
| 预定义流 | STDIN | 标准输入流 |
直接使用,如
gets
|
| STDOUT | 标准输出流 |
直接使用,如
puts "Hello"
| |
| STDERR | 标准错误流 |
直接使用,如
$stderr.puts "Error"
| |
| ARGF | 处理命令行文件或标准输入 | 结合ARGV使用 | |
| DATA | 读取脚本末尾文本 |
脚本需包含
__END__
标记
|
8. 编码处理的注意事项
在处理文件和流的编码时,有一些关键的注意事项需要牢记:
-
默认编码
:如果不指定编码,Ruby在读取文件时默认使用默认外部编码,该编码通常由用户的区域设置决定,可能是多字节编码。在写入文件或从管道和套接字读写时,默认使用
ASCII-8BIT/BINARY
编码。
-
二进制数据读取
:要读取二进制数据,必须明确指定要读取未编码的字节。可以使用模式
"r:binary"
打开文件,或者在打开文件后将
Encoding::BINARY
传递给
set_encoding
方法。在Windows上,为了避免自动换行符转换,应使用模式
"rb:binary"
打开二进制文件或调用
binmode
方法。
-
编码转换
:当指定了不同的外部和内部编码时,Ruby会在读取和写入时进行编码转换。例如,将
iso-8859-1
编码的文件转换为
UTF-8
编码进行处理:
in = File.open("data.txt", "r:iso8859-1:utf-8")
content = in.read
puts content.encoding # 输出UTF-8
9. 综合应用示例
下面通过一个综合示例展示如何运用上述知识完成一个实际的文件处理任务:读取一个
iso-8859-1
编码的文件,将其内容转换为
UTF-8
编码并写入一个新文件,同时记录操作的时间。
require 'time'
# 源文件和目标文件路径
source_file = "source.txt"
target_file = "target.txt"
# 记录操作开始时间
start_time = Time.now
begin
# 以iso-8859-1编码读取源文件
in_file = File.open(source_file, "r:iso8859-1:utf-8")
content = in_file.read
# 以UTF-8编码写入目标文件
out_file = File.open(target_file, "w:utf-8")
out_file.write(content)
# 关闭文件
in_file.close
out_file.close
# 记录操作结束时间
end_time = Time.now
# 计算操作耗时
duration = end_time - start_time
puts "文件处理完成,耗时: #{duration} 秒"
rescue StandardError => e
puts "操作过程中出现错误: #{e.message}"
end
10. 总结
通过本文的介绍,我们全面了解了Ruby中文件和目录操作、输入输出以及编码处理的相关知识。从获取文件列表、测试文件属性,到创建、重命名、删除文件和目录,再到输入输出流的打开和使用,以及编码的设置和转换,每个环节都有其特定的方法和注意事项。
在实际应用中,我们可以根据具体需求选择合适的方法和工具,合理运用这些知识可以高效地完成各种文件处理任务。同时,注意编码的正确设置,避免因编码问题导致的数据丢失或乱码。希望本文能够帮助你更好地掌握Ruby中的文件和目录操作以及输入输出处理,提升你的编程效率和能力。
超级会员免费看
1474

被折叠的 条评论
为什么被折叠?



