Chapter 16 - File, Object Serialization

 

The content and code of this article is referenced from book Pro C#5.0 and the .NET 4.5 Framework by Apress. The intention of the writing is to review the konwledge and gain better understanding of the .net framework. 

 

1. Exploring System.IO

The System.IO namespace is the region of the base class libraries devoted to file-based input and output services. 

Class Meaning 
BinaryReader, BinaryWriter These classes allow you to store and retrieve primitive data types as a binary value
BufferedStream This class provides temporary storage for a stream of bytes that you can commit to storage at a later time
Directory, DirectoryInfo You use these classes to manipulate a machine's directory structure. 
DriveInfo   This class provides detailed information regarding the drives
File, FileInfo You use these classes to manipulate a machine's set of files. 
FileStream This class gives you file access
FileSystemWatcher This class allows you to monitor the modification of files in a specific directory
MemoryStream This class provides random access to streamed data stored in memory rather than in a physical file
Path   This class performs operations on System.String that contain file or directory path 
StreamWriter, StreamReader You use these classes to store textual information to a file. 
StringWriter, StringReader   They also work with textual data, but the underlying storage is a string rather that a file.

 

1.1 Working with DirectoryInfo Type

We begin working with the DirectoryInfo type by specifying a particular directory path as a constructor parameter. User the dot(.) notation if you want to obtain access to the current working directory. 

Create(), CreateSubdirectory() Create a directory when given a path name
Delete() Deletes a directory and all its contents
GetDirectories() Returns an array of DirectoryInfo objects that represent all subdirectories in the current directory
GetFiles() Retrieves an array of FileInfo objects that represent a set of files 
MoveTo() Moves a directory and its contents to a new path
Parent Retrieves the parent directory
Root Gets the root portion of a path
   
   

 

 

        static void Main(string[] args)
        {
            //current directory
            DirectoryInfo workingDirectory = new DirectoryInfo(".");
            //working on windows directory
            DirectoryInfo workingDirectory1 = new DirectoryInfo(@"C:\Windows");
            //Create NotExist directory if not exist
            DirectoryInfo workingDirectory2 = new DirectoryInfo(@"C:\NotExist");
            workingDirectory2.Create();

            ShowDirInfo(workingDirectory);
        }

        private static void ShowDirInfo(DirectoryInfo dir)
        {
            Console.WriteLine("Directory full name is {0}", dir.FullName);
            Console.WriteLine("Directory name is {0}", dir.Name);
            Console.WriteLine("Directory attributes is {0}", dir.Attributes);
            Console.WriteLine("Directory creation time is {0}", dir.CreationTime);
            Console.WriteLine("Root is {0}", dir.Root);
        }

 

1.2 Enumerating files 

DirectoryInfo defines a method called GetFiles which returns an array of FileInfo objects, each of which exposes details of a particular file.

        private static void DisplayFiles()
        {
            //current directory
            DirectoryInfo dir = new DirectoryInfo (".");

            foreach (FileInfo file in dir.GetFiles()) {
                Console.WriteLine ("file name is {0}", file.Name);
                Console.WriteLine ("file size is {0}", file.Length);
                Console.WriteLine ("file creation time is {0}", file.CreationTime);
            }
        }

 

1.3 Create subdirectory

You can extend a directory structure using the DirectoryInfo.CreateSubdirectory() method. 

        private static void DisplayFiles()
        {
            //current directory
            DirectoryInfo dir = new DirectoryInfo (".");

            dir.CreateSubdirectory ("sub1");

            dir.CreateSubdirectory (@"sub2\folder");
        }

You are not required to capture the return value of the CreateSubdirectory(), but you should be aware that a DirectoryInfo object representing the newly created item. 

        private static void DisplayFiles()
        {
            //current directory
            DirectoryInfo dir = new DirectoryInfo (".");

            DirectoryInfo createdDir = dir.CreateSubdirectory (@"sub2\folder");
        }

 

1.4 Working with the Directory Type

The static member of Directory provides similary functionality provided by the instance of DirecotoryInfo. However, do remember, members of Directory typically return string data rather than strongly typed objects. 

 

1.5 Working with DriveInfo type

The System.IO namespace provides a class named DriveInfo. Like Directory.GetLogicalDrives() or DriveInfo.GetDrives() allow you to discover the names of a machine's drives. 

        private static void DisplayDrives()
        {
            DriveInfo[] myDrives = DriveInfo.GetDrives ();

            foreach (DriveInfo d in myDrives) {
                Console.WriteLine (d.Name);
                Console.WriteLine (d.DriveType);

                if (d.IsReady) {
                    Console.WriteLine (d.TotalFreeSpace);
                    Console.WriteLine (d.VolumeLabel);
                }
            }
        }

 

2. Working with FileInfo

