IO类
输入输出的种类
- 标准输入。获取从键盘输入的内容。
- 通过预定义常量
STDIN
可调用操作标准输入的 IO 对象。
- 全局变量
$stdin
也可以引用标准输入的 IO 对象。
- 不指定接收者的
get
方法等都会默认从标准输入中获取数据。
- 标准输出。向标准输出写入的数据会显示在屏幕中。
- 通过预定义常量
STDOUT
可调用操作标准输出的 IO 对象。
- 另外,全局变量
$stdout
也可以引用标准输出的 IO 对象。
- 不指定接收者的
puts, print, printf
方法会默认将数据写入标准输出。
- 标准错误输出。向标准错误输出写入的数据会显示在屏幕中。
- 通过预定义常量
STDERR
可调用操作标准输出的 IO 对象。
- 另外,全局变量
$stderr
也可以引用标准输出的 IO 对象。
- 标准错误输出原本只用来输出错误和警告信息,但希望与程序正常输出的信息做出区别时也可以使用它。
- 将输出结果重定向到文件时,标准输出的内容会被写入到文件,标准错误输出的内容被输出到屏幕中。
- IO 对象是否与控制台关联,可以使用 tty? 方法判断。
# out.rb
$stdout.print "Output to $stdout. \n"
$stderr.print "Output to $stderr. \n"
# terminal
ruby out.rb > log.txt #=> Output to $stderr.
# log.txt
Output to $stdout.
文件输入、输出
通过 IO 类的子类 File 类可以进行文件的输入输出处理。
io = File.open(file, mode)
io = open(file, mode)
io.close
io.clise?
检查 IO 对象是否关闭了
File.read(file)
一次性读取文件的内容,在 windows 中不能使用这个方法读取图像等二进制数据。因为会对换行符进行转换,而无法得到正确结果。
mode 指定以何种方式打开文件,缺省为只读模式。windows 环境下,模式后加上 b 可表示二进制模式。
模式 |
作用 |
r |
用只读模式打开文件 |
r+ |
用读写模式打开文件 |
w |
用只写模式打开文件。文件不存在则创建新文件;文件已存在则清空文件,既将文件大小设置为0 |
w+ |
用读写模式打开文件,其余同 w |
a |
用追加模式打开文件。文件不存在则创建新的文件 |
a+ |
用读取/追加模式打开文件。文件不存在则创建新的文件 |
一个程序中同时打开的文件的数量是有限制的,用完的文件应该尽快关闭。File.open
方法,如果使用块,则文件会在使用完毕后自动关闭。
输入操作
io.gets(rs)
io.each(rs)
io.each_line(rs)
io.readlines(rs)
一次性的读取所有数据,并返回每行数据作为元素封装的数组。
从 IO 类的对象 io 中读取一行数据。用参数 rs 的字符串分行,默认预定义变量 $/(默认值为 “\n”)。
这些方法返回的字符串包含末尾的换行符。用 chomp!
方法可以删除末尾换行符。
输入完毕后再尝试获取数据时,gets
方法会返回 nil。我们还可以用 eof?
方法检查输入是否已经完毕。
# 经典读取数据的用法
while line = io.gets
line.chomp!
#... do something
end
p io.eof?
io.each_line do |line|
line.chomp!
# ... do something
end
arr = io.readlines
arr.each_line do |line|
line.chomp!
# ... do something
end
io.lineno
使用 get,each_line 方法逐行读取数据时,会自动记录读取的行数。
io.lineno=(number)
改变行数。
io.each_char
逐个字符的读取 io 中的数据并执行块。
io.each_byte
逐个字节的读取 io 中的数据并执行块。得到的字符所对应的 ASCII 码以整数值的形式传递给块变量。
io.getc
只读取 io 中的一个字符。注意,根据编码的不同,一个字符可能对应不同的字节数。
io.ungetc(ch)
将参数 ch 指定的字符退回到 io 的输入缓存中。
io.getbyte
只读取 io 中的一个字节,返回的字节转换为 ASCII 码后的整数对象。
io.ungetbyte(byte)
将参数 byte 指定的一个字节退回到输入缓存中。参数为整数时,将该整数除以 256 后的余数作为 ASCII 码字符返回一个字节;参数作为字符串时,则返回字符串的第一个字节。
io.read(size)
读取参数 size 指定大小的数据。默认一次性读取全部数据并返回。
$stdin.each_line do |line|
printf("%3d %s", $stdin.lineno, line)
end
while ch = io.getc
#... do something
end
# hello.txt 的内容为 "Hello, Ruby.\n"
File.open("hello.txt") do |io|
p io.read(5) #=> "Hello"
p io.read #=> ", Ruby.\n"
end
输出操作
io.puts(str0, str1, ...)
io.putc(ch)
输出参数 ch 指定的字符编码所对应的字符。参数为字符串时输出首字符。
io.print(str0, str1, ...)
io.printf(fmt, arg0, arg1, ...)
按照指定的格式输出字符串。
io.write(str)
输出参数 str 指定的字符串。参数为 String 以外的参数时会自动将其转换为字符串。方法返回值为输出的字节数。
io<<str
输出参数 str 指定的字符串。返回接收者本身。
$stdout.puts "foo", "bar", "baz"
$stdout.putc(82)
$stdout.putc("Ruby")
$stdout.putc("\n")
size = $stdout.write("Hello. \n") #=> Hello.
p size #=> 6
文件指针
一般情况下会以行为单位处理文本数据,为了提高读取效率,可以将文件分为固定长度的文件块,来直接访问某个位置的数据。
我们用文件指针或者当前文件偏移量来表示 IO 对象指向的文件的位置。每当读写文件时,文件指针都会自动移动,而我们也可以使文件指针指向任意位置来读写数据。
io.pos
io.pos=(position)
io.seek(offset, whence)
移动文件指针的方法。offset 为用于指定位置的整数,参数 whence 用于指定 offset 如何移动。
io.rewind
将文件指针返回到文件开头。 lineno
方法返回的行编号为 0
io.truncate(size)
按照参数 size 指定的大小截断文件
#hello.txt 中的内容为 "Hello, Ruby.\n"
File.open("hello.txt") do |io|
p io.read(5) #=> "Hello"
p io.pos #=> 5
io.pos = 0
p io.gets #=> "Hello, Ruby.\n"
end
二进制模式与文本模式
- 各个平台的换行符不一样,为了保证程序的兼容性,会将字符串中的”\n”转换为当前 OS 的换行符并输出。此外,在读取的时候也会将实际的换行符转换为 “\n”。
- 当需要确定文件大小进行输入输出处理时,或者直接使用从其他平台复制文件时,如果进行换行符转换,就可能因为法问题。为了解决这个问题 Ruby 提供了不转换换行符的方法。
- 换行符处理的前提是以行为单位做输入输出处理。需要转换时,称为文本模式,不需要转换时称作二进制模式。
- 转换为二进制模式的 IO 对象无法再次转换为文本模式。
File.open("foo.txt", "w") do |file|
io.binmode
io.write "Hello, world.\n"
end
缓冲
- 在使用 write,print 等方法操作 IO 对象时,程序内部会开辟出一定的空间来保存临时生成的数据副本。这部分空间称为缓冲。缓冲里积累一定的数据后,就会做实际的输出处理,然后清空缓冲。
- 向控制台输出的两种方式中,标准错误输出完全不采用缓冲处理。因为,标准输出和标准错误输出混合使用时,程序实际输出的顺序可能会与程序代码中记录的顺序不一样。
- 标准错误输出的主要目的时输出警告,错误等信息,因为执行结果必须马上反映出来。另外在显示程序中正常以外的信息时建议使用标准错误输出。
$stdout.print "out1"
$stderr.print "err1"
$stdout.print "out2"
$stdout.print "out3"
$stderr.print "err2"
$stdout.print "out4"
#=>
err1 err2
out1 out2 out3 out4
io.flush
强制输出缓冲中的数据。
io.sync
io.sync=(state)
程序写入缓冲时,flush
会自动调用。
$stdout.print "out1"; $stdout.flush
$stderr.print "err1"
$stdout.sync = true
$stdout.print "out1"
$stderr.print "err1"
$stdout.print "out2"
$stderr.print "err2"
#=> out1 err1 out2 err2
命令行交互
IO.popen(command, mode)
与其他命令进行数据处理。
open("|command", mode)
pattern = Regexp.new(ARGV[0])
filename = ARGV[1]
if /.gz$/ =~ filename
file = IO.popen("gunzip -c #{filename}")
else
file = File.open(filename)
end
file.each_line do |text|
if pattern =~ text
print text
end
end
filename = ARGV[0]
open("|gunzip -c #{filename}") do |io
io.each_line do |line|
print line
end
end
open-uri
- 除了控制台和文件以外,进程间通信时使用的管道pipe,网络间通信时使用的套接字socket,都可以作为 IO 对象使用。
- 使用
open-uri
库的功能时,不要使用 File.open
方法,只是用 open
方法即可。
require 'open-uri'
open("http://www.ruby-lang.org") do |io|
puts io.read
end
url = "ftp://www.ruby-lang.org/pub/ruby/2.0/ruby-2.0.0-p0.tar.gz"
open(url) do |io|
open("ruby-2.0.0-p0.tar.gz", "w") do |f|
f.write(io.read)
end
end
stringio
StringIO 就是用于模拟 IO 对象的对象。向其进行输出并不会输出到任何地方,而是会保存到对象中,之后可以用 read
方法来读取该输出。
另一种用法是,将字符串当做 IO 数据处理。将大数据保存在文件中,将小数据直接传输给别的处理,可以通过 StringIO 对象,这样程序就不用区分字符串和 IO 对象了。
require 'stringio'
io = StringIO.new
io.puts "A"
io.puts "B"
io.puts "C"
io.rewind
p io.read #=> "A\nB\nC\n"
require 'stringio'
io = StringIO.new("A\nB\nC\n")
p io.gets #=> "A\n"
p io.gets #=> "B\n"
p io.gets #=> "C\n"