Basic Input and Output

一开始时你会以为 Ruby 提供了两组独立的 IO
例程。第一种是基本接口,目前为止我们一直都是使用它。

print "Enter your name: "
name = gets

Kernel 模块中实现了一组完整的 IO 相关方法,比如
getopenprintprintfputcputsreadlinereadlines
test,这些方法使得编写 Ruby
程序更加方便和简洁。这些方法通常对标准输入输出进行 I/O
操作,这有利于它们编写过滤器。你可以从 411 页开始查看文档。

第二种方式就是通过 IO 对象完成很多自定义的控制。

IO 对象是什么?

Ruby 定义了单独一个基础类—— IO,它可以操作输入输出。File
BasicSocket
都是它的子类,并在它的基础上实现了很多个性化的操作,不过原则是不变的。一个
IO 对象就是一个在 Ruby
程序和外部资源之间的双向通道。(对于那些想了解实现细节的人来说,这个双向通道意味着一个单独的
IO
对象有时需要管理不止一个操作系统的文件描述符。比如,如果你开启了一对管道,那一个
IO 对象就包含一个读管道和一个写管道。)也就是说一个 IO
对象比你看到的包含更多,不过在最后你可以轻松的写入其中或从其中读取。

在这章中,我们会专注于 IO 类以及它比较常用的子类—— File
类。对于在网络中使用
socket 类的细节可以看从 469 页开始的部分。

打开和关闭文件

你也许希望可以用 File.new 创建新的文件对象。

aFile = File.new("testfile", "r")

# ... process the file

aFile.close

你通过只读,只写或读写的方式创建 File
对象,不同的创建文件模式根据模式字符串决定(例子中我们是通过 r
确定以只读模式打开 testfile 文件的)。可使用的模式列表在
326
页。当创建文件时你也可以随意指定它的权限,关于这里的细节可以查阅 303
页的内容。在打开文件后你便可以操作它,可以读或写自己需要的数据。最后,出于对软件的负责我们还应该关闭文件,以确认缓冲数据都已经完全写入文件并且相关资源都得到释放。

不过 Ruby 也可以使你的生活更加简单。File.open
也可以打开文件。在常规使用上它与 File.new
的行为相似。但是,如果有关联代码块需要调用的话 open
会有些区别。与返回新的 File
对象不同,它将调用代码块并以刚打开的 File
作为参数。当代码块退出时文件也将自动关闭。

File.open("testfile", "r") do |aFile|

# ... process the file

end

读写文件

与基本的 I/O 类似的方法对文件对象也是有用的。所以,gets
是从标准输入读取一行内容,aFile.gets 也是从文件对象 aFile
中读取一行内容。

不过 I/O
对象喜欢一组额外的访问方法,这些方法的目的是使我们的工作更加轻松。

迭代器读取文件

和用常规循环从 IO 流中读取数据一样,你也可以使用多种 Ruby
的迭代器。IO#each_byte 会从 IO 对象中连续读取 8
位字节并调用代码块。

aFile = File.new("testfile")
aFile.each_byte {|ch| putc ch; putc ?. }

结果是:

T.h.i.s. .i.s. .l.i.n.e. .o.n.e.
.T.h.i.s. .i.s. .l.i.n.e. .t.w.o.
.T.h.i.s. .i.s. .l.i.n.e. .t.h.r.e.e.
.A.n.d. .s.o. .o.n.......
.

IO#each_line
从文件中读取下一行内容并调用代码块。下面的例子中我们会用 String#dump
让原始的换行符可见,所以你会知道我们并没有说谎。

aFile.each_line {|line| puts "Got #{line.dump}" }

结果是:

Got "This is line one\n"
Got "This is line two\n"
Got "This is line three\n"
Got "And so on...\n"

你可以将任意字符串作为行分隔符向 each_line
传参,转入内容也会按照相应的分隔符进行分隔,最后还会将每行数据至结束字符间的内容返回。这就是你可以在上个例子中看见
「\n」输出的原因。下面的例子中,我们要用
「e」作为行分隔符。

aFile.each_line("e") do |line|
  puts "Got #{ line.dump }"
end

结果是:

Got "This is line"
Got " one"
Got "\nThis is line"
Got " two\nThis is line"
Got " thre"
Got "e"
Got "\nAnd so on...\n"