FileInfo class allows you to obtain details regarding existing files on your hard drive. Note tha a majority of the methods of the FileInfo class return a specifi I/O-centric object (FileStream, StreamWriter), allows you to begin reading and writing data. 

Notice that the FileInfo.Create() method returns a FileStream object, which exposes write/read operations to the underlying file. After you finish with the current FileStream object, you must ensure you close down the handle to release stream resources. 

        private static void CreateFile()
        {
            FileInfo f = new FileInfo (@"C:\test.dat");

            using (FileStream fs = f.Create ()) {
                //...    
            }
        }

 

2.1 FileInfo.Open()

You can use FileInfo.Open() method  to open exising file. 

        private static void OpenFile()
        {
            FileInfo f = new FileInfo (@"C:\test.dat");

            using (FileStream fs = f.Open (FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))     {
                //...    
            }
        }

 

Notice that Open() typically takes several parameters to qualify exactly how to iterate the file you want to manipulate. 

The first parameter specifies the I/O request (e.g. make a new file, open an existing, or append to a file), which you can specify via FileMode enumeration. The second parameter, a value from the FileAccess enumeration, to determine the read, write, access. 

 

2.2 FileInfo.OpenRead() and FileInfo.OpenWrite() method

OpenRead() and OpenWrite() are quick ways to create read-only and write-only FileStream without calling Open with different types of parameters. 

            var f1 = new FileInfo(@"D:\test.dat");
            using (var stream = f1.OpenRead())
            {

            }

            using (var stream = f1.OpenWrite())
            {

            }

 

2.3 FileInfo.OpenText()

Unlike other FileInfo methods that return FileStream object, the OpenText() method returns an instance of StreamReader. The StreamReader type provides a way to read character data from the underlying file. 

            var f1 = new FileInfo(@"D:\test.dat");
            using (StreamReader reader = f1.OpenText())
            {

            }

 

2.4 CreateText() and AppendText()

Both methods return a StreamWriter object, provides a way to write character data to the underlying file. 

 

3. Working with the File Type

The file type uses several static members to provide functionality almost identical that of the fileInfo type. 

3.1 Additional File-Centric Members

The file type also supports a few members, which can simplify the processes of reading and writing textual data.

ReadAllBytes Opens the file, returns the binary data as an array of bytes, and then close the file.
ReadAllLines Open the file, returns the character data as an array of strings
ReadAllText Open the file, returns the character data as a System.String
WriteAllBytes Open the file, writes out the byte array
WriteAllLines Open the file, writes out an array of strings
WriteAllText Open the file, writes the character data from a string

You can use these methods of the File type to read and write batches of data in only a few lines of code. And each of these methods automatically closes down the underlying file handle. 

        static void Main(string[] args)
        {
            string[] data = {"test1", "test2", "test3"};

            File.WriteAllLines(@"C:\test.txt", data);

            foreach (var d in File.ReadAllLines(@"C:\test.txt"))
            {
                Console.WriteLine(d);   
            }
        }

 

4. The abstract Stream class

A stream represents a chunk of data flowing between a source and a destination. Streams provide a common way to interact with a sequence of bytes.

The abstract System.IO.Stream class defines several members that provide support for synchronous and asynchronous interactions with storage medium. 

CanRead

CanWrite

CanSeek

Whether current stream supports reading, seeking or writing
Close() Close the current stream and release any resources associated with the stream
Flush() Updates the underlying data source or repository with the current state of the buffer and then clears the buffer. 
Length  Return the lenght of stream in bytes
Position Determine the position in the current stream

Read()

ReadByte()

Reads a sequence of bytes from the current stream
Seek() Set the position in the current stream
SetLength() Sets the length of the current stream

Write()

WriteByte()

Writes a sequence of bytes to the current stream
   
   
   

 

4.1 Working with FileStream

The FileStream class provides an implementation for the abstract Stream members in a manner appropriate for file-based streaming. It can only read or write a single byte or an array of bytes. Normally, you will not need to interact directly with FileStream class. Instead, you probably use various stream wrapper, which is easier to work with textual data or .net types. 

using (FileStream stream = File.Open (@"C:\test.dat")) {
                string message = "test";

                //convert string to bytes
                byte[] msgAsBytes = Encoding.Default.GetBytes (message);

                //write byte[] to file
                stream.Write (msgAsBytes, 0, msgAsBytes.Length);

                //reset position of stream
                stream.Position = 0;

                //read the data form the file
                byte[] bytesFromFile = new byte[msgAsBytes.Length];
                for (int i = 0; i < msgAsBytes.Length; i++) {
                    bytesFromFile [i] = (Byte)stream.ReadByte();
                }
            }

The major downfall of working directly with FileStream type is it demands to operate on raw bytes. 

 

4.2 Working with StreamWriters and StreamReaders

The StreamWriter and StreamReader classes are useful whenever you need to read or write character-based data. Both of types work by default with Unicode characters. 

