Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Pro Visual C++-CLI And The .NET 2.0 Platform (2006) [eng]-1

.pdf
Скачиваний:
70
Добавлен:
16.08.2013
Размер:
24.18 Mб
Скачать

288

C H A P T E R 8 I N P U T , O U T P U T , A N D S E R I A L I Z A T I O N

Figure 8-2 shows the results of the FileInfo.exe program.

Figure 8-2. Results of FileInfo.exe

Opening Files

There is no shortage of ways that you can open a file using the .NET Framework class library. There are 14 methods combined in the File and FileInfo class (see Table 8-4). Many of these methods have numerous parameter combinations. Both File and FileInfo use the same 7 method names, and each of the methods with the same name do the same thing. Though the methods have the same name, the parameters passed differ, or at least the first parameter differs.

There always seems to be one exception. The File::Create() has an overloaded method that has a buffer size parameter that the FileInfo class’s Create() method lacks.

Table 8-4. Opening a File Using the File and FileInfo Classes

Method

Description

Open()

Creates a FileStream to a file providing a plethora of read/write and share

 

privilege options

Create()

Creates a FileStream providing full read and write privileges to a file

OpenRead()

Creates a read-only FileStream to an existing file

OpenWrite()

Creates a write-only unshared FileStream to a file

AppendText()

Creates a StreamWriter that appends text to the end of an existing file

CreateText()

Creates a StreamWriter that writes a new text file

OpenText()

Creates a StreamReader that reads from an existing file

 

 

You will see FileStream, StreamWriter, and StreamReader later in this chapter.

Of these 14 (7×2) methods, only 2 actually take any parameters (other than the name of the file you wish to open for the static methods). Basically, the .NET Framework class library provides 2 equivalent file open methods and 12 shortcuts.

C H A P T E R 8 I N P U T , O U T P U T , A N D S E R I A L I Z A T I O N

289

The Open Methods

There are only two root open methods in the .NET Framework class library: File::Open() and FileInfo::Open(). These methods are virtually the same, except the File::Open() method has one additional parameter: the path to the file you want to open. The FileInfo::Open() method gets this information from its constructor.

The Open() method is made up of three overloaded methods. Each overload provides progressively more information about how you want the file opened. The first overload takes as a parameter the file mode with which you wish to open the file (see Table 8-5). Because the other two parameters are not specified, the file will open by default with read/write access and as unshared.

FileInfo

^fileinfo = gcnew FileInfo("file.dat");

FileStream

^fs =

fileinfo.Open(FileMode::Truncate);

// or

 

 

FileStream

^fs =

File::Open("file.dat", FileMode::CreateNew);

Table 8-5. FileMode Enumeration Values

FileMode

Description

Append

Opens a file if it exists and sets the next write point to the end of the file. If the file

 

does not exist, it creates a new one. You can only use FileMode::Append with a

 

file access of write-only, as any attempt to read throws an ArgumentException.

Create

Creates a new file. If the file already exists, it will be overwritten.

CreateNew

Creates a new file. If the file already exists, an IOException is thrown.

Open

Opens an existing file. If the file does not exist, a FileNotFoundException is thrown.

OpenOrCreate

Opens an existing file. If the file does not exist, it creates a new file.

Truncate

Opens an existing file and truncates it to a length of 0 bytes. If the file does not

 

exist, a FileNotFoundException is thrown.

 

 

The second overload takes the additional parameter of the file access you require the file to have (see Table 8-6). The file will also be opened by default as unshared.

FileInfo ^fileinfo = gcnew FileInfo("file.dat");

FileStream ^fs = fileinfo->Open(FileMode::Truncate, FileAccess::ReadWrite); // or

FileStream ^fs = File::Open("file.dat", FileMode::Append, FileAccess::Write);

Table 8-6. FileAccess Enumeration Values

FileAccess

Description

Read

Allows data only to be read from the file

ReadWrite

Allows data to be read from and written to the file

Write

Allows data only to be written to the file

 

 

290

C H A P T E R 8 I N P U T , O U T P U T , A N D S E R I A L I Z A T I O N

