《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 创建脚本,将文件中的行逆序排序,保留文件中的第一行数据

 

posted @ 2020-06-09 00:22  就是想学习  阅读(212)  评论(0编辑  收藏  举报