《Rubu基础教程第五版》第十七章笔记 IO类
输入/输出的种类
标准输入/输出
程序在启动后会预先分配3个IO对象。
标准输入
标准输入是用于接收数据的IO对象。可以通过预定义常量STDIN调用IO对象,也可以通过全局变量$stdin引用IO对象。不指定接受者的gets方法等都会默认从$stdin中获取数据。标准输入最初与控制台关联,接收从键盘输入的内容。
标准输出
标准输出是用于输出数据的IO对象。可以通过预定义常量STDOUT调用IO对象,也可以用全局变量$stdout引用IO对象。不知道接受者的puts、print、printf等方法会默认将数据输出到$stdout。标准输出最初与控制台关联
标准错误输出
标准错误输出是用于输出警告、错误的IO对象。可以通过预定义常量STDERR调用IO对象,也可以用全局变量$stderr引用IO对象。用于显示警告信息的warn方法会将信息输出到$stderr。标准版错误输出最初与控制台关联
代码
3.times do |i|
$stdout.puts "#{Random.rand}"
STDERR.puts "已经输出了#{i+1}次"
end
通过一个标准输出,一个错误输出,只有标准输出会被写入文件。
通过tty检查标准输入是否为屏幕的例子
shijianzhongdeMacBook-Pro:chapter_17 shijianzhong$ ruby tty.rb /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/universal-darwin19/rbconfig.rb:229: warning: Insecure world writable dir /usr/local/opt/mysql@5.7/bin in PATH, mode 040777 Stdin is a TTY. shijianzhongdeMacBook-Pro:chapter_17 shijianzhong$ echo | ruby tty.rb /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/universal-darwin19/rbconfig.rb:229: warning: Insecure world writable dir /usr/local/opt/mysql@5.7/bin in PATH, mode 040777 Stdin is not a TTY. shijianzhongdeMacBook-Pro:chapter_17 shijianzhong$ ruby tty.rb < log.txt /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/universal-darwin19/rbconfig.rb:229: warning: Insecure world writable dir /usr/local/opt/mysql@5.7/bin in PATH, mode 040777 Stdin is not a TTY. shijianzhongdeMacBook-Pro:chapter_17 shijianzhong$ cat tty.rb if $stdin.tty? print "Stdin is a TTY. \n" else print "Stdin is not a TTY. \n" end shijianzhongdeMacBook-Pro:chapter_17 shijianzhong$
文件的输入与输出
通过IO类的子类File类可以进行文件的输入/输出操作。
io = File.open(file [, mode[,perm]][, opt])
io = open(file [, mode[,perm]][, opt])
通过上面的方法可以获得一个io对象
mode默认是"r",可以通过设定指定的值,模式跟Python中的open差不多
File操作中,可以通过块变量传递给块。块执行完毕后,块变量引用的File对象也会自动关闭。像Python中的with open as f
File.open("foo.txt") do |file|
while line = file.gets
... #执行逻辑
end
end
file.closed?检查file对象是否关闭
File.read(file,[, length [, offset]])
不创建File对象,直接读取file里面的数据。length指定读取长度,offset指定从前面第几个字节开始读取数据。如忽略这些参数,程序会从头到位一次性读取文件内容
File.binread(file,[, length [, offset]])
二进制的方式读取文件
FIle.write(file[,data[,offset]])
不创建File对象,直接向file写入data。省略参数offset时,会将文件的全部内容替换为data,指定该参数时,则会将前面offset个字节写入文件,后面的数据则保持不变
>> text = "Hello,Ruby!\n"
=> "Hello,Ruby!\n"
>> File.write("hello.txt", text)
=> 12
>> p File.read("hello.txt")
"Hello,Ruby!\n"
=> "Hello,Ruby!\n"
>> File.write("hello.txt", "12345", 5)
=> 5
>> p File.read("hello.txt")
"Hello12345!\n"
=> "Hello12345!\n"
>>
File.binwrite(file[,data[,offset]])
以二进制模式打开并写入file
基本的输入/输出操作
对于io对象的基本操作。
输入操作 io.gets,io.each,io.each_line,io.readlines
io.gets(rs) rs为分隔符,默认为"\n"
这个方法就像逐行读取,默认读取的时候带\n,可以通过chmop!去除\n
可以通过eof?去判断有没是否输入完毕
io.each 与io.each_line返回的是一个枚举对象了
irb(main):001:0> io = open("log.txt")
=> #<File:log.txt>
irb(main):002:0> io.each.class
=> Enumerator
irb(main):003:0>
io.reandline方案可以差异性读取所有数据,并返回将每行数据作为元素封装的数组。
io.lineno
使用gets方法、each_line方法逐行读取数据时,会自动记录读取的行数。这个行数可以通过lineno方法获取。
io.each_char
io.eache_char do |ch|
...
end
逐个字符地读取io中的数据并执行块,将读取的字符(String对象)作为块变量的传递
io.each_byte
逐个字节的读取io中的数据并启动块,将读取到的字节所对应的ASCII码以整数值的形式传递给块变量
io.getc
while ch = io.getc
...
end
只读取io中的一个字符。
io.ungetc(ch)
将参数ch指定的字符退回到io到输入缓冲区中.
>> File.open("hello.txt") do |io|
?> p io.getc
>> io.ungetc("h")
>> p io.gets
>> end
"H"
"hello,Ruby.\\n\n"
=> "hello,Ruby.\\n\n"
>> exit
shijianzhongdeMacBook-Pro:chapter_17 shijianzhong$ cat hello.txt
Hello,Ruby.\n
io.getbyte
只读取io中的一个字节,以整数对象返回与得到的字节相对应的ASCII码。数据全部读取完后,再读取时会返回nil
io.ungetbyte(byte)
将参数byte指定的一个字节退出到输入缓冲区中。
io.read(size)
读取参数size中指定的大小的数据。不指定大小时,会一次性读取全部数据并返回
File.open("hello.txt") do |io|
p io.read(5)
p io.read
end
输出操作
io.puts在字符串末尾添加换行符后输出。指定多个参数时,会分别添加换行符。如果参数为String类以外的对象,则会调用to_s方法,将其转换为字符串后再输出
irb(main):015:0> $stdout.puts "String", :Symbol, 1/100r String Symbol 1/100 => nil irb(main):016:0>
io.putc(ch)
输出参数ch指定的字符编码所对应的字符,参数为字符串时输出首字符
RR
shijianzhongdeMacBook-Pro:chapter_17 shijianzhong$ cat stduout_putc.rb
$stdout.putc(82)
$stdout.putc("Ruby")
$stdout.putc("\n")
剩下还有 io.print, io.printf, io.write
irb(main):001:0> size = $stdout.write("Hello.\n")
Hello.
=> 7
irb(main):002:0> p size
7
=> 7
io<<str
输出参数str指定的字符串。<<会返回接受者本身,因此可以像下面这样写
io << "foo" << "bar" << "baz"
文件指针
我们用文件指针(file pointer)或者当前文件偏移量(current file offset)来表示IO对象的文件的位置。
io.pos
io.pos =(position)
通过pos方法可以获得文件指针现在的位置。改变文件指针的位置用pos=方法
"Hello"
5
"Hello,Ruby.\n"
shijianzhongdeMacBook-Pro:chapter_17 shijianzhong$ cat io_pos.rb
File.open("hello.txt") do |io|
p io.read(5)
p io.pos
io.pos = 0 # 初始到开头
p io.gets
end
io.seek(offset, whence)
whence用于指定office如何移动
whence中指定的值
IO::SEEK_SET 将文件指针移动到offset指定的位置 那就相当于 io.pos
IO::SEEK_CUR 将offset视为相对于当前位置的偏移量位置来移动文件指针
IO::SEEK_END 将offset指定为行对于文件末尾的偏移文职
io.rewind
"Hello"
0
"Hello,Ruby.\n"
shijianzhongdeMacBook-Pro:chapter_17 shijianzhong$ cat io_rewind.rb
File.open("hello.txt") do |io|
p io.read(5)
p io.rewind # 返回到文件的初始位置 ,pos =0
p io.gets
end
shijianzhongdeMacBook-Pro:chapter_17 shijianzhong$
io.truncate(size)
按照参数size指定的大小截止文件。
io.truncate(0) # 将文件大小设为0
io.truncate(io.pos) # 删除当前文件指针以后的数据
二进制模式与文本模式
ruby默认会在各个平台对换行进行转换,如果不想进行转换,可以将状态切换成二进制模式
File.open("foo.txt", "w") do |io|
io.binmode # 进入二进制模式
io.write "Hello, world.\n"
end
缓冲
即使对IO对象进行写入,结果叶不能并不一定马上就反应再控制台或者文件中。再使用write、print等方法操作IO对象时,程序内部会开辟除一定的空间来保存临时生成的数据副本
这部分空间称为缓冲区。缓冲区里积累到一定量的数据后,就会进行输出处理,然后清空缓冲区
使用缓冲区进行数据处理称为缓冲
第1次: 0
第2次: 0
第3次: 0
结束后: 15
"aaaaaaaaaaaaaaa"
shijianzhongdeMacBook-Pro:chapter_17 shijianzhong$ cat test_buffering1.rb
filename = "buffering.txt"
File.open(filename, "w") do |file|
3.times do |i|
# 检查写入5次字节后的文件的大小
file.write("a" * 5)
puts "第#{i+1}次: #{File.size(filename)}"
end
end
puts "结束后: #{File.size(filename)}"
p File.read(filename)
这样的好处可以减少频繁的硬盘读写
io.flush
第1次: 5
第2次: 10
第3次: 15
结束后: 15
"aaaaaaaaaaaaaaa"
shijianzhongdeMacBook-Pro:chapter_17 shijianzhong$ cat test_buffering2.rb
filename = "buffering.txt"
File.open(filename, "w") do |file|
3.times do |i|
# 检查写入5次字节后的文件的大小
file.write("a" * 5)
file.flush # 立即输出缓存区的数据
puts "第#{i+1}次: #{File.size(filename)}"
end
end
puts "结束后: #{File.size(filename)}"
p File.read(filename)
shijianzhongdeMacBook-Pro:chapter_17 shijianzhong$
io.sync
io.sync=(state)
sync是syncchronize(同步)的意思。设定了这个就不需要io.flush
第1次: 5
第2次: 10
第3次: 15
结束后: 15
"aaaaaaaaaaaaaaa"
shijianongdeMBP:chapter_17 shijianzhong$ cat test_buffering3.rb
filename = "buffering.txt"
File.open(filename, "w") do |file|
file.sync = true
3.times do |i|
# 检查写入5次字节后的文件的大小
file.write("a" * 5)
puts "第#{i+1}次: #{File.size(filename)}"
end
end
puts "结束后: #{File.size(filename)}"
p File.read(filename)
shijianongdeMBP:chapter_17 shijianzhong$
与命令进行交互
IO.popen(command, mode)
参数 mode的使用方法与File.open方法是一样,参数缺省时默认是"r"模式。
用IO.popen方法生成的IO对象的输入/输出,会关联启动的命令command的标准输入/标准版输出。也就是说,IO对象的输出会作为命令的输入,命令的输出则会作为IO对象的输入
shijianongdeMBP:chapter_17 shijianzhong$ cat simple_grep_gz.rb
pattern = Regexp.new(ARGV[0])
filename = ARGV[1]
if /.gz$/ =~ filename
file = IO.popen("zcat #{filename}") # 指定命令输出io流
else
file = File.open(filnename)
end
file.each_line do |line| # 逐行读取文件
if pattern =~ line
print line
end
end
open("|command", mode)
将带有管道符号的命令传给open方法的效果与使用IO.pepon方法一样
finename = ARGV[0]
open("|zcat #{filename}") do |io|
io.each_line do |line|
print line
end
end
open-url库
通过require引用open-uri库后,我们可以像打开普通文件一样打开HTTP,FTP的URL。
shijianongdeMBP:chapter_17 shijianzhong$ cat read_url.rb
require "open-uri"
open("http://www.ruby-lang.org/zh_cn/") do |io|
puts io.read # 输出网页信息
end
# 通过FTP读取数据
filename = "ruby-2.3.0.tar.gz"
url = "ftp://www.ruby-lang.org/pub/ruby/2.3/#{filename}"
open(url) do |io|
File.binwrite(filename, io.read) # 写入文件
end
stringio库
StringIO就时用于模拟IO对象的修昂。通过require引用stringio库后,就可以使用stringIO对象了
"A\nB\nC\n"
shijianongdeMBP:chapter_17 shijianzhong$ cat stringio_puts.rb
require "stringio"
io = StringIO.new
io.puts("A")
io.puts("B")
io.puts("C")
io.rewind
p io.read # 进行输出操作
通过将字符串传递给StringIO.new方法的参数,就可以由字符串创建StringIO对象
rld writable dir /usr/local/opt/mysql@5.7/bin in PATH, mode 040777
"A\n"
"B\n"
"C\n"
shijianongdeMBP:chapter_17 shijianzhong$ cat stringio_gets.rb
require "stringio"
io = StringIO.new("A\nB\nC\n")
p io.gets
p io.gets
p io.gets
练习题
1 创建脚本,统计文本的行数,统计文本的单词数,统计文本的字符数
shijianzhongdeMacBook-Pro:exercises shijianzhong$ cat e1.rb def count_file(file) lines = File.readlines(file).size word_size = File.read(file).split.size character = File.read(file).size [lines, word_size, character] end p count_file(ARGV[0])
2 创建脚本,将文件中的行逆序排序,保留文件中的第一行数据
浙公网安备 33010602011771号