The final overload has one more parameter. It specifies how the file is shared with others trying to access it concurrently (see Table 8-7).

FileInfo

^fileinfo = gcnew FileInfo("file.dat");

FileStream

^fs = fileinfo->Open(FileMode::Truncate, FileAccess::ReadWrite,

 

FileShare::Read);

// or

FileStream ^fs = File::Open("file.dat", FileMode::Append, FileAccess::Write,

 

FileShare::None);

Table 8-7. FileShare Enumeration Values

 

 

FileShare

Description

 

 

None

Specifies exclusive access to the current file. Subsequent openings of the file by a

 

process, including the current one, will fail until the file closes.

Read

Specifies that subsequent openings of the file by a process, including the current

 

one, will succeed only if it is for a FileMode of Read.

ReadWrite

Specifies that subsequent openings of the file by a process, including the current

 

one, will succeed for either reading or writing.

Write

Specifies that subsequent openings of the file by a process, including the current

 

one, will succeed only if it is for a FileMode of Write.

 

 

All those parameters make the file open process very configurable, but also a little tedious. This is especially true if you just want to open the file in a very generic and standard way. The .NET Framework class library provides you with a way to simplify file opening if the way you want to open a file happens to fall in one of six standard open configurations.

FileInfo

^fileinfo

= gcnew FileInfo("file.dat");

FileStream

^CreateFile

= fileinfo.Create();

FileStream

^OpenReadFile

= fileinfo.OpenRead();

FileStream

^OpenWriteFile

= fileinfo.OpenWrite();

StreamWriter

^AppendTextFile

= fileinfo.AppendText();

StreamWriter

^CreateTextFile

= fileinfo.CreateText();

StreamReader

^OpenTextFile

= fileinfo.OpenText();

// or

 

 

FileStream

^CreateFile

= File::Create("file.dat");

FileStream

^OpenReadFile

= File::OpenRead("file.dat");

FileStream

^OpenWriteFile

= File::OpenWrite("file.dat");

StreamWriter

^AppendTextFile

= File::AppendText("file.dat");

StreamWriter

^CreateTextFile

= File::CreateText("file.dat");

StreamReader

^OpenTextFile

= File::OpenText("file.dat");

Notice that none of the preceding file opening methods takes any parameters, except the file path in the case of the static method of the File class. Personally, I think the names of the methods make them pretty self-explanatory.

C H A P T E R 8 I N P U T , O U T P U T , A N D S E R I A L I Z A T I O N

291

I/O Manipulation

Okay, you now have a file open and it is time to actually do something with it. Oops, did I say “file”? Files are only one thing that you can do I/O manipulation with. You can also do I/O manipulation in and out of memory using the MemoryStream and BufferedStream classes and in and out of network sockets using NetworkStream. You will look at the MemoryStream class a little later to see how it differs from a FileStream.

There are several different means to accomplish I/O manipulation. You will examine the three most common: using Streams, using TextReaders and TextWriters, and using BinaryReaders and BinaryWriters. Figure 8-3 shows the class hierarchy for manipulating files.

Figure 8-3. The class hierarchy for I/O manipulation

Using Streams

In the computer world, streams are a method of transferring a sequential stream of data to and from one source to another in either a synchronous or asynchronous manner. The .NET Framework class library sends this data as a stream of bytes. A stream can also transfer these blocks of data starting from any location in one source to any location in another source.

What does this mean to you? Basically, you can read data, write data, and adjust the current location where you access the data. Not much to it, is there?

All stream-based I/O in the .NET Framework class library derives from the abstract base class Stream. The Stream class contains several virtual methods, which the inheriting class must define (see Table 8-8). Basically, these virtual methods define core Stream functionality and thus ensure that the inheriting class satisfies the definition of a stream as stated previously.

292 C H A P T E R 8 I N P U T , O U T P U T , A N D S E R I A L I Z A T I O N

Table 8-8. The Virtual Methods and Properties of the Stream Class

Member

Description

CanRead

A Boolean value specifying whether reading is supported.

CanSeek

A Boolean value specifying whether seeking is supported.

CanWrite

A Boolean value specifying whether writing is supported.

Close()

