当前位置: 首页 > 工具软件 > Ruby In Steel > 使用案例 >

ruby way之Io之二

濮阳宁
2023-12-01
1 检测文件是否存在及其大小

FileTest的 exist?方法可以检测一个文件是否存在:

flag = FileTest::exist?("LochNessMonster")
flag = FileTest::exists?("UFO")
# exists? is a synonym for exist?


如果我们想要知道文件是否有内容,可以使用File::Stat的zero? 方法:

flag = File.new("somefile").stat.zero?


这个将会返回true,这是因为在ruby中0也是true,nil才是false.

所以我们可以使用size?方法:

if File.new("myfile").stat.size?
puts "The file has contents."
else
puts "The file is empty."
end


FileTest模块里面也有zero? 和size?方法:

flag1 = FileTest::zero?("file1")
flag2 = FileTest::size?("file2")


这里还有一个size方法:

size1 = File.size("file1")
size2 = File.stat("file2").size


2 检测特殊文件属性

这边要注意,File类mix了FIleTest模块,并且FileTest模块和File::Stat模块功能上也有很多重复.

unix/linux有面向字符和面向块的设备。FileTest的方法blockdev?和chardev?可以进行测试:

flag1 = FileTest::chardev?("/dev/hdisk0")  # false
flag2 = FileTest::blockdev?("/dev/hdisk0") # true


有时我们想要知道一个流是否联系到了终端,这时我们可以使用IO类的tty?方法:

flag1 = STDIN.tty?                   # true
flag2 = File.new("diskfile").isatty # false


一个流可以是一个管道,或者一个socket:

flag1 = FileTest::pipe?(myfile)
flag2 = FileTest::socket?(myfile)


要区分目录和普通文件我们这样使用:

file1 = File.new("/tmp")
file2 = File.new("/tmp/myfile")
test1 = file1.directory? # true
test2 = file1.file? # false
test3 = file2.directory? # false
test4 = file2.file? # true


File还有一个类方法ftype,他将返回流的类型.他也在File::Stat里面,只不过是实例方法.它的返回值可能是下面的字符

串(file、directory、blockSpecial、characterSpecial、fifo、link或socket).

this_kind = File.ftype("/dev/hdisk0")     # "blockSpecial"
that_kind = File.new("/tmp").stat.ftype # "directory"


要测试一个文件是否为另一个文件的链接,可以使用FileTest的symlink?方法,要计算链接数量,可以使用nlink方法:

File.symlink("yourfile","myfile")           # Make a link
is_sym = FileTest::symlink?("myfile") # true
hard_count = File.new("myfile").stat.nlink # 0


3 使用管道

ruby中使用IO.popen打开管道:

check = IO.popen("spell","r+")
check.puts("'T was brillig, and the slithy toves")
check.puts("Did gyre and gimble in the wabe.")
check.close_write
list = check.readlines
list.collect! { |x| x.chomp }
# list is now %w[brillig gimble gyre slithy toves wabe]


要注意 必须调用close_write,如果没有调用它,读取管道的时候,就不能到达文件的末尾.

下面是一个block的形式:

File.popen("/usr/games/fortune") do |pipe|
quote = pipe.gets
puts quote
# On a clean disk, you can seek forever. - Thomas Steel
end



如果指定了一个字符串"-",那么一个新的ruby实例将被创建.如果指定了一个block,那么这个block将会作为两个独立

的进程运行。子进程得到nil,父进程得到一个IO对象:

IO.popen("-") do |mypipe|
if mypipe
puts "I'm the parent: pid = #{Process.pid}"
listen = mypipe.gets
puts listen
else
puts "I'm the child: pid = #{Process.pid}"
end
end

# Prints:
# I'm the parent: pid = 10580
# I'm the child: pid = 10582


pipe方法也返回互相连接的一对管道:

pipe = IO.pipe
reader = pipe[0]
writer = pipe[1]

str = nil
thread1 = Thread.new(reader,writer) do |reader,writer|
# writer.close_write
str = reader.gets
reader.close
end

thread2 = Thread.new(reader,writer) do |reader,writer|
# reader.close_read
writer.puts("What hath God wrought?")
writer.close
end

thread1.join
thread2.join

puts str # What hath God wrought?


4 使用非阻塞IO

ruby会在后台执行一些操作,使io不会被阻断,因此大部分情况下可以使用ruby线程来管理IO,当一个线程被Io阻塞之

后,另外的线程能够继续执行.

由于ruby的线程不是一个native的线程,因此ruby的线程都在同一个进程里面.

