17.9 Classes File and Directory

Files are organized in directories (also called folders). Classes File and Directory enable programs to manipulate files and directories on disk. Class File can determine information about files and can be used to open files for reading or writing. We discussed techniques for writing to and reading from files in previous sections.

Figure 17.11 lists several of class File’s static methods for manipulating and determining information about files. We demonstrate several of these methods in Fig. 17.13.

Fig. 17.11 File class static methods (partial list).

static Method Description
AppendText Returns a StreamWriter that appends text to an existing file or creates a file if one does not exist.
Copy Copies a file to a new file.
Create Creates a file and returns its associated FileStream.
CreateText Creates a text file and returns its associated StreamWriter.
Delete Deletes the specified file.
Exists Returns true if the specified file exists and false otherwise.
GetCreationTime Returns a DateTime object representing when the file/directory was created.
GetLastAccessTime Returns a DateTime object representing when the file/directory was last accessed.
GetLastWriteTime Returns a DateTime object representing when the file/directory was last modified.
Move Moves the specified file to a specified location.
Open Returns a FileStream associated with the specified file and equipped with the specified read/write permissions.
OpenRead Returns a read-only FileStream associated with the specified file.
OpenText Returns a StreamReader associated with the specified file.
OpenWrite Returns a write FileStream associated with the specified file.

Class Directory provides capabilities for manipulating directories. Figure 17.12 lists some of class Directory’s static methods for directory manipulation. Figure 17.13 demonstrates several of these methods, as well. The DirectoryInfo object returned by method CreateDirectory contains information about a directory. Much of the information contained in class DirectoryInfo also can be accessed via the methods of class Directory.

Fig. 17.12 Directory class static methods.

static Method Description
CreateDirectory Creates a directory and returns its associated DirectoryInfo object.
Delete Deletes the specified directory.
Exists Returns true if the specified directory exists and false otherwise.
GetDirectories Returns a string array containing the names of the subdirectories in the specified directory.
GetFiles Returns a string array containing the names of the files in the specified directory.
GetCreationTime Returns a DateTime object representing when the directory was created.
GetLastAccessTime Returns a DateTime object representing when the directory was last accessed.
GetLastWriteTime Returns a DateTime object representing when items were last written to the directory.
Move Moves the specified directory to a specified location.

17.9.1 Demonstrating Classes File and Directory

Class FileTestForm (Fig. 17.13) uses File and Directory methods to access file and directory information. The Form contains the inputTextBox, in which the user enters a file or directory name. For each key that the user presses while typing in the TextBox, the program calls inputTextBox_KeyDown (lines 19–76). If the user presses the Enter key (line 22), this method displays either the file’s or directory’s contents, depending on the text the user input. (If the user does not press the Enter key, this method returns without displaying any content.)

Fig. 17.13 Using classes File and Directory.

