Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Basic stuff on Files, Directories and Streams

0.00/5 (No votes)
26 Jun 2002 2  
Demonstrates the use of the reader/writer classes as well as the file/directory info classes

Introduction

Most common applications require reading and writing of files. There are numerous classes in the .NET BCL that provide various methods for creating and manipulating files and directories. In this article I'll cover the basic operations that you might have to perform with files. Since the functionality offered by most of the classes discussed here is too extensive to be completely covered, I only demonstrate some really common  uses of these classes. The readers can then explore the rest of the functionality on their own. The first version of this article used C# examples, but in this refined and expanded version I have used Managed C++. The article comprises of various techniques and is not in the form of a top-to-down tutorial. I welcome requests for more tips or techniques that readers would like to see included in the article.

Getting file information

For this we can use the System.IO.FileInfo class which has several instance methods for performing various file operations. As a short example we'll retrieve some information on Windows Notepad. I've used a native API call to retrieve the Windows directory as I couldn't figure out the .NET way of doing it. I'd be obliged if someone could tell me how to do it using .NET. Show is a user defined function that I use for a padded output.

void FileInfoDemo()
{
    TCHAR WinPath[MAX_PATH+1];
    GetWindowsDirectory(WinPath,MAX_PATH+1);
    String* NotepadPath = WinPath;
    NotepadPath = String::Concat(NotepadPath,S"\\notepad.exe");

    FileInfo* finfo = new FileInfo(NotepadPath);
    Show("Name",finfo->Name);
    Show("Directory",finfo->Directory);
    Show("Extension",finfo->Extension);
    Show("Length",__box(finfo->Length));
    Show("FullName",finfo->FullName);
    Show("CreationTime ",finfo->CreationTime.ToString()); 
}

Basically, all we do is to specify the path to the file in the constructor. Now we can use some very useful Properties to query information regarding our file. The FileInfo class has methods that return various stream readers and writers, but there are better methods to read and write to files as I'll be covering, later in this article.

Enumerate sub-directories and files

We use the DirectoryInfo class to enumerate sub-directories and files in a particular folder. It has two methods among many others called GetFiles and GetDirectories, the former returning a FileInfo array and the latter returning a DirectoryInfo array.

void DirectoryInfoDemo1()
{
    DirectoryInfo* dinfo = new DirectoryInfo("C:\\");
    DirectoryInfo* subdirs[] = dinfo->GetDirectories();
    Console::WriteLine("Sub-Directories for C:\\");
    Console::WriteLine("-----------------------");
    for(int i=0; i<subdirs->Length; i++)
        Show((i+1).ToString(),subdirs->Item[i]);
}

void DirectoryInfoDemo2()
{
    DirectoryInfo* dinfo = new DirectoryInfo("C:\\");
    FileInfo* filesindir[] = dinfo->GetFiles();
    Console::WriteLine("Files under C:\\");
    Console::WriteLine("---------------");
    for(int i=0; i<filesindir->Length; i++)
        Show((i+1).ToString(),filesindir->Item[i]); 
}

Binary files

We can read and write binary files using the BinaryReader and BinaryWriter classes. Just for fun, let's create a simple 16 bit COM format executable. We can write it using just 4 lines of 16 bit assembler as shown below. It simply calls Interrupt 21h Function 02h which outputs the character held in DL. I have also given the machine code equivalents of these assembler statements. So all we do is create an array holding these 8 bytes and then we use the BinaryWriter class to write to a file. Now we can actually run this file and it will print an 'A' on screen.

MOV AH,02h    ; B4 02

MOV DL,41h    ; B2 41

INT 21h       ; CD 21

INT 20h       ; CD 20
void BinaryWriterDemo()
{
    Console::WriteLine("Creating C:\\nish.com");
    Console::WriteLine("This will output an 'A' to the console");
    FileStream* fs = new FileStream("C:\\nish.com",
    FileMode::Create,FileAccess::Write);
    BinaryWriter* bw = new BinaryWriter(fs);
    unsigned char SomeBinaryData __gc[] = 
        {0xb4,0x02,0xb2,0x41,0xcd,0x21,0xcd,0x20};
    bw->Write(SomeBinaryData);
    bw->Flush();
    bw->Close();
}

Now I'll show you how to use the BinaryReader class to read the binary file we created above and display the bytes in the file.

void BinaryReaderDemo()
{
    Console::WriteLine("Reading C:\\nish.com");
    FileStream* fs = new FileStream("C:\\nish.com",
    FileMode::Open,FileAccess::Read);
    BinaryReader* br = new BinaryReader(fs);
    unsigned char c;
    while(br->PeekChar() != -1)
    {
        c = br->ReadByte();
        Console::Write("{0:X2} ",__box(c));
    }
    br->Close();
    Console::WriteLine();
}

One small issue with the BinaryReader.ReadByte Method is that it throws an EndOfStreamException instead of indicating it using a special return value. To avoid handling the exception and doing it the old-C style way, which may or may not be a good thing, I have used PeekChar which will return the next byte but will not move the file pointer.

Text Files

There are more than one way of reading and writing text files, but here I'll show you how to use the StreamWriter and StreamReader classes. They remind me very much of the MFC CStdioFile class. The StreamReader class has a ReadLine method and the StreamWrite class has a WriteLine method both of which are exactly similar in behaviour with the CStdioFile::ReadString and CStdioFile::WriteString methods. Both the classes have constructors that take a file path as argument and thus we don't need to create the FileStream object first as we had to do with the BinaryReader and BinaryWriter classes.

void StreamWriterDemo()
{
    Console::WriteLine("Creating C:\\TempFile.txt");
    StreamWriter *sw = new StreamWriter ("C:\\TempFile.txt");
    sw->WriteLine("This is the first line"); 
    sw->WriteLine("This is the second line"); 
    sw->WriteLine("This is the third/last line"); 
    sw->Close();
}

void StreamReaderDemo()
{
    Console::WriteLine("Reading C:\\TempFile.txt");
    Console::WriteLine("-----------------------");
    StreamReader* sr = new StreamReader ("C:\\TempFile.txt");
    String* s;
    while(s = sr->ReadLine())
        Console::WriteLine(s);
    sr->Close();
}

Reading from a string

They think of everything, don't they. StringReader is a class derived from TextReader that allows us to read from a string directly. There is also a corresponding StringWriter class. I am not very definite about it's purpose, but I guess someone would find some use for it. Maybe it might prove useful for parsing simple strings.

void StringReaderDemo(String* s)
{
    Console::WriteLine("Reading the string [{0}]",s);
    StringReader* sr = new StringReader(s);
    int c;
    while((c = sr->Read()) != -1)
        Console::Write("{0} ",__box(Convert::ToChar(c)));
    Console::WriteLine(); 
    sr->Close();
} 

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here