A method that closes the file and releases resources associated with the stream.

Flush()

This method moves the data from the source buffer to its destination source

 

and then clears the buffer. If the stream does not support a buffer, this method

 

does nothing.

Length

The length of the stream in bytes.

Position

If seeking is supported, then this property can be used to get or set the position

 

in the stream.

Read()

Reads a specified number of bytes from the stream and then advances the position

 

after the last read byte.

ReadByte()

Reads a single byte from the stream and then advances the position after the byte.

Seek()

If seeking is supported, then this method can be used to set the position in

 

the stream.

SetLength()

Sets the length of the stream in bytes.

Write()

Writes a specified number of bytes to the stream and then advances the position

 

after the last written byte.

WriteByte()

Writes one byte to the stream and then advances the position after the byte.

 

 

You will see some of these properties and methods implemented in the following stream implementations.

FileStreams

One of the most common implementations of a Stream is the FileStream class. This class provides implementations for the abstract Stream class so that it can perform file-based streaming. Or, in other words, it allows you to read from and write to a file.

You have already seen several ways to open a FileStream. It is also possible to open a FileStream directly without using File or FileInfo. To do this, you use one of the FileStream’s many constructors. The most common parameters passed to the constructor are identical to those passed to the static

File::Open() method.

FileStream ^fs = gcnew FileStream("file.dat", FileMode::CreateNew);

FileStream ^fs = gcnew FileStream("file.dat", FileMode::Append,

FileAccess::Write);

FileStream ^fs = gcnew FileStream("file.dat", FileMode::Create,

FileAccess::Write, FileShare::None);

Once you finally have the FileStream open, you can start to read and/or write Bytes of data from or to it. As you saw from the virtual methods defined by the Stream class in Table 8-8, there are two ways of reading and writing to a stream. You can do it either by individual unsigned chars or by arrays of unsigned chars.

C H A P T E R 8 I N P U T , O U T P U T , A N D S E R I A L I Z A T I O N

293

array<unsigned char>^ data = { 'A', 'p', 'p', 'l', 'e' }; fso->Write(data, 0, 4);

fso->WriteByte(data[4]);

array<unsigned char>^ ca = gcnew array<unsigned char>(5); ca[0] = fsi->ReadByte();

fsi->Read(ca, 1, 4);

Simply placing the location in the Position property sets the location of the next place to read from or write to the file.

fsi->Position = 0;

You can also set the location of the next read or write by the Seek() method. This method allows you to use offsets from the beginning of the file (same as the Position property), the current location, or the end of the file.

fsi->Seek(0, SeekOrigin::Begin);

If you desire further access but want the data available in the file (for another operation or just for safety), flush the file buffer.

fso->Flush();

You should always close your files after you are done with them.

fso->Close();

Listing 8-3 shows the FileStream class in action and demonstrates many of the functionalities described previously.

Listing 8-3. Working with a FileStream

using namespace System; using namespace System::IO;

void main()

{

FileStream ^fso = gcnew FileStream("file.dat", FileMode::Create, FileAccess::Write, FileShare::None);

array<unsigned char>^ data = gcnew array<unsigned char> { 'T', 'h', 'i', 's', ' ', 'i', 's', ' ', 'a', ' ', 't', 'e', 's', 't', '!', '\r', '\n', 'T', 'h', 'i', 's', ' ', 'i', 's', ' ', 'o', 'n', 'l', 'y', ' ', 'a', ' ', 't', 'e', 's', 't', '.','\r', '\n' };

for (int i = 0; i < data->Length-5; i += 5)

{

fso->Write(data, i, 5);

}

for (int i = data->Length -4; i < data->Length; i++)

{

fso->WriteByte(data[i]);

}

294

C H A P T E R 8 I N P U T , O U T P U T , A N D S E R I A L I Z A T I O N

fso->Close();

FileInfo ^fi = gcnew FileInfo("file.dat");

FileStream ^fsi = fi->OpenRead();

int b;

while ((b = fsi->ReadByte()) != -1)

{

Console::Write((Char)b);

}

fsi->Position = 0;

array<unsigned char>^ ca = gcnew array<unsigned char>(17); fsi->Read(ca, 0, 17);

for (int i = 0; i < ca->Length; i++)

{

Console::Write((Char)ca[i]);

}

Console::WriteLine();

fsi->Close();

fi->Delete(); // If you want to get rid of it

}