如果你结合迭代器和使用代码块时文件会自动关闭的特点就会产生
IO.foreach。这个方法需要将 I/O
资源的名称作为入参,将其打开并调用迭代器对文件的每行内容读取,再自动关闭文件。

IO.foreach("testfile") { |line| puts line }

结果是:

This is line one
This is line two
This is line three
And so on...

或者如果你喜欢,你也可以将文件内容收集为行的数组。

arr = IO.readlines("testfile")
arr.length             »4
arr[0]                 »"This is line one\n"

不要忘记 I/O
在非必然世界中不一定有必然的结果,有时许多错误情况都会导致异常,你应该为捕捉它们并做出合适的反应作好准备。

写入文件

我们所写的例子中几乎都调用了 putsprint
方法,并且传递任意的对象,然后只能依赖 Ruby
会做正确的行为(当然,它确实也做到了)。不过它是怎么准确做到的呢?

答案非常简单。除了少量的例外情况,每个传递给 putsprint
的对象都是通过调用对象的 to_s
方法转换为字符串的。如果由于某些原因 to_s
方法没有返回有效字符串,将会创建一个包含对象类名及 id
的字符串返回,像 <ClassName:0x123456> 这样。

异常情况也比较常见。nil
对象会作为字符串「nil」打印,传参给 puts
的数组将对其中的每个元素分别打印。

如果你想输出二进制数据又不希望 Ruby
弄乱它,需要怎么做?一般情况下你可以用 IO#print
并将包含字节码的字符串传入。不过你也可以通过低级别的输入输出例程完成此功能,可以查阅
335 页的 IO#sysreadIO#syswrite 方法。

首先你要怎样将二进制数据转换为字符串呢?有两个类似功能的方法,一种是将字节码按字节码的方式输入,另一种是通过
Array#pack 方法。

str = ""                     »""
str << 1 << 2 << 3           »"\001\002\003"
[ 4,str 5, 6 ].pack("c*")    »"\004\005\006"

但我忘记了 C++ 输入输出流

有时个人品味是没法解释的。不过,既然可以通过 <<
操作符将对象附加至 Array,也可以将对象到输出的 IO 流中。

endl = "\n"
$stdout << 99 << " red balloons" << endl

结果是:

99 red balloons

再说明一下,<< 方法会在将参数传递之前通过它们的 to_s
方法将它们转换为字符串。

与网络交流

无论是低级别还是高级别的网络协议的使用上,Ruby
都表现出了流畅性。

对于喜欢在网络级别上钻研的人,Ruby 为他们在 socket
库中准备了一系列相关的类。这些类可以帮助你访问 TCP,UDP,SOCKS
及 Unix
主要网络接口,以及架构上支持的任何网络接口类型。库中也提供了帮助类,可以使用帮助类很方便地编写服务器。下面是一个简单的例子,我们通过
finger 协议从本地机器上获得关于「oracle」用户的相关信息。

require 'socket'
client = TCPSocket.open('localhost', 'finger')
client.send("oracle\n", 0)    # 0 means standard packet
puts client.readlines
client.close

结果是:

Login: oracle         Name: Oracle installation
Directory: /home/oracle             Shell: /bin/bash
Never logged in.in
No Mail.
No Plan.

在更高层中,net 库模块提供了相关的处理类对应用层协议(一般有 FTP, HTTP,
POP, SMTP, 和网络)进行操作。相关文档可从 482
页开始查阅。例如,下面的程序将列举 Pragmatic Programmer
主页显示的图片。

require 'net/http'

h = Net::HTTP.new('www.pragmaticprogrammer.com', 80)
resp, data = h.get('/index.html', nil)
if resp.message == "OK"
  data.scan(/<img src="(.*?)"/) { |x| puts x }
end

结果是:

images/title_main.gif
images/dot.gif
images/dot.gif
images/dot.gif
images/aafounders_70.jpg
images/pp_cover_thumb.png
images/ruby_cover_thumb.png
images/dot.gif
images/dot.gif

本文翻译自《Programming Ruby》,主要目的是自己学习使用,文中翻译不到位之处烦请指正,如需转载请注明出处

本章原文为 Basic Input and
Output

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值