Ruby treats files and I/O operations as Object oriented.
Ruby's I/O system
The IO class handles all input and output streams.
IO class
An IO object represents a readable writable connection to a disk file, keyboard, screen, or device.
These constants are automatically set Stderr,stdin,stdout after the program is started. STD indicates that Standard,err is Error,in input,out is Output.
The standard input, output, and error streams are encapsulated into IO instances. Do an experiment:
>> Stderr.class
=> IO
>> stderr.puts ("problem!")
problem!
=> Nil
>> stderr.write ("problem!\n")
problem!
=> 9
STDERR is an IO object. If an Io object is open to write, you can invoke puts on it, and what you want to puts will be written to the output stream of the Io object. IO objects also have the print and write methods. Something written to an IO object does not automatically add a newline character, and the value returned is the number of bytes to write.
As an enumerable IO object
To enumerate, you must have a each method so that you can iterate. The iteration IO object is based on the $/variable. The default value for this variable is a newline character: \ n
>> Stdin.each {|line| P line}
This is Line 1
"This is line 1\n"
This is Line 2
"This is line 2\n"
All separated by $/, which is a newline character
"All separated by $/, which is a newline character\n"
Change the value of the global variable $/:
>> $/= "NEXT"
=> "NEXT"
>> Stdin.each {|line| P line}
The
NEXT
"The Line\nnext"
Next Line
where "line" really means
Until we ... NEXT
"\nnext line\nwhere \ line\" Really means\nuntil we ... NEXT "
$/determines how each of the IO objects. Because IO can be enumerated, you can perform other enumeration operations on it.
>> Stdin.select {|line| line =~/\a[a-z]/}
We ' re only interested in
lines that begin with
Uppercase letters
^d
=> ["We ' re only interested in\n", "uppercase letters\n"]
>> stdin.map {|line| Line.reverse}
Senil eseht
Terces a Niatnoc
. Egassem
^d
=> ["\nthese lines", "\ncontain a Secret", "\nmessage."]
Stdin,stdout,stderr
Ruby thinks all input comes from the keyboard, and all output is put to the terminal. Puts,gets will operate on STDOUT and STDIN.
If you want to use STDERR as output, you have to make it clear:
If broken?
Stderr.puts "There ' s a problem!"
End
In addition to these three constants, Ruby also provides three global variables: $stdin, $stdout, $stderr.
Standard I/O global variables
The main difference between STDIN and $stdin is that you can't reassign values to constants, but you can reassign values for variables. Variable allows you to modify the default I/O flow behavior without affecting the original stream.
For example, you want to put the output into a file containing standard out and standard error. Save the following code in a RB file:
Record = File.Open ("./tmp/record", "W")
Old_stdout = $stdout
$stdout = Record
$stderr = $stdout
Puts "This are a record"
z = 10/0
The first page is to open the file you want to write, and then save the current $stdout to a variable, redefine the $stdout, and let it be the record. The $stderr is set to make it equal to $stdout. Now, any puts results will be written to the/tmp/record file, because the puts will output to the $stdout. $stderr output is also placed in the file, because we also assign $stderr to the file handle.
Create a Tmp/record file in the project's directory, then run it, and then open the record file to see:
This is a record
Demo.rb:6:in '/': divided by 0 (zerodivisionerror)
From Demo.rb:6:in ' <main> '
Global variables allow you to control where the flow is going.
Keyboard input
Most keyboard input is done with gets and getc. Gets returns the line entered, GETC returns a character. Gets requires you to explicitly give the output a name.
Line = gets
char = stdin.getc
The input will be cached and you must press ENTER.
For some reason, you set $stdin to something other than the keyboard, you can still use stdin as the recipient of gets to read the keyboard input:
line = Stdin.gets
File Operation Basics
Ruby's built-in file class can process files. File is a subclass of Io, so it can share some of the properties of Io objects, but the file class adds and modifies certain behaviors.
Read files
We can read one byte of a file at a time, or we can specify the number of bytes to read each time, or we can read a row at a time, and the row is distinguished by the value of the $/variable.
The easiest way to create a file object is to use File.new to give the file name to the constructor, assuming that the file being read already exists and we get an open read file handle.
Create a file, the name is TICKET2.RB, put it under the code directory:
Class Ticket
Def initialize (venue, date)
@venue = Venue
@date = Date
End
def price= (Price)
@price = Price
End
def venue
@venue
End
def date
@date
End
def Price
@price
End
End
Try this:
>> f = file.new ("code/ticket2.rb")
=> #<file:code/ticket2.rb>
Use a file instance to read a file. The Read method reads the contents of the entire file:
>> F.read
=> "Class ticket\n Def initialize (venue, date) \ n @venue = venue\n @date = date\n end\n\n def price= (price) \ n @price = P rice\n end\n\n def venue\n @venue \ end\n\n def date\n @date \ end\n\n def price\n @price \ n end\nend\n "
Read Line-based file
Read the next line with the Gets method:
>> f = file.new ("code/ticket2.rb")
=> #<file:code/ticket2.rb>
>> f.gets
=> "Class ticket\n"
>> f.gets
=> "Def initialize (venue, date) \ n"
>> f.gets
=> "@venue = venue\n"
ReadLine and gets the same line can read the file, different places is to the end of the file, gets return Nil,readline will error.
Try this again:
>> F.read
=> "@date = date\n end\n\n def price= (price) \ n @price = price\n end\n\n def venue\n @venue \ n end\n\n def date\n @date \ n end\n\n def price\n @price \ end\nend\n "
>> f.gets
=> Nil
>> F.readline
Eoferror:end of File reached
From (IRB): 14:in ' ReadLine '
From (IRB): 14
From/usr/local/bin/irb:11:in ' <main> '
With ReadLines, you can read all the rows of the entire file and put them in an array. Rewind can move the internal position pointer of a file object to the beginning of the document:
>> F.rewind
=> 0
>> F.readlines
=> ["Class ticket\n", "Def Initialize (venue, date) \ n", "@venue = venue\n", "@date = date\n", "end\n", "\ n", "Def P rice= (price) \ n "," @price = price\n "," end\n "," \ n "," Def venue\n "," @venue \ n "," end\n "," \ n "," Def date\n "," @date \ N "," end\n "," \ n "," Def price\n "," @price \ n "," end\n "," end\n "]
The File object can be enumerated. Instead of reading the entire file into memory, we can use each line of reading:
>> F.each {|line| puts "next line: #{line}"}
Next line: Class Ticket
Next line: Def initialize (venue, date)
Reading byte and character-based files
The Getc method reads and returns a character of the file:
>> f.getc
=> "C"
UNGETC:
>> f.getc
=> "C"
>> f.ungetc ("X")
=> Nil
>> f.gets
=> "Xlass ticket\n"
GetByte method. A character is represented by one or more bytes, depending on the encoding of the character.
>> f.getc
=> Nil
>> F.readchar
Eoferror:end of File reached
>> F.getbyte
=> Nil
>> F.readbyte
Eoferror:end of File reached
Retrieving and querying file locations
The Pos property and Seek method of the file object can change the position of the internal pointer.
Pos
>> F.rewind
=> 0
>> F.pos
=> 0
>> f.gets
=> "Class ticket\n"
>> F.pos
=> 13
Position the pointer at the specified location:
>> F.pos = 10
=> 10
>> f.gets
=> "et\n"
Seek
The Seek method can move the position pointer of a file to a new place.
F.seek (Io::seek_set)
F.seek (Io::seek_cur)
F.seek ( -10, Io::seek_end)
The first line retrieves 20 bytes. The second line retrieves 15 bytes from the current position. The third line checks the end of the file to 10 bytes ahead. Io::seek_set is optional and can be f.seek directly, F.pos = 20.
Read files using the File class method
File.read and File.readlines.
Full_text = File.read ("MyFile.txt")
Lines_of_text = File.readlines ("MyFile.txt")
The first line gets a string that contains the entire contents of the file. The second line gets an array of items inside each line of the file. These two methods automatically open and close the file.
Write a file
Puts,print,write. W represents the Write mode of the file, which is used as the second parameter of the file.new to create the file, and if the file already exists, it will overwrite the contents. A indicates an append mode, the file does not exist, and a file is created using append mode.
An experiment would make it clear:
>> f = file.new ("Data.out", "W")
=> #<file:data.out>
>> f.puts "It's hard to meet, too."
=> Nil
>> F.close
=> Nil
>> puts File.read ("Data.out")
It's hard not to meet
=> Nil
>> f = file.new ("Data.out", "a")
=> #<file:data.out>
>> f.puts "East wind is powerless and flowers are disabled"
=> Nil
>> F.close
=> Nil
>> puts File.read ("Data.out")
It's hard not to meet
The east wind is powerless and the flowers are disabled
=> Nil
Scope of code block partitioning file operations
Using File.new to create a file object It's a bit bad that you have to turn off the file yourself. Alternatively, you can use File.Open and give it a block of code. A code block can receive a File object as its unique parameter. When the code block is finished, the file object is automatically closed.
First create a file, the name is Records.txt, the content is:
Pablo casals| catalan|cello|1876-1973
Jascha heifetz| russian-american|violin|1901-1988
Emanuel feuermann| austrian-american|cello|1902-1942
The following code is placed in a RB file:
File.Open ("Records.txt") do |f|
While record = F.gets
Name, nationality, instrument, dates = record.chomp.split (' | ')
Puts "#{name}" (#{dates}), who is #{nationality},
Played #{instrument}. "
End
End
The results of the execution are:
Pablo Casals (1876-1973), who was Catalan,
Played cello.
Jascha Heifetz (1901-1988), who was Russian-american,
Played violin.
Emanuel Feuermann (1902-1942), who was Austrian-american,
Played cello.
The enumerable of files
Use each instead of while:
File.Open ("Records.txt") do |f|
F.each do |record|
Name, nationality, instrument, dates = record.chomp.split (' | ')
Puts "#{name}" (#{dates}), who is #{nationality},
Played #{instrument}. "
End
End
Experiment:
# Sample record in Members.txt:
# David Black male 55
Count = 0
Total_ages = File.readlines ("Members.txt"). Inject (0) do |total,line|
Count + 1
Fields = Line.split
Age = Fields[3].to_i
Total + Age
End
Puts "Average age of Group: #{total_ages/count}."
Experiment:
Count = 0
Total_ages = File.Open ("Members.txt") do |f|
F.inject (0) do |total,line|
Count + 1
Fields = Line.split
Age = Fields[3].to_i
Total + Age
End
End
Puts "Average age of Group: #{total_ages/count}."
File I/O Exceptions and errors
File-related errors are generally under the Errno namespace: errno::eacces, permissions. Errno::enoent,no such entity, no files or directories. Errno::eisdir, directories, open things are not files but directories.
>> File.Open ("No_file_with_this_name")
Errno::enoent:no such file or directory @ Rb_sysopen-no_file_with_this_name
From (IRB): 23:in ' Initialize '
From (IRB): 23:in ' open '
From (IRB): 23
From/usr/local/bin/irb:11:in ' <main> '
>> f = File.Open ("/tmp")
=> #<file:/tmp>
>> f.gets
Errno::eisdir:is a directory @ io_fillbuf-fd:10/tmp
From (IRB): 25:in ' gets '
From (IRB): 25
From/usr/local/bin/irb:11:in ' <main> '
>> File.Open ("/var/root")
Errno::eacces:permission denied @ rb_sysopen-/var/root
From (IRB): 26:in ' Initialize '
From (IRB): 26:in ' open '
From (IRB): 26
From/usr/local/bin/irb:11:in ' <main> '
Querying IO and File objects
The IO class provides some query methods, and the File class adds some.
Get information from the File class and the Filetest module
The query method provided by file and Filetest lets you know a lot about file information.
Whether the file exists
>> filetest.exist? ("/usr/local/src/ruby/readme")
=> false
Directory? File? or a shortcut?
Filetest.directory? ("/home/users/dblack/info")
Filetest.file? ("/home/users/dblack/info")
Filetest.symlink? ("/home/users/dblack/info")
Blockdev?,pipe?,chardev?,socket?
Can read? Can write? Executable?
Filetest.readable? ("/tmp")
Filetest.writable? ("/tmp")
Filetest.executable? ("/home/users/dblack/setup")
How big is the file?
Filetest.size ("/home/users/dblack/setup")
Filetest.zero? ("/tmp/tempfile")
File::stat
Two ways:
>> file::stat.new ("code/ticket2.rb")
=> #<file::stat dev=0x1000002, ino=234708237, mode=0100644, Nlink=1, uid=501, gid=20, rdev=0x0, size=223, blksize= 4096, blocks=8, atime=2016-09-14 14:42:03 +0800, mtime=2016-09-14 14:16:29 +0800, ctime=2016-09-14 14:16:29 +0800, birtht Ime=2016-09-14 14:16:28 +0800>
>> File.Open ("code/ticket2.rb") {|f| F.stat}
=> #<file::stat dev=0x1000002, ino=234708237, mode=0100644, Nlink=1, uid=501, gid=20, rdev=0x0, size=223, blksize= 4096, blocks=8, atime=2016-09-14 14:42:03 +0800, mtime=2016-09-14 14:16:29 +0800, ctime=2016-09-14 14:16:29 +0800, birtht Ime=2016-09-14 14:16:28 +0800>
>>
Handling directories with the Dir class
>> d = dir.new ("./node_modules/mocha")
=> #<dir:./node_modules/mocha>
Reading directories
Entries method, or Glob (does not show hidden entries).
Entries method
>> d.entries
=> [".", "...", "Bin", "Bower.json", "Browser-entry.js", "changelog.md", "Images", "Index.js", "Lib", "LICENSE", "Mocha . css "," Mocha.js "," Package.json "," readme.md "]
Or use the class method:
>> dir.entries ("./node_modules/mocha")
=> [".", "...", "Bin", "Bower.json", "Browser-entry.js", "changelog.md", "Images", "Index.js", "Lib", "LICENSE", "Mocha . css "," Mocha.js "," Package.json "," readme.md "]
File size, does not contain hidden files, is a point of the beginning of the file, put the following code into a file and then execute:
D = dir.new ("./node_modules/mocha")
Entries = D.entries
entries.delete_if {|entry| entry =~/^\./}
entries.map! {|entry| File.join (D.path, entry)}
entries.delete_if {|entry|! File.file? (Entry)}
Print "Total Bytes:"
Puts Entries.inject (0) {|total, entry| Total + file.size (entry)}
Results:
Total bytes:520610
Glob
Can do something like this:
LS *.js
RM *.? Xt
For f in [a-z]*
* represents any number of characters,? represents an arbitrary character.
Using Dir.glob and dir.[], the square bracket version of the method allows you to use the index-style syntax:
>> dir["Node_modules/mocha/*.js"]
=> ["Node_modules/mocha/browser-entry.js", "Node_modules/mocha/index.js", "Node_modules/mocha/mocha.js"]
The Glob method can add one or more tag parameters to control some behavior:
Dir.glob ("info*") # []
Dir.glob ("info", File::fnm_casefold # ["Info", "Information"]
Fnm_dotmatch, a file that contains a point at the beginning of the result.
Use two tags:
>> Dir.glob ("*info*")
=> []
>> Dir.glob ("*info*", File::fnm_dotmatch)
=> [". Information"]
>> Dir.glob ("*info*", File::fnm_dotmatch | File::fnm_casefold)
=> [". Information", ". Info", "info"]
Processing and querying the directory
mkdir: Create directory, ChDir: Change working directory, rmdir: Delete directory.
Newdir = "/tmp/newdir"
NewFile = "NewFile"
Dir.mkdir (Newdir)
Dir.chdir (Newdir) do
File.Open (NewFile, "w") do |f|
F.puts "Demo file in a new directory"
End
Puts "current directory: #{dir.pwd}"
Puts "list:"
P Dir.entries (".")
File.unlink (NewFile)
End
Dir.rmdir (Newdir)
Does the print "#{newdir}" still exist? "
If File.exist? (Newdir)
Puts "yes"
Else
Puts "no"
End
The result:
Current directory:/private/tmp/newdir
List:
[".", "..", "NewFile"]
Does/tmp/newdir still exist? No
File tools in the standard library
FileUtils Module
Copy, move, delete file
>> require ' fileutils '
=> true
>> FILEUTILS.CP ("demo.rb", "Demo.rb.bak")
=> Nil
>> Fileutils.mkdir ("Backup")
=> ["Backup"]
>> FILEUTILS.CP (["Demo.rb.bak"], "Backup")
=> ["Demo.rb.bak"]
>> dir["backup/*"]
=> ["Backup/demo.rb.bak"]
Fileutils.mv
Fileutils.rm
Fileutils.rm_rf
Dryrun
FileUtils::D RYRUN.RM_RF
Fileutils::nowrite.rm
Pathname class
>> require ' pathname '
=> true
>> Path = pathname.new ("/users/xiaoxue/desktop/test1.rb")
=> #<pathname:/users/xiaoxue/desktop/test1.rb>
BaseName
>> Path.basename
=> #<pathname:test1.rb>
>> puts Path.basename
Test1.rb
=> Nil
DirName
>> Path.dirname
=> #<pathname:/users/xiaoxue/desktop>
Extname
>> Path.extname
=> ". RB"
Ascend
>> Path.ascend do |dir|
?> puts "Next Level up: #{dir}"
>> End
Next Level Up:/users/xiaoxue/desktop/test1.rb
Next Level Up:/users/xiaoxue/desktop
Next Level Up:/users/xiaoxue
Next Level Up:/users
Next Level Up:/
=> Nil
>> Path = pathname.new ("/users/xiaoxue/desktop/test1.rb")
=> #<pathname:/users/xiaoxue/desktop/test1.rb>
>> Path.ascend do |dir|
?> puts "ascended to #{dir.basename}"
>> End
ascended to TEST1.RB
ascended to Desktop
ascended to Xiaoxue
ascended to Users
ascended to/
=> Nil
Stringio class
Take the string as an IO object. Retrieve, Rewind ...
For example, you have a module that cancels the annotation in the file, reads the file, and writes it to another file in addition to the contents of the annotation:
Module Decommenter
def self.decomment (infile, outfile, comment_re =/\a\s*#/)
Infile.each do |inline|
Outfile.print inline unless inline =~ comment_re
End
End
End
Decommenter.decomment requires two open file handles, one to read and one to write. The regular expression determines whether each row entered is a comment. Rows that are not annotations are written to the output file.
How to use:
File.Open ("myprogram.rb") do |inf|
File.Open ("Myprogram.rb.out", "w") do |outf|
Decommenter.decomment (INF, OUTF)
End
End
Using True file tests
You want to use the input and output of the real file test file, you can use the Ruby tempfile class.
Require ' tempfile '
To create a temporary file:
tf = tempfile.new ("My_temp_file").
Require ' Stringio '
Require_relative ' Decommenter '
String <<eom
# This is comment.
This isn't a comment.
# this is.
# so are this.
This isn't also not a comment.
EOM
infile = Stringio.new (String)
outfile = Stringio.new ("")
Decommenter.decomment (infile, outfile)
Puts "test succeeded" if outfile.string = = <<eom
This isn't a comment.
This isn't also not a comment.
EOM
Open-uri Library
Use Http,https to get information.
Require ' Open-uri '
Rubypage = open ("http://rubycentral.org")
Puts Rubypage.gets