如果你想关闭一个非阻塞io,你可以这样做:

require 'io/nonblock'

# ...

test = mysock.nonblock? # false

mysock.nonblock = true # turn off blocking
# ...
mysock.nonblock = false # turn on again

mysock.nonblock { some_operation(mysock) }
# Perform some_operation with nonblocking set to true

mysock.nonblock(false) { other_operation(mysock) }
# Perform other_operation with non-blocking set to false


5 使用readpartial

readpartial被设计来用于就像socket这样的流.

readpartial要求提供最大长度的参数,如果指定了buffer,那么这个buffer应指向用于存储数据的一个字符串。

data = sock.readpartial(128)  # Read at most 128 bytes


readpartial 方法,不能接受非阻塞的flag,他有时会阻塞:IO对象的buffer是空的;流的内容为空;流没有到达文件末尾



因此,如果流中还有数据的话,readpartial将不会阻塞.

如果流没有数据,并且他已经抵达文件的末尾,readpartial 将会立即抛出一个EOFError.

如果调用阻塞,他将会等待直到接收到数据或者得到一个EOF.

当sysread 调用在阻塞模式下,他的行为与readpartial相似.

6 操作路径名


先来看一下File.dirname和File.basename方法:

str = "/home/dave/podbay.rb"
dir = File.dirname(str) # "/home/dave"
file1 = File.basename(str) # "podbay.rb"
file2 = File.basename(str,".rb") # "podbay"


File.split方法,可以将一个文件的路径名和文件名分隔开:

info = File.split(str)        # ["/home/dave","podbay.rb"]


类方法expand_path 将一个相对路径,转换为一个绝对路径名:

Dir.chdir("/home/poole/personal/docs")
abs = File.expand_path("../../misc") # "/home/poole/misc"


对于打开的文件,path 将会返回这个文件的路径名:

file = File.new("../../foobar")

name = file.path # "../../foobar"


类方法类方法join正好和split相反:

path = File.join("usr","local","bin","someprog")


7使用Pathname

pathname类实际上是,Dir, File, FileTest,和FileUtils的包装器,它包含他们的很多功能:

require 'pathname'
path = Pathname.new("home/hal")
file = Pathname.new("file.txt")
p2 = path + file
path.directory? # true
path.file? # false
p2.directory? # false
p2.file? # true

puts parts = p2.split # [Pathname:/home/hal, Pathname:file.txt]
puts ext = p2.extname # .txt


再看看其他的有用的方法:

p1 = Pathname.new("//")           # odd but legal
p1.root? # true
p2 = Pathname.new("/home/poole")
p3 = p2.parent # Pathname:/home
items = p2.children # array of Pathnames (all files and
# dirs immediately under poole)


relative和absolute判断路径是否是相对的:

p1 = Pathname.new("/home/dave")
p1.absolute? # true
p1.relative? # false


8 Command-Level 文件操作

其实也就是copy, delete, rename,等等 些操作了:

File.delete("history")
File.unlink("toast")
File.rename("Ceylon","SriLanka")
File.link("/etc/hosts","/etc/hostfile") # hard link
File.symlink("/etc/hosts","/tmp/hosts") # symbolic link
File.truncate("myfile",1000) # Now at most 1000 bytes


fileUtils也有很多有用的方法
require "fileutils"
same = FileUtils.compare_file("alpha","beta") # true
# Copy epsilon to theta and log any errors.
FileUtils.copy("epsilon","theta", true)
FileUtils.move("/tmp/names","/etc") # Move to new directory
FileUtils.move("colours","colors") # Just a rename
FileUtils.safe_unlink("alpha","beta","gamma")
# Log errors on the next two files
FileUtils.safe_unlink("delta","epsilon",true)
FileUtils.install("foo.so","/usr/lib")


9 从键盘抓取输入

也就是抓取用户从键盘输入的字符。
unix平台:

def getchar
system("stty raw -echo") # Raw mode, no echo
char = STDIN.getc
system("stty -raw echo") # Reset terminal mode
char
end


windows平台:

require 'Win32API'

def getchar
char = Win32API.new("crtdll", "_getch", [], 'L').Call
end


10 读取整个文件到内存

读取整个文件到数组,你不需要打开文件,IO.readlines 可以完成这个工作,他自己会open和close.

arr = IO.readlines("myfile")
lines = arr.size
puts "myfile has #{lines} lines in it."

longest = arr.collect {|x| x.length}.max
puts "The longest line in it has #{longest} characters."


