Chapter 2
This chapter covers working with files and directories from Eiffel. Listing files and directories, copying, renaming and deleting files. Using features from the {DIRECTORY}, {FILE} and {PATH}
classes.
Suppose we have the following directory structure
dir
sub_dir
src
controller
persitence
mysql
sqlite
test
a.txt
b.text
entries (a_dir: DIRECTORY)
-- Show entires for the current directory `a_dir'.
do
print ("%N======== Files and Directories ===========%N")
across
a_dir.entries as ic
loop
print (ic.item.name)
io.put_new_line
end
end
The feature {DIRECTORY}.entries
, it's a query that returns a collections of PATH's, and the feature entries
prints both files and directories.
======== Files and Directories ===========
.
..
a.txt
b.txt
sub_dir
each_dir (a_dir: DIRECTORY)
-- Show only directories for the current directory `a_dir'.
local
file: FILE
do
print ("%N========Directories ===========%N")
across
a_dir.entries as ic
loop
create {RAW_FILE} file.make_with_path (a_dir.path.extended_path (ic.item))
if file.is_directory then
print (ic.item.name)
io.put_new_line
end
end
end
To limit our output to directories, we create a file and use the {FILE}.is_directory: BOOLEAN
query, for each entry.
========Directories ===========
.
..
sub_dir
each_file (a_dir: DIRECTORY)
-- -- Show only files for the current directory `a_dir'.
local
file: FILE
do
print ("%N========Files ===========%N")
across
a_dir.entries as ic
loop
create {RAW_FILE} file.make_with_path (a_dir.path.extended_path (ic.item))
if not file.is_directory then
print (ic.item.name)
io.put_new_line
end
end
end
========Files ===========
a.txt
b.txt
each_recursive_dir (a_dir: DIRECTORY)
-- Show how to traverse the entire directory tree starting at directory `a_dir'.
local
dir: DIRECTORY
entry: PATH
filename: READABLE_STRING_32
do
across
a_dir.entries as ic
loop
entry := ic.item
if not (entry.is_current_symbol or else entry.is_parent_symbol) then
create dir.make_with_path (a_dir.path.extended_path (entry))
if dir.exists then
print (dir.path.name)
io.put_new_line
each_recursive_dir (dir)
end
end
end
end
========Recursive Directories ===========
.\dir\sub_dir
.\dir\sub_dir\src
.\dir\sub_dir\src\controller
.\dir\sub_dir\src\persistence
.\dir\sub_dir\src\persistence\mysql
.\dir\sub_dir\src\persistence\sqlite
.\dir\sub_dir\test
For binary files you can use:
{RAW_FILE}
: Files, viewed as persistent sequences of bytes.
{PLAIN_TEXT_FILE}
:Files viewed as persistent sequences of ASCII characters.
read_file_line_by_line (a_path: PATH)
-- Show how to read a file line by line.
-- For binary files you can use {RAW_FILE}.
local
file: PLAIN_TEXT_FILE
do
print ("============= Read file line by line ====================")
create file.make_with_path (a_path)
if file.exists and then file.is_readable then
file.open_read
from
until
file.end_of_file
loop
file.read_line
print (file.last_string)
io.put_new_line
end
file.close
else
io.error.put_string ("Could not read, the file:[" + a_path.name + " ] does not exist")
io.put_new_line
end
end
The previous code read the file, indentified by a path `a_path', if it exists, line by line and printing them into the output standard.
It's very useful being able to read in the entire contents of a file using a single feature `{FILE}.read_stream (nb_char: INTEGER)
', but we need to know the size of the file.
{FILE}.read_stream (nb_chr: INTEGER)
-- Read a string of at most `nb_char' bound characters
-- or until end of file.
-- Make result available in `last_string'.
`
The trick is to call the feature {FILE}.count:INTEGER
, which return the size in bytes of the file. So, we call the {FILE}.read_string({FILE}.count)
, and we will have the result available in {FILE}.last_string
.
Let see an example.
read_file_to_string (a_path: PATH)
-- Show how to read a file into a string
-- For binary files you can use {RAW_FILE}.
local
l_file: FILE
l_content: STRING
do
print ("============= Read file into string ====================")
create {PLAIN_TEXT_FILE} l_file.make_with_path (a_path)
if l_file.exists and then l_file.is_readable then
l_file.open_read
l_file.read_stream (l_file.count)
l_content := l_file.last_string
print (l_content)
io.put_new_line
l_file.close
else
io.error.put_string ("Could not read, the file:[" + a_path.name + " ] does not exist")
io.put_new_line
end
end
Show how to have the entire file in memory as the _Reading the contents of a File into a STRING variable```, while still allowing you iterate through it line by line.
read_file_to_collecton (a_path: PATH)
-- Show how to read a file into a Collection, ie LIST [STRING]
-- For binary files you can use {RAW_FILE}.
local
l_file: PLAIN_TEXT_FILE
l_collection: ARRAYED_LIST [STRING]
do
print ("============= Read file into a collection ====================")
create l_file.make_with_path (a_path)
create l_collection.make (10)
if l_file.exists and then l_file.is_readable then
l_file.open_read
from
until
l_file.end_of_file
loop
l_file.read_line
l_collection.force (l_file.last_string.twin)
end
l_file.close
else
io.error.put_string ("Could not read, the file:[" + a_path.name + " ] does not exist")
io.put_new_line
end
if l_collection.is_empty then
print ("The collection is empty")
else
-- Iterate over the collection of lines
across
l_collection as ic
loop
print (ic.item)
io.put_new_line
end
end
end
Using the features on FILE}
that we've seen in this chapter, we can build some metadata such us numer of lines, words per line and total number of words.
file_metadata (a_path: PATH)
-- Show how to get file metadata.
local
l_file: PLAIN_TEXT_FILE
l_collection: LIST [STRING]
l_string: STRING
l_lines: INTEGER
l_words: INTEGER
do
create l_file.make_with_path (a_path)
create {ARRAYED_LIST [STRING]} l_collection.make (10)
if l_file.exists and then l_file.is_readable then
l_file.open_read
from
until
l_file.end_of_file
loop
l_file.read_line
l_string := l_file.last_string.twin
l_collection.force (l_string)
end
print ("%NNumber of Lines:" + l_collection.count.out)
across l_collection as ic
from
l_lines := 1
loop
print ("%N Line: " + l_lines.out)
l_words := number_of_words (ic.item)
print (" - Words:" + number_of_words (ic.item).out)
l_lines := l_lines + 1
end
print ("%N Number of words: " + l_words.out)
else
io.error.put_string ("Could not read, the file:[" + a_path.name + " ] does not exist")
io.put_new_line
end
end
Helper feature to check the number of words per line.
number_of_words (a_line: STRING): INTEGER
-- Number of words for a line `a_line'.
do
if a_line.is_empty then
Result := 0
else
Result := a_line.split(' ').count
end
end
write_text_to_file
-- Show how to write text to a File
local
l_file: PLAIN_TEXT_FILE
do
create l_file.make_open_write("file.txt")
-- Open the file `file.txt' for writing;
-- create it if it does not exist
-- Note: if the file exist, you will lost all the existing info.
-- Write
if l_file.exists and then l_file.is_access_writable then
l_file.put_string ("Example: how to write text to a file")
l_file.close
end
-- Read the file `file.txt'
create l_file.make_open_read("file.txt")
if l_file.exists and then l_file.is_readable then
l_file.read_stream (l_file.count)
io.put_new_line
print (l_file.last_string)
l_file.close
end
end
append_data_to_file
-- Show how to append data to a file
local
l_file: PLAIN_TEXT_FILE
do
create l_file.make_open_append ("new.text")
-- Open the file `new.text' file for append;
-- if the file does not exist, create a new one
if l_file.exists and then l_file.is_writable then
l_file.put_string ("This is a string line")
l_file.put_new_line
l_file.put_boolean (True)
l_file.put_new_line
l_file.put_integer (10)
l_file.close
end
end