Figure 8-4 shows the file output generated by the FileStream.exe program.

Figure 8-4. File output of FileStream.exe

MemoryStreams

Programming with a MemoryStream is not much different from working with a FileStream. Obviously, what’s happening behind the scenes, on the other hand, is completely different. You’re no longer dealing with files; instead, you’re dealing with computer memory.

There are only a few differences from a coding perspective when you deal with a MemoryStream. Obviously, the constructor is different.

MemoryStream ^fs = gcnew MemoryStream();

A MemoryStream has an additional property and a couple of unique methods (see Table 8-9).

C H A P T E R 8 I N P U T , O U T P U T , A N D S E R I A L I Z A T I O N

295

Table 8-9. Additional MemoryStream Property and Methods

Member

Description

Capacity

This property gets or sets the number of bytes allocated to the stream.

GetBuffer()

This method returns an unsigned array of bytes that the stream created.

WriteTo()

This method writes the contents of the MemoryStream to another stream, which

 

comes in handy if you want to write the stream out to a FileStream.

 

 

Listing 8-4 shows the MemoryStream class in action and demonstrates many of the functionalities described previously.

Listing 8-4. Working with a MemoryStream

using namespace System; using namespace System::IO;

void main()

{

array<unsigned char>^ data = gcnew array<unsigned char> { 'T', 'h', 'i', 's', ' ', 'i', 's', ' ', 'a', ' ', 't', 'e', 's', 't', '!', '\r', '\n', 'T', 'h', 'i', 's', ' ', 'i', 's', ' ', 'o', 'n', 'l', 'y', ' ', 'a', ' ', 't', 'e', 's', 't', '.','\r', '\n' };

MemoryStream ^ms = gcnew MemoryStream(); ms->Capacity = 40;

for (int i = 0; i < data->Length-5; i += 5)

{

ms->Write(data, i, 5);

}

for (int i = data->Length -4; i < data->Length; i++)

{

ms->WriteByte(data[i]);

}

array<unsigned char>^ ca = ms->GetBuffer(); for each (unsigned char c in ca)

{

Console::Write((Char)c);

}

Console::WriteLine();

FileStream ^fs = File::OpenWrite("file.dat");

ms->WriteTo(fs);

fs->Close(); ms->Close();

}

296

C H A P T E R 8 I N P U T , O U T P U T , A N D S E R I A L I Z A T I O N

Figure 8-5 shows a display of the buffer contained within the MemoryStream. Figure 8-6 shows the results displayed to the console. Figure 8-7 shows the resulting file output generated by the MemoryStream.exe program. Notice that Figures 8-5 through 8-7 all have the same results, as expected.

Figure 8-5. Display of the buffer of the MemoryStream created by MemoryStream.exe

C H A P T E R 8 I N P U T , O U T P U T , A N D S E R I A L I Z A T I O N

297

Figure 8-6. Console results of MemoryStream.exe

Figure 8-7. File output of MemoryStream.exe

Using StreamReaders and StreamWriters

A drawback when using a FileStream is that it isn’t very String- or character-friendly. Because what you often want to store are Strings and characters, it only makes sense that methods be made to optimize and simplify the process of writing these to a stream. This is where the StreamReader and StreamWriter classes become helpful.

Just like the Stream class, the abstract StreamReader and StreamWriter classes define all the functionality that needs to be implemented to support String and character reading and writing (see Tables 8-10 and 8-11).

Table 8-10. Common StreamReader Members

Method

Description

Close()

Closes the file and releases any resources

Peek()

Reads the next character without advancing the stream pointer

Read()

Reads data from the input stream

ReadBlock()

Reads a specified number of characters from the stream to a specified starting

 

location in an input buffer

ReadLine()

Reads a line of data from the input stream and returns it as a String

ReadToEnd()

Reads the rest of the data from the current file location to the end and returns it

 

as a single String