StreamReader derives from an abstract type named TextReader. StreamWriter derives from an abstract base class named TextWriter. 

using (StreamWriter writer = File.CreateText ("test.txt")) {
                writer.WriteLine ("test line1");
                writer.WriteLine ("test line2");
                writer.Write (writer.NewLine);
            }

 

            using (StreamReader reader = File.OpenText ("test.txt")) {
                string input = null;
                while ((input = reader.ReadLine ()) != null) {
                    Console.WriteLine (input);
                }
            }

 

4.3 Directly creating StreamReader/StreamWriter type

Instread of calling CreateText()/OpenText() from File class, you can create StreamReader or StreamWriter directly. 

            using (StreamWriter writer = StreamWriter("test.txt")) {
                writer.WriteLine ("test line1");
                writer.WriteLine ("test line2");
                writer.Write (writer.NewLine);
            }

 

4.4 Working with StringReader and StringWriter

You can use the StringWriter and StringReader types to treat textual information as a stream of in-memory characters.

 

            using (StringWriter writer = new StringWriter ()) {
                writer.WriteLine ("test");
                Console.WriteLine (writer);
            }

StreamWriter and StringWriter both derive from the same base class TextWriter. However, StringWriter is able to use GetStringBuilder() method to extract a Stringbuilder object.

            using (StringWriter writer = new StringWriter ()) {
                writer.WriteLine ("test");
                Console.WriteLine (writer);

                StringBuilder sb = writer.GetStringBuilder ();
                sb.Insert (0, "line1");
                sb.Remove (0, "line1".Length);
            }

 

                using (StringReader reader = new StringReader (writer.ToString())) {
                    string input = null;

                    while ((input = reader.ReadLine ()) != null) {
                        Console.WriteLine (input);
                    }
                }

 

4.5 BinaryReader and BinaryWriter

 BinaryReader and BinaryWriter both derive from System.Object, it allows you to read and write discrete data types to an underlying stream in a binary format. 

            FileInfo f = new FileInfo ("test.dat");

            using (BinaryWriter writer = new BinaryWriter (f.OpenWrite ())) {

                double d = 23.43;
                int i = 10;
                string s = "test";

                writer.Write (d);
                writer.Write (i);
                writer.Write (s);
            }

Notice, the constructor of BinaryWriter takes any stream-derived type (FileStream, MemoryStream or BufferedStream)

 

            using (BinaryReader reader = new BinaryReader (f.OpenRead ())) {
                Console.WriteLine (reader.ReadDouble ());
                Console.WriteLine (reader.ReadInt32 ());
                Console.WriteLine (reader.ReadString ());
            }

 

5. Watching file programmatically

FileSystemWatcher, this type can ben quite helpful when you want to monitor files on your system programmatically. 

To begin working with the FileSystemWatcher type, you need to set the Path property to specify the name of the directory that contains the files you want to watch. 

        public static void Main (string[] args)
        {
            FileSystemWatcher watcher = new FileSystemWatcher ();

            try{
                watcher.Path = @"C:\Folder";
            }
            catch(ArgumentException ex) {
                return;
            }
        
            watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite |
            NotifyFilters.FileName | NotifyFilters.DirectoryName;

            watcher.Filter = "*.txt";

            //add event handler
            watcher.Changed += new FileSystemEventHandler(OnChanged);
            watcher.Created += new FileSystemEventHandler (OnChanged);
            watcher.Deleted += new FileSystemEventHandler (OnChanged);

            watcher.EnableRaisingEvents = true;
        }

        static void OnChanged(object source, FileSystemEventArgs e)
        {
            Console.WriteLine ("File: {0}, {1}", e.FullPath, e.ChangeType);
        }

 

6. Object Serialization

The serialization describes the process of persisting the state of an object into a stream. The persisted data sequence contains all the necessary information you need to reconstruct the object for use later. In many cases, saving application data using serialization services results in less code than using readers/writers in System.IO namespace. 

    [Serializable]
    public class Test
    {
        public string state1;
        public string state2;
    }
        public static void Main (string[] args)
        {
            Test test = new Test ();
            test.state1 = "test1";
            test.state2 = "test2";

            BinaryFormatter format = new BinaryFormatter ();

            using (Stream stream = new FileStream ("test.dat", FileMode.Create, FileAccess.Write, FileShare.None)) {
                format.Serialize (stream, test);
            }
        }

 

6.1 Construct objects for serialization 

To make an object available to .net serialization services, all you need to do is decorate each related class with the [Serializable] attribute.  

    [Serializable]
    public class Test
    {
        public string state1;
        public string state2;

        [NonSerialized]
        public string state3;
    }

 

posted @ 2015-05-03 14:45  tim_bo  阅读(158)  评论(0编辑  收藏  举报