Alternate View

  1     // Fig. 17.13: FileTestForm.cs
  2     // Using classes File and Directory.
  3     using System;
  4     using System.Windows.Forms;
  5     using System.IO;
  6
  7     namespace FileTest
  8     {
  9        // displays contents of files and directories
 10        public partial class FileTestForm : Form
 11        {
 12           // parameterless constructor
 13           public FileTestForm()
 14           {
 15                 InitializeComponent();
 16           }
 17          
 18           // invoked when user presses key
 19           private void inputTextBox_KeyDown(object sender, KeyEventArgs e)
 20           {
 21              // determine whether user pressed Enter key
 22              if (e.KeyCode == Keys.Enter)
 23              {
 24                 // get user-specified file or directory
 25                 string fileName = inputTextBox.Text;
 26          
 27                 // determine whether fileName is a file
 28                 if (File.Exists(fileName))
 29                 {
 30                    // get file's creation date, modification date, etc.
 31                    GetInformation(fileName);
 32                
 33                    // display file contents through StreamReader
 34                    try
 35                    {
 36                       // obtain reader and file contents
 37                       using (var stream = new StreamReader(fileName))
 38                        {
 39                           outputTextBox.AppendText(stream.ReadToEnd());
 40                        }
 41                    }
 42                    catch (IOException)
 43                    {
 44                        MessageBox.Show("Error reading from file",
 45                           "File Error", MessageBoxButtons.OK,
 46                            MessageBoxIcon.Error);
 47                    }
 48                 }
 49                 // determine whether fileName is a directory
 50                 else if (Directory.Exists(fileName))
 51                 {
 52                    // get directory's creation date,
 53                    // modification date, etc.
 54                    GetInformation(fileName);
 55                
 56                    // obtain directory list of specified directory
 57                    string[] directoryList =              
 58                       Directory.GetDirectories(fileName);
 59                
 60                    outputTextBox.AppendText("Directory contents:
");
 61                
 62                    // output directoryList contents
 63                    foreach (var directory in directoryList)
 64                    {
 65                       outputTextBox.AppendText($"{directory}
");
 66                    }
 67                 }
 68                   else
 69                   {
 70                      // notify user that neither file nor directory exists
 71                      MessageBox.Show(
 72                         $"{inputTextBox.Text} does not exist", "File Error",
 73                         MessageBoxButtons.OK, MessageBoxIcon.Error);
 74                   }
 75               }
 76           }
 77          
 78           // get information on file or directory,
 79           // and output it to outputTextBox
 80           private void GetInformation(string fileName)
 81           {
 82              outputTextBox.Clear();
 83          
 84              // output that file or directory exists
 85              outputTextBox.AppendText($"{fileName} exists
");
 86          
 87              // output when file or directory was created
 88              outputTextBox.AppendText(
 89                 $"Created: {File.GetCreationTime(fileName)}
" +
 90                 Environment.NewLine);
 91          
 92              // output when file or directory was last modified
 93              outputTextBox.AppendText(
 94                 $"Last modified: {File.GetLastWriteTime(fileName)}
" +
 95                 Environment.NewLine);
 96          
 97              // output when file or directory was last accessed
 98              outputTextBox.AppendText(
 99                 $"Last accessed: {File.GetLastAccessTime(fileName)}
" +
100                 Environment.NewLine);
101           }
102       }
103   }

Line 28 uses File method Exists to determine whether the user-specified text is the name of an existing file. If so, line 31 invokes private method GetInformation (lines 80–101), which calls File methods GetCreationTime (line 89), GetLastWriteTime (line 94) and GetLastAccessTime (line 99) to access file information. When method GetInformation returns, line 37 instantiates a StreamReader for reading text from the file. The StreamReader constructor takes as an argument a string containing the name and path of the file to open. Line 39 calls StreamReader method ReadToEnd to read the entire contents of the file as a string, then appends the string to outputTextBox. Once the file has been read, the using block disposes of the corresponding object, which closes the file.

If line 28 determines that the user-specified text is not a file, line 50 determines whether it’s a directory using Directory method Exists. If the user specified an existing directory, line 54 invokes method GetInformation to access the directory information. Lines 57–58 call Directory method GetDirectories to obtain a string array containing the names of the subdirectories in the specified directory. Lines 63–66 display each element in the string array. Note that, if line 50 determines that the user-specified text is not a directory name, lines 71–73 notify the user that the name the user entered does not exist as a file or directory.

17.9.2 Searching Directories with LINQ

We now consider another example that uses file- and directory-manipulation capabilities. Class LINQToFileDirectoryForm (Fig. 17.14) uses LINQ with classes File, Path and Directory to report the number of files of each file type that exist in the specified directory path. The program also serves as a “clean-up” utility—when it finds a file that has the .bak filename extension (i.e., a backup file), the program displays a MessageBox asking the user whether that file should be removed, then responds appropriately to the user’s input. This example also uses LINQ to Objects to help delete the backup files.

When the user clicks Search Directory, the program invokes searchButton_Click (lines 23–62), which searches recursively through the directory path specified by the user. If the user inputs text in the TextBox, line 27 calls Directory method Exists to determine whether that text is a valid directory. If it’s not, lines 30–31 notify the user of the error.

Fig. 17.14 Using LINQ to search directories and determine file types.

Alternate View

  1    // Fig. 17.14: LINQToFileDirectoryForm.cs
  2    // Using LINQ to search directories and determine file types.
  3    using System;
  4    using System.Collections.Generic;
  5    using System.Linq;
  6    using System.Windows.Forms;
  7    using System.IO;
  8
  9    namespace LINQToFileDirectory
 10    {
 11       public partial class LINQToFileDirectoryForm : Form
 12       {
 13          // store extensions found, and number of each extension found
 14          Dictionary>string, int> found = new Dictionary<string, int>();
 15
 16          // parameterless constructor
 17          public LINQToFileDirectoryForm()
 18          {
 19             InitializeComponent();
 20          }
 21         
 22          // handles the Search Directory Button's Click event
 23          private void searchButton_Click(object sender, EventArgs e)
 24          {
 25             // check whether user specified path exists
 26             if (!string.IsNullOrEmpty(pathTextBox.Text) &&
 27                !Directory.Exists(pathTextBox.Text))
 28             {
 29                // show error if user does not specify valid directory
 30                MessageBox.Show("Invalid Directory", "Error",
 31                   MessageBoxButtons.OK, MessageBoxIcon.Error);
 32             }
 33             else
 34             {
 35                // directory to search; if not specified use current directory
 36                string currentDirectory =
 37                   (!string.IsNullOrEmpty(pathTextBox.Text)) ?
 38                      pathTextBox.Text : Directory.GetCurrentDirectory();
 39         
 40                directoryTextBox.Text = currentDirectory; // show directory
 41               
 42                // clear TextBoxes
 43                pathTextBox.Clear();
 44                resultsTextBox.Clear();
 45               
 46                SearchDirectory(currentDirectory); // search the directory
 47               
 48                // allow user to delete .bak files
 49                CleanDirectory(currentDirectory);
 50               
 51                // summarize and display the results
 52                foreach (var current in found.Keys)
 53                {
 54                   // display the number of files with current extension
 55                   resultsTextBox.AppendText(
 56                      $"* Found {found[current]} {current} files." +
 57                      Environment.NewLine);
 58                }
 59               
 60                found.Clear(); // clear results for new search
 61              }
 62          }
 63         
 64          // search directory using LINQ
 65          private void SearchDirectory(string folder)
 66          {
 67             // files contained in the directory
 68             string[] files = Directory.GetFiles(folder);
 69         
 70             // subdirectories in the directory
 71             string[] directories = Directory.GetDirectories(folder);
 72            
 73             // find all file extensions in this directory
 74             var extensions =
 75                from file in files
 76                group file by Path.GetExtension(file);
 77            
 78             foreach (var extension in extensions)
 79             {
 80                if (found.ContainsKey(extension.Key))
 81                {
 82                   found[extension.Key] += extension.Count(); // update count
 83                }
 84                else
 85                {
 86                   found[extension.Key] = extension.Count(); // add count
 87                }
 88             }
 89            
 90             // recursive call to search subdirectories
 91             foreach (var subdirectory in directories)
 92             {
 93                SearchDirectory(subdirectory);
 94             }
 95          }
 96         
 97          // allow user to delete backup files (.bak)
 98          private void CleanDirectory(string folder)
 99          {
100            // files contained in the directory
101            string[] files = Directory.GetFiles(folder);
102         
103            // subdirectories in the directory
104            string[] directories = Directory.GetDirectories(folder);
105
106            // select all the backup files in this directory
107            var backupFiles =
108               from file in files
109               where Path.GetExtension(file) == ".bak"
110               select file;
111           
112            // iterate over all backup files (.bak)
113            foreach (var backup in backupFiles)
114            {
115               DialogResult result = MessageBox.Show(
116                  $"Found backup file {Path.GetFileName(backup)}. Delete?",
117                  "Delete Backup", MessageBoxButtons.YesNo,
118                  MessageBoxIcon.Question);
119           
120               // delete file if user clicked 'yes'
121               if (result == DialogResult.Yes)
122               {
123                  File.Delete(backup); // delete backup file
124                  --found[".bak"]; // decrement count in Dictionary
125              
126                  // if there are no .bak files, delete key from Dictionary
127                  if (found[".bak"] == 0)
128                  {
129                     found.Remove(".bak");
130                  }
131               }
132            }
133         
134            // recursive call to clean subdirectories
135            foreach (var subdirectory in directories)
136            {
137               CleanDirectory(subdirectory);
138            }
139         }
140       }
141    }

Method SearchDirectory

Lines 36–38 get the current directory (if the user did not specify a path) or the specified directory. Line 46 passes the directory name to recursive method SearchDirectory (lines 65–95). Line 68 calls Directory method GetFiles to get a string array containing filenames in the specified directory. Line 71 calls Directory method GetDirectories to get a string array containing the subdirectory names in the specified directory.

Lines 74–76 use LINQ to get the filename extensions in the files array. Path method GetExtension obtains the extension for the specified filename. We use the LINQ group by clause to group the results by filename extension. For each filename-extension group returned by the LINQ query, lines 78–88 use LINQ method Count to determine the number of occurrences of that extension in the files array.

Class LINQToFileDirectoryForm uses a Dictionary (declared in line 14) to store each filename extension and the corresponding number of filenames with that extension. A Dictionary (namespace System.Collections.Generic) is a collection of key–value pairs, in which each key has a corresponding value. Class Dictionary is a generic class like class List (presented in Section 9.4). Line 14 indicates that the Dictionary found contains pairs of strings and ints, which represent the filename extensions and the number of files with those extensions, respectively. Line 80 uses Dictionary method ContainsKey to determine whether the specified filename extension has been placed in the Dictionary previously. If this method returns true, line 82 adds the count of the number of files with a given extention to the current total for that extension that’s stored in the Dictionary. Otherwise, line 86 inserts a new key–value pair into the Dictionary for the new filename extension and its extension count. Lines 91–94 recursively call SearchDirectory for each subdirectory in the current directory—depending on the number of files and folders, this operation could take substantial time to complete.

Method CleanDirectory

When method SearchDirectory returns, line 49 calls CleanDirectory (lines 98–139) to search for all files with extension .bak. Lines 101 and 104 obtain the list of filenames and list of directory names in the current directory, respectively. The LINQ query in lines 107–110 locates all filenames in the current directory that have the .bak extension. Lines 113–132 iterate through the results and ask the user whether each file should be deleted. If the user clicks Yes in the dialog, line 123 uses File method Delete to remove the file from disk, and line 124 subtracts 1 from the total number of .bak files. If the number of .bak files remaining is 0, line 129 uses Dictionary method Remove to delete the key–value pair for .bak files from the Dictionary. Lines 135–138 recursively call CleanDirectory for each subdirectory in the current directory. After each subdirectory has been checked for .bak files, method CleanDirectory returns, and lines 52–58 display the summary of filename extensions and the number of files with each extension. Line 52 uses Dictionary property Keys to get all the keys. Line 56 uses the Dictionary’s indexer to get the value for the current key. Finally, line 60 uses Dictionary method Clear to delete the contents of the Dictionary.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset