Eiffel Recipes

Chapter 2

Working with Files

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

2.1 Listing all Resources in a Directory

    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

2.2 Listing only Directories

    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

2.3 Listing only Files

    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

2.4 Traverse the entire directory tree, show Directories

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

2.5 Traverse the entire directory tree, show Resources

2.6 Listing specific files in a directory

2.7 Reading the contents of a File

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.

2.7.1 Reading the contents of a File line by line.

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.

2.7.2 Reading the contents of a File into a {STRING} variable.

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

2.7.2 Reading the contents of a File into a COLLECTION.

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

2.7.3 File Metadata

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

2.7.4 Write Text to a File

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

2.7.5 Write one item at time

2.7.6 Appending data to an existing file

    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

2.7.7 Merging text files