也可以用IO.read(它返回一个大的字符串):

str = IO.read("myfile")
bytes = arr.size
puts "myfile has #{bytes} bytes in it."

longest = str.collect {|x| x.length}.max # strings are enumerable!
puts "The longest line in it has #{longest} characters."


由于File继承了IO,因此File也有这两个方法.

11 逐行迭代一个文件

我们可以使用IO.foreach 方法,或者each方法,如果是前者文件不需要显示打开:

# Print all lines containing the word "target"
IO.foreach("somefile") do |line|
puts line if line =~ /target/
end

# Another way...
file = File.new("somefile")
file.each do |line|
puts line if line =~ /target/
end


12逐字节对文件进行遍历

可以使用each_byte方法,如果你想要转换byte到字符的话使用chr方法:

file = File.new("myfile")
e_count = 0
file.each_byte do |byte|
e_count += 1 if byte == ?e
end


12 把字符串当文件来用

我们可以使用stringio库:

require 'stringio'

ios = StringIO.new("abcdefghijkl\nABC\n123")

ios.seek(5)
ios.puts("xyz")

puts ios.tell # 8

puts ios.string.dump # "abcdexyzijkl\nABC\n123"

c = ios.getc
puts "c = #{c}" # c = 105

ios.ungetc(?w)

puts ios.string.dump # "abcdexyzwjkl\nABC\n123"

puts "Ptr = #{ios.tell}"

s1 = ios.gets # "wjkl"
s2 = ios.gets # "ABC"


13读取嵌套在程序中的数据

ruby中程序末尾的__END__ 标记说明,下面的数据是程序内嵌的数据,你可以使用一个IO对象DATA来读取。

# Print each line backwards...
DATA.each_line do |line|
puts line.reverse
end
__END__
A man, a plan, a canal... Panama!
Madam, I'm Adam.
,siht daer nac uoy fI
.drah oot gnikrow neeb ev'uoy


14 读取程序源码

DATA指向__END__ 后面的数据,如果你调用rewind,它将会将文件指针指向程序的开头:

DATA.rewind
num = 1
DATA.each_line do |line|
puts "#{'%03d' % num} #{line}"
num += 1
end
__END__


15 操作临时文件

这里使用Tempfile库:

require "tempfile"

temp = Tempfile.new("stuff")
name = temp.path # "/tmp/stuff17060.0"
temp.puts "Kilroy was here"
temp.close

# Later...
temp.open
str = temp.gets # "Kilroy was here"
temp.close(true) # 立即删除


16 改变和设置当前目录

得到当前的目录可以使用Dir.pwd或他的别名Dir.getwd来获得.

Dir.chdir 用来改变当前目录:

Dir.chdir("/var/tmp")

puts Dir.pwd # "/var/tmp"

puts Dir.getwd # "/var/tmp"


这个还能接受一个block,接受block说明,block里的代码都是在改变了的目录下进行的:

Dir.chdir("/home")
Dir.chdir("/tmp") do
puts Dir.pwd # /tmp
# other code...
end
puts Dir.pwd # /home


17改变当前根目录

Dir.chdir("/home/guy/sandbox/tmp")

Dir.chroot("/home/guy/sandbox")

puts Dir.pwd # "/tmp"


18 遍历一个目录

Dir.foreach("/tmp") { |entry| puts entry }

dir = Dir.new("/tmp")
dir.each { |entry| puts entry }


19 创建一个目录链

在linux使用mkdir -p来做,在ruby中我么可以这么做:

require "fileutils"

FileUtils.makedirs("/tmp/these/dirs/need/not/exist")


20 递归的删除目录

在linux下我们能够使用rm -rf ...来做,在ruby中我们能这么做:

require 'pathname'

dir = Pathname.new("/home/poole/")

dir.rmtree

# or:

require 'fileutils'

FileUtils.rm_r("/home/poole")


21 查找文件和目录

使用find库来做:

require "find"

def findfiles(dir, name)
list = []
Find.find(dir) do |path|
Find.prune if [".",".."].include? path
case name
when String
list << path if File.basename(path) == name
when Regexp
list << path if File.basename(path) =~ name
else
raise ArgumentError
end
end
list
end

findfiles "/home/hal", "toc.txt"
# ["/home/hal/docs/toc.txt", "/home/hal/misc/toc.txt"]

findfiles "/home", /^[a-z]+.doc/
# ["/home/hal/docs/alpha.doc", "/home/guy/guide.doc",
# "/home/bill/help/readme.doc"]
 类似资料: