C# study notes - file system data

I. Introduction

      C# often performs read and write operations on data, and the basis for these operations is on streams, that is, C# reads data through streams. There are two commonly used streams in C#:

      1. Output stream: When writing data to some external target, an input stream is used.

       2. Input stream: used to read data into memory or variables that can be accessed by the program.

      The System.IO namespace contains classes for reading and writing data in files. Here we only introduce the main classes for file input and output, as follows:

            kind                      illustrate                                          
File Static utility class that provides many static methods for moving, copying and deleting files.
Directory Static utility class that provides many static methods for moving, copying and deleting directories.
Path Utility class for handling path names.
FileInfo Represents a physical file on disk, and this class contains methods for handling this file. To complete reading and writing files, a Stream object must be created.
DirectoryInfo Represents a physical directory on disk, and this class contains methods for handling this directory.
FileSystemInfo Used as a base class for FileInfo and DirectoryInfo, you can use polymorphism to handle both files and directories.
FileStream Represents a file that is writable or readable, or both. This file can be read and written synchronously or asynchronously.
StreamReader Reading character data from a stream can be created using FileStream as a base class.
StreamWriter To write character data to a stream, you can create it using FileStream as a base class.
FileSystemWatcher  FileSystemWatcher is the most complex class introduced in this chapter. It is used to monitor files and directories, providing events that applications can capture when changes to these files and directories
occur.
Table 1-1 Classes used to access file systems
NOTE: System.IO.Compression namespace, which allows reading and writing compressed files. Let’s mainly look at two stream classes:

DeflateStream - represents a stream that automatically compresses data when writing or automatically decompresses when reading, using the Deflate algorithm to achieve compression.

GZipStream - represents a stream that automatically compresses data when writing or automatically decompresses when reading, using the GZIP algorithm to achieve compression.

2. FiIe class and Directory class

The File and Directory utility classes provide a number of static methods for working with files and directories. These methods can move files, query and update properties, and create FileStream objects. The following are the most commonly used static class methods of the File class:

  Table 1-2 Static methods of File class
     method                    illustrate                                 
Copy() Copy files from source location to destination location.
Create() Creates a file at the specified path.
Delete() Delete Files.
Open() Returns a FileStream object at the specified path.
Move() Moves the specified files to a new location. You can give the file a different name in the new location.

The most commonly used static class methods of the Directory class:

Table 1-3 Static methods of Directory class
                          method                                                   illustrate                                                          
CreateDirectory() Creates a directory with the specified path.
Delete() Delete the specified directory and all files in it.
GetDirectories() Returns an array of string objects representing the directory names under the specified directory.
EnumerateDirectories()  Similar to GetDirectories(), but returns an IEnumerable<string> collection of directory names.
GetFiles() Returns an array of string objects containing the file names in the specified directory.
EnumerateFiles() Similar to GetFiles(), but returns an IEnumerable<string> collection of file names.
GetFileSystemEntries() Returns an array of string objects containing the file and directory names in the specified directory.
EnumerateFileSystemEntries()  Similar to GetFilesSystemEntries(), but returns an IEnumerable<string> collection of file and directory names.
Move() Moves the specified directory to a new location, giving the folder a new name in the new location.
 Note: Three of the EnumerateXxx() methods were introduced in .NET4, and their performance is better than the corresponding GetXxx() method when there are a large number of files or directories.

FileInfo class: It is not a static class and has no static methods. It can only be used after instantiation. The FileInfo object represents a file on the disk or network location. You can create a FileInfo object by providing the file path:

FileInfo aFile = new FileInfo (@"C:\Log.txt"); Note: @ means that the string is interpreted literally and not as an escape character

The FileInfo class provides many methods similar to the File class, but since File is a static method, it requires a string parameter to specify the file location for each method call. The following code does the same job:

                   FileInfo aFile = new FileInfo(“Data.txt”);                                        if(File.Exists(“Data.txt”)) Console.WriteLine("File Exists");

                   if(aFile.Exists) Console,WriteLine("File Exists");

Usage scenarios of FileInfo and File:

1. If you only make a single method call, you can use the static File class. Because there is no need to instantiate, the call is faster.

2. If the application performs several operations on the file, it is better to instantiate the FileInfo object and use its methods.

The following introduces the properties of FileSystem:

Table 1-4 Properties of FileSystem
                           Attributes                                                  illustrate
                    Attributes  Use the FileAttributes enumeration to get or set the attributes of the current file or directory.
CreationTime,
CreationTimeUtc
Get the creation date and time of the current file, both UTC and non-UTC versions available.
Extension Extract the file extension. This property is read-only.
Exists Determines whether a file exists. This is a read-only abstract property overridden in FileInfo and DirectoryInfo.
FullName Retrieve the full path of the file. This property is read-only.
LastAccessTime,
LastAccessTimeUtc
Gets or sets the date and time the current file was last accessed, UTC and non-UTC versions can be used
LastWriteTime,
LastWriteTimeUtc
Gets or sets the date and time the current file was last written to, UTC and non-UTC versions can be used
Name Retrieves the full path to a file. This is a read-only abstract property, overridden in FileInfo and DirectoryInfo.
The following are the dedicated properties of FileInfo:
Table 1-5 Properties of FileInfo
       Attributes                                      illustrate                                   
Directory Retrieves a DirectoryInfo object representing the directory containing the current file. This property is read-only 
DirectoryName Returns the path to the file directory. This property is read-only
IsReadOnly A shortcut for the read-only feature of the file. This attribute can also be accessed through Attributes
Length Get the size of the file in bytes, returning a long value. This property is read-only
 Note: The FileInfo object itself does not represent a stream. To read and write files, a Stream object must be created. However, the FileInfo object provides several methods that return instantiated Stream objects to help do this.

DirectoryInfo is introduced below:

The difference and usage between Directory and DirectoryInfo are similar to the difference and usage between File and FileInfo. The following are the dedicated properties of DirectoryInfo:

Table 1-6 Special properties of the DirectoryInfo class
      Attributes                       illustrate                                                                         
Parent     Retrieves a DirectoryInfo object representing the directory containing the current directory. This property is read-only
Root Retrieve a DirectoryInfo object representing the root directory containing the current directory, such as the C:\ directory. This property is read-only
 Pathnames and relative paths:

Pathname: also becomes an absolute pathname. It is the known location from which the specified file or directory is displayed. In layman's terms, it is the complete path.

Relative path: Starting from the current working directory, this is the default for relative pathnames. For example, the application runs in the C:\Development\FileDemo directory and uses the relative path LogFile.txt. The file is: C:\Development\FileDemo\LogFile.txt. If you want to move the directory up, use the .. string, then the path ..\Log.txt represents the C:\Development\Log.txt file. If you move up two directories, it's ..\..\. If necessary, use Directory.GetCurrentDirectory() to find out the current location of the working directory, or use Directory.SetCurrentDirectory() to set a new path.

FileStream object:

A FileStream object represents a file stream pointing to a disk or network path. This class provides methods for reading and writing bytes in a file. However, StreamReader or StreamWrite are often used to perform these functions. This is because the FileStream class operates on bytes and byte arrays, while the Stream class operates on character data. Character data is easy to use, but some operations, such as random file access (accessing data at a certain point in the middle of the file), must be performed by a FileStream object. The simplest constructor to create a FileStream object has only two parameters, the file name and the FileMode enumeration value. as follows:

FileStream aFile = new FileStream(filename,FileMode.<Member>); The FileMode enumeration contains several members that specify how to open or create files. As shown in Table 1-7:

Table 1-7 FileMode enumeration members
               The file exists                              file does not exist                  
Append Open the file, the stream points to the end of the file, can only be used in conjunction with
enumeration FileAccess.Write
Create a new file. Can only be used with enumeration
FileAccess.Write
Create Delete the file and create a new one Create new file
CreateNew throw an exception Create new file
Open Open the file and point the stream to the beginning of the file throw an exception
OpenOrCreate Open the file and point the stream to the beginning of the file Create new file
Truncate Open the file, clear its contents, stream to the beginning of the file
, retain the original creation date of the file
throw an exception

Another commonly used constructor is as follows:

FileStream aFile = new FileStream (filename, FileMode.<Member>, FileAccess.<Member>); The third parameter is a member of the FileAcess enumeration, which specifies the role of the stream. As shown in Table 1-8:

Table 1-8 FileAccess enumeration members
                          illustrate                                
Read      Open the file for read-only
Write       Open the file for writing only
ReadWrite      Open a file for reading and writing
 Note: Performing operations on a file that are not specified by FileAcess enumeration members will cause an exception to be thrown. The function of this attribute is to change the user's access rights to the file based on the user's permission level. In versions of the FileStream constructor that do not use the FileAcess enumeration, the default value FileAcess.ReadWrite is used.

Both the File and FileInfo classes provide OpenRead() and OpenWrite() methods to make it easier to create FileStream objects. The former opens the file for read-only access, and the latter only allows writing to the file. The Data.tex file is opened for read-only access as follows:

FileStream aFile = File.OpenRead("Data.txt");

       The following code performs the same function: FileInfo aFileInfo = new aFileInfo("Data.txt"); FileStream aFile = aFileInfo.OpenRead();

1. File location

The FileStream class maintains an internal file pointer that points to the location in the file where the next read or write operation will occur. In most cases, when a file is opened, it points to the beginning of the file, but the position can be modified through the Seek() method. This method takes two parameters: the first specifies the distance in bytes to move the file pointer. The second one specifies the starting position to start calculation, represented by a value of the SeekOrigin enumeration. The SeekOrigin enumeration contains: Begin, Current and End. For example: aFile.Seek(8,SeekOrigin.Begin); moves the pointer 8 bytes from the beginning of the file.

2. Read data

The FileStream class can only process raw bytes (bytes), which makes the FileStream class can be used for any data files, not just text files, such as images and sounds, etc., but the FileStream object cannot directly read data into a string. , the StreamReader class does.

The FileStream.Read() method is the main means of accessing data from the file pointed to by the FileStream object. This method reads data from the file and then writes the data into a byte array. It has three parameters: the first one is passed The input byte array is used to accept data in the FileStream object; the second is the position in the byte array where data is started to be written, which is usually 0; the third specifies how many bytes to read from the file.

example:

        static void Main(string[] args)
        {
            byte[] byteData = new byte[200];
            char[] charaData = new char[200];

            FileStream aFile = new FileStream("../../Program.cs",FileMode.OpenOrCreate);
            aFile.Seek(144,SeekOrigin.Begin);
            aFile.Read(byteData,0,200);
            Decoder d = Encoding.UTF8.GetDecoder();
            d.GetChars(byteData,0,byteData.Length,charaData,0);
            Console.WriteLine(charaData);
            Console.ReadLine();
        }
3. Write data

The writing process is similar to the reading process. Examples below:

        static void Main(string[] args)
        {
            byte[] byteData;
            char[] charaData;

            FileStream aFile = new FileStream("Program.txt",FileMode.Create);
            charaData = "My pink half of the drainpipe.".ToCharArray();
            byteData = new byte[charaData.Length];
            Encoder d = Encoding.UTF8.GetEncoder();
            d.GetBytes(charaData, 0, charaData.Length, byteData, 0,true);

            aFile.Seek(0,SeekOrigin.Begin);
            aFile.Write(byteData,0,byteData.Length);
        }
StreamWriter object:

Operating byte arrays is troublesome, so StreamReader or StreamWriter is usually used to process files. If there is no need to change the file pointer position, these classes make it easy to operate files. The StreamWriter class allows characters and strings to be written to files. It handles the underlying conversion and writes data to a FileStream object. There are many ways to create a StreamWriter object. If you already have a FileStream object, you can use this object to create it:

FileStream aFile = new FileStream("Log.txt",FileMode.CreateNew); 

StreamWriter sw = new StreamWriter(aFile);

You can also create a StreamWriter object directly from a file: 

StreamWriter sw = new StreamWriter("Log.txt",true);The parameters of this constructor are the file name and a Boolean value. This Boolean value specifies whether to append the file or create a new file:

a. If it is false, create a new file or intercept the existing file and open it

b. If it is true, open the file and keep the original data. If it cannot be found, create a new file.

The StreamWriter object does not provide options similar to FileMode and FileAccess in FileStream. It can only use Boolean to append files or create new files, so it always has read and write rights to the file. If you want to use advanced parameters, you must first specify them in the FileStream constructor and then create a StreamWriter in the FileStream object. Examples are as follows:

        static void Main(string[] args)
        {
            FileStream aFile = new FileStream("Log.txt",FileMode.OpenOrCreate);
            StreamWriter sw = new StreamWriter(aFile);
            bool truth = true;
            sw.WriteLine("Hell to you");
            sw.WriteLine("It is now {0} and things are looking good.",DateTime.Now.ToLongDateString());
            sw.Write("More than that");
            sw.Write("it's {0} that C# is fun.",truth);
            sw.Close();
        }
Note: WriteLine() automatically wraps the line, and Write() will write at the end of the last time each time.

StreamReader object:

Used to read data, its creation method is similar to StreamWriter. The simplest creation method is as follows:

FileStream aFile = new FileStream(“Log.txt”,FileMode.Open);StreamReader sr = new StreamReader(aFile);

It can also be created with a specific file path: StreamReader sr = new StreamReader ("Log.txt"); specific examples are as follows:

        static void Main(string[] args)
        {
            string line;
            FileStream aFile = new FileStream("Log.txt",FileMode.OpenOrCreate);
            StreamReader sr = new StreamReader(aFile);
            line = sr.ReadLine();
            while(line != null)
            {
                Console.WriteLine(line);
                line = sr.ReadLine();
            }
            sr.Close();
            Console.ReadKey();
        }
1. Read data

The ReadLine() method is not the only way to access data in a file. The StreamReader class also contains many methods for reading data, the simplest of which is Read(). This method returns the next character of the stream as a positive integer value. If the end of the stream is reached, it returns -1. Use the Convert class. You can convert this value to a character and rewrite the above using this method:

            StreamReader sr = new StreamReader(aFile);
            int charCode;
            charCode = sr.Read();
            while(charCode !=-1)
            {
                Console.WriteLine(Convert.ToChar(charCode));
                charCode = sr.Read();
            }
            sr.Close();
For small files, there is a very simple method, ReadToEnd(), which reads the entire file and returns it as a string. like:

            StreamReader sr = new StreamReader(aFile);
            line = sr.ReadToEnd();
            Console.WriteLine(line);
            sr.Close();
Another way to handle large files is the new static method File.ReadLines() in .NET 4, which returns an IEnumerable<string> collection. Iterates over the strings in this collection, reading the file one line at a time. Use as follows

            foreach(string alternativeLine in File.ReadLines("Log.txt"))
            {
                Console.WriteLine(alternativeLine);
            }
2. Files separated by delimiters

For data formats separated by delimiters, the Split() method of the String class is commonly used to convert a string into an array. Specific examples are as follows:

        private static List<Dictionary<string, string>> GetData(out List<string> columns)
        {
            string line;
            string[] stringArray;
            char[] charArray = new char[] { ',' };
            List<Dictionary<string, string>> data =
                new List<Dictionary<string, string>>();
            columns = new List<string>();
            FileStream aFile = new FileStream(@"..\..\SomeData.txt",FileMode.Open);
            StreamReader sr = new StreamReader(aFile);

            line = sr.ReadLine();
            stringArray = line.Split(charArray);
            for(int x = 0;x<=stringArray.GetUpperBound(0);x++)
            {
                columns.Add(stringArray[x]);
            }
            line = sr.ReadLine();
            while(line!=null)
            {
                stringArray = line.Split(charArray);
                Dictionary<string, string> dataRow = new Dictionary<string, string>();
                for(int x = 0;x<=stringArray.GetUpperBound(0);x++)
                {
                    dataRow.Add(columns[x],stringArray[x]);
                }
                data.Add(dataRow);
                line = sr.ReadLine();
            }
            sr.Close();
            return data;
        }
        static void Main(string[] args)
        {
            List<string> columns;
            List<Dictionary<string, string>> myData = GetData(out columns);
            foreach(string column in columns)
            {
                Console.Write("{0,-20}",column);
            }
            Console.WriteLine();
            foreach(Dictionary<string,string> row in myData)
            {
                foreach(string column in columns)
                {
                    Console.Write("{0,-20}", row[column]);
                }
                Console.WriteLine();
            }
            Console.ReadKey();          
        }
Asynchronous file access:

When performing a large number of file access operations at one time or processing very large files, reading and writing file system data is very slow. At this time, if you want to perform other operations while waiting for these operations to be completed, you need to perform a one-step operation. This This kind of asynchronous is applicable to the FileStream, StreamWriter and StreamReader classes, usually with the Async suffix, such as the ReaderLineAsync() method of the StreamReader class.

Read and write compressed files:

When processing files, using compressed files will save a lot of hard drive space. The System.IO.Compression namespace contains classes that can compress files in code, using the GZIP or Deflate algorithms. But compressing files doesn't just mean compressing them. Commercial applications allow multiple files to be placed in a compressed file (often called an archive file). This section is much simpler: just saving text data in a compressed file. This file cannot be accessed in external utilities, but the file is much smaller than the uncompressed version.

There are two compression stream classes DeflateStream and GZipStream in the System.IO.Compression namespace. They work very similarly. For these two classes, they must be initialized with an existing stream. For files, the stream is a FileStream object. You can then use them for StreamReader and StreamWriter. Additionally just specify whether the stream is for compression (saving a file) or decompression (loading a file), and the class knows what to do with the data passed to it. Examples are as follows:

        static void SaveCompressedFile(string filename,string data)
        {
            FileStream fileStream = new FileStream(filename,FileMode.Create,FileAccess.Write);
            GZipStream compressionStream = new GZipStream(fileStream,CompressionMode.Compress);
            StreamWriter writer = new StreamWriter(compressionStream);
            writer.Write(data);
            writer.Close();
        }
        static string LoadCompressedFile(string filename)
        {
            FileStream fileStream = new FileStream(filename,FileMode.Open,FileAccess.Read);
            GZipStream compressionStream = new GZipStream(fileStream,CompressionMode.Decompress);
            StreamReader reader = new StreamReader(compressionStream);
            string data = reader.ReadToEnd();
            reader.Close();
            return data;
        }
        static void Main(string[] args)
        {
            string filename = "compressedFile.txt";
            string sourceString = Console.ReadLine();
            StringBuilder sourceStringMultiplier = new StringBuilder(sourceString.Length*100);
            for(int i = 0; i<100; i++)
            {
                sourceStringMultiplier.Append(sourceString);
            }
            sourceString = sourceStringMultiplier.ToString();
            Console.WriteLine("Source data is {0} bytes long.",sourceString.Length);
            SaveCompressedFile(filename, sourceString);
            Console.WriteLine("\nData saved to {0}.",filename);
            FileInfo compressedFileData = new FileInfo(filename);
            Console.WriteLine("Compressed file is {0} bytes long.",compressedFileData.Length);
            string recoveredString = LoadCompressedFile(filename);
            recoveredString = recoveredString.Substring(0,recoveredString.Length/100);
            Console.WriteLine("\nRecovered data: {0}",recoveredString);
            Console.ReadKey();
        }
Serialized object:

Applications often need to store data on the hard drive. Building text and data files piece by piece was described earlier, but this is usually not the simplest way. Sometimes it's better to store data in the form of objects.

1. .NET Framework provides the infrastructure for serialized objects in the System.Runtime.Serialization and System.Runtime.Serialization.Formatter namespaces. The latter contains some namespaces.Concrete classes implement this infrastructure. In the framework, there is an important implementation available: System.Runtime.Serialization,Formatter.Binary. This namespace contains the BinaryFormatter class that converts objects intoSerialize to binary data, and binary data can also be serialized to objects.

Table 1-9 Methods of the IFormatter interface
          method                                                        illustrate                                    
void Serialize(Stream stream,object source) Serialize source to stream
object Deserialize(Stream stream) Deserialize the data in the stream and return the resulting object
These methods all handle streams, so they can be combined with the previous content. Serialization using BinaryFormatter is simple:

IFormatter serializer = new BinaryFormatter();      serializer.Serialize(myStream,myObject);

Deserialization is equally simple:

IFormatter serializer = new BinaryFormatter();    MyObjectType myNewObject = serializer.Deserialize(myStream) as MyObjectType;

This code will be put into practice below:

Add a new Product class:

    public class Product
    {
        public long Id;
        public string Name;
        public double Price;
        [NonSerialized]
        string Notes;
        public Product(long id, string name, double price, string notes)
        {
            Id = id;
            Name = name;
            Price = price;
            Notes = notes;
        }
        public override string ToString()
        {
            return string.Format("{0}: {1} (${2:F2}) {3}", Id, Name, Price, Notes);
        }
    }
Then add in the Main method of the Progress class:

        static void Main(string[] args)
        {
            List<Product> products = new List<Product>();
            products.Add(new Product(1,"Spily Pung",1000.0,"Goods stuff."));
            products.Add(new Product(2, "Gloop Galloop Soup", 25.0, "Tasty."));
            products.Add(new Product(4, "Hat Sauce", 12.0, "One for the kids."));
            Console.WriteLine("Products to save:");
            foreach(Product product in products)
            {
                Console.WriteLine(product);
            }
            Console.WriteLine();

            IFormatter serializer = new BinaryFormatter();

            FileStream saveFile = new FileStream("Products.bin", FileMode.Create, FileAccess.Write);
            serializer.Serialize(saveFile,products);
            saveFile.Close();

            FileStream loadFile = new FileStream("Products.bin",FileMode.Open,FileAccess.Read);
            List<Product> saveProducts = serializer.Deserialize(loadFile) as List<Product>;
            loadFile.Close();

            Console.WriteLine("Products loaded:");
            foreach(Product product in saveProducts)
            {
                Console.WriteLine(product);
            }
        }
This example creates a collection of Product objects, saves the collection to disk, and then reloads it. But the first run throws an exception because the Product object is not marked as "serializable". The .NET Framework requires objects to be marked as serializable in order to serialize them. There are many reasons for this, including:

1. The serialization effect of some objects is not good. For example, they need to reference local data that only exists if they themselves are in memory.

2. Some objects contain sensitive data that should not be saved or transferred to another process in an unsafe manner.

The Serializable attribute is not inherited by derived classes; it must be applied to every class that is to be serialized. NoSerialized This attribute can be used by any member, and will not be serialized after use.

Monitor the file system:

.NET Freamwork uses FileSystemWatcher to monitor files. The process is very simple: you must first set some properties to specify the location and content of the monitoring and the time when the event to be processed by the application is triggered. Then provide the address of the customized event handler to FileSystemWatcher. When an important event occurs, FileSystemWatcher can call these event handlers, and finally open FileSystemWatcher and wait for the event. The properties that must be set before enabling FileSystemWatcher are as follows:

Table 1-10 Properties of FileSystemWatcher
               Attributes                                           illustrate                                                                                  
Path Set the file location or directory to monitor
NotifyFilter This is a combination of NotifyFilters enumeration values. The NotifyFilters enumeration value specifies what content is to be monitored in the monitored file.
These represent the properties of the file or folder to be monitored. If the specified property changes, the event is raised. Possible enumeration values ​​are Attributes, CreationTime, DirectoryName, FileName, LastAccess, LastWrite, Security, and Size. Note that these enumeration values ​​can be combined using the binary OR operator

Filter A filter that specifies which files to monitor. For example, *.txt
 After setting up, you must write event handlers for the four events Changed, Created, Deleted and Renamed. After setting the properties and events, set the EnableRaisingEvents property to true to start monitoring. Example:

Instantiate the FileSystemWatcher object and add a listener

            FileSystemWatcher watcher = new FileSystemWatcher();
            watcher.Deleted += (s, e) => AddMessage("File: {0} Deleted",e.FullPath);
            watcher.Renamed += (s, e) => AddMessage("File renamed from {0} to {1}",e.OldName,e.FullPath);
            watcher.Changed += (s, e) => AddMessage("File: {0} {1}",e.FullPath,e.ChangeType.ToString());
            watcher.Created += (s, e) => AddMessage("File: {0} Created",e.FullPath);
Set the listening position and other listening conditions:

            watcher.Path = System.IO.Path.GetDirectoryName("Progress.txt");
            watcher.Filter = System.IO.Path.GetFileName("Progress.txt");
            watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.Size;
            watcher.EnableRaisingEvents = true;
System.IO.Path is used to process and extract the information in the file location string. Here we first use it to extract the directory name entered by the user in the text box through the GetDirectoryName() method. NotifyFilter is the filter.

Guess you like

Origin blog.csdn.net/u014086857/article/details/52831324