I covered the basics of opening and
reading a file through the File
and
FileInfo
objects in Chapter 2.
In this section, I’ll focus on writing to a file
using the same objects.
To
begin with, File
has a Create(
)
method. This method takes a filename as a parameter and
returns a FileStream
, so the most basic creation
and writing to a file is fairly intuitive. Stream
and its subclasses implement a variety of Write( )
methods, including one that writes an array of bytes to the
Stream
. The following code snippet creates a file
named myfile.txt and writes the text
.NET & XML
to it:
byte [ ] buffer = new byte [ ] {46,78,69,84,32,38,32,88,77,76}; string filename = "myfile.txt"; FileStream stream; stream = File.Create(filename); stream.Write(buffer,0,buffer.Length);
That byte array is an awkward way to write a string to a
Stream
; ordinarily, you wouldn’t
hardcode an array of bytes like that. I’ll show you
a more typical way of encoding a string as a byte array in a moment.
If the file already exists, the previous code overwrites the files’s current contents. You may not want to do that in practice; you may prefer to append to the file if it already exists. You can handle this very easily in .NET in several different ways. This snippet shows one way, with the changes highlighted:
byte [ ] buffer = new byte [ ] {46,78,69,84,32,38,32,88,77,76}; string filename = "myfile.txt"; FileStream stream; if (File.Exists(filename)) { // it already exists, let's append to it stream = File.OpenWrite(filename); stream.Seek(0,SeekOrigin.End); } else { // it does not exist, let's create it stream = File.Create(filename); } stream.Write(buffer,0,buffer.Length);
SeekOrigin
is an enumeration in the
System.IO
namespace that indicates where the
Seek( )
method should seek from. In this code,
I’m seeking 0 bytes from the end, but you could also
seek from the beginning of the Stream
(SeekOrigin.Begin
) or from the current position
(SeekOrigin.Current
).
There are several other ways to open a file for writing. For example, this snippet shows several changes from the previous one. The changes are highlighted:
byte [ ] buffer = new byte [ ] {46,78,69,84,32,38,32,88,77,76}; string filename = "myfile.txt"; FileStream stream; FileMode fileMode; if (File.Exists(filename)) { // it already exists, let's append to it fileMode = FileMode.Append; } else { // it does not exist, let's create it fileMode = FileMode.CreateNew; } stream = File.Open(filename,fileMode,FileAccess.Write,FileShare.None); stream.Write(buffer,0,buffer.Length);
The
File.Open( )
method has several overloads with
additional parameters. The FileMode
enumeration
specifies what operations the file is to be opened for. Table 3-1 lists the FileMode
enumerations.
Value |
Description |
CreateNew |
A new file will be created. If it already exists, an
|
Create |
A file will be created, or, if it already exists, truncated and overwritten. |
Open |
An existing file will be opened. If it does not exist, a
|
OpenOrCreate |
If the file exists, it will be opened; if it does not, a new file will be created. |
Truncate |
An existing file will be opened and truncated to zero bytes long. An attempt to read a truncated file will result in an exception being thrown. |
Append |
If the file exists, it will be opened and data will be written at the
end of the file. If the file does not exist, a new file will be
created. Any attempt to |
The
FileAccess
enumeration restricts the operations a
program can exercise on the file, once it has been opened. Table 3-2 details the FileAccess
enumerations.
Value |
Description |
Read |
Data can be read from the file. |
Write |
Data can be written to the file. |
ReadWrite |
Data can be read from or written to the file. Equivalent to
|
FileShare
restricts what operations
other applications can exercise. Table 3-3 describes the FileShare
enumerations.
Value |
Description |
None |
No other process may access the file as long as this process has it open. |
Read |
Subsequent requests to read from the file by other processes will succeed, if they have the other appropriate permissions. |
Write |
Subsequent requests to write to the file by other processes will succeed, if they have the other appropriate permissions. |
ReadWrite |
Subsequent requests to read from and write to the file by other
processes will succeed, if they have the other appropriate
permissions (equivalent to |
Inheritable |
The file handle is inheritable by child processes. This is not directly supported by Win32. |
Having to create an array of bytes to
write with the Stream.Write( )
method is a bit
tiresome. Luckily, there are at least two ways to work around this.
The first is System.Text.Encoding
. This class
contains methods to convert string
s to and from
byte
arrays, for a given number of standard
encodings, including ASCII, UTF-8, and UTF-16. These encodings are
provided as static properties of the Encoding
class. String
s in .NET are stored in
Unicode—while ASCII characters are each stored in a single
byte, Unicode characters are stored in four bytes. The
GetBytes( )
method takes a .NET
string
and returns an array of bytes in the
appropriate encoding, suitable for use by Stream.Write(
)
:
Unicode is a standard that provides a unique four-byte representation
of every character in every alphabet, on any computer operating
system and platform. Happily, XML and .NET both use encodings of
Unicode by default. For more information about Unicode, visit the
Unicode Consortium’s web site at http://www.unicode.org/
.
string message = "Hello, world."; byte [ ] buffer = Encoding.ASCII.GetBytes(message); string filename = "myfile.txt"; FileStream stream; FileMode fileMode; if (File.Exists(filename)) { // it already exists, let's append to it fileMode = FileMode.Append; } else { // it does not exist, let's create it fileMode = FileMode.CreateNew; } stream = File.Open(filename,fileMode,FileAccess.Write,FileShare.None); stream.Write(buffer,0,buffer.Length);
Just to belabor the point, remember
that a C# byte
is the familiar eight-bit byte, but
a C# char
is a Unicode character. The encodings
defined in the .NET Framework are shown in Table 3-4.
Class name |
Property name |
Description |
|
|
7 bit ASCII, used in the snippet above. Support is required for XML processors. |
|
|
Big-endian Unicode. Little-endian Unicode. |
|
Represents the default encoding for the current system. | |
|
|
UTF-7. |
|
|
UTF-8. Support is required for XML processors. |
In addition, any other encodings are
accessible by calling Encoding.GetEncoding( )
and
passing the code page or encoding name.
The other way to write a stream of
characters is even simpler. A FileStream
is a
subclass of Stream
, which can be used as a
parameter to the StreamWriter
’s
constructor. StreamWriter
is analogous to
StreamReader
, and is a subclass of
TextWriter
, which is optimized to write textual
data to a Stream
. The
TextWriter
’s Write(
)
and WriteLine( )
methods take care of
the encoding of various datatypes when writing to a
Stream
:
string textToWrite = "This is the text I want to write to the file."; string filename = "myfile.txt"; FileStream stream; FileMode fileMode; if (File.Exists(filename)) { // it already exists, let's append to it fileMode = FileMode.Append; } else { // it does not exist, let's create it fileMode = FileMode.CreateNew; } stream = File.Open(filename,fileMode,FileAccess.Write,FileShare.None); StreamWriter writer = new StreamWriter(stream); writer.Write(textToWrite); writer.Flush( ); writer.Close( );
The last two lines of this
code snippet cause the output buffer to be flushed, and the file to
be closed. Every time you write to the file and you want the file on
disk to reflect the changes immediately, it is important to call
Flush( )
on the Stream
or
StreamWriter
. You can also indicate that the
contents of the file are to be flushed to disk automatically by
setting AutoFlush
to true:
StreamWriter writer = new StreamWriter(stream);
writer.AutoFlush = true;
writer.Write(textToWrite);
When you
are completely done with a file, you should call Close(
)
on the File
or the
Stream
. If you don’t call
Close( )
yourself, the file will be closed when
the garbage collector cleans up the method’s local
variables. Unfortunately, you don’t know when that
will happen, so it’s always best to close files
yourself.
You can close the underlying file by calling Close(
)
on the FileStream
because, by default,
when you instantiate a FileStream
, it owns the
underlying file. It’s important to remember that the
FileStream
owns the underlying file handle in case
you open several streams on the same file; closing any one of them
will cause all of them to be closed.
There’s an even
quicker way to append to an existing file. The
StreamWriter
class has a constructor that takes a
filename as a parameter. Since StreamWriter
inherits from TextWriter
, it implements the
IDisposable
interface, which allows you to use the
using
keyword to automatically close the
Stream
at the end of the using
block.
All the code you wrote above could instead be simplified to five lines, if all you want to do is write a quick chunk of text to a file:
string textToWrite = "This is the text I want to write to the file."; string filename = "myfile.txt"; using (StreamWriter writer = new StreamWriter(filename,true)) { writer.Write(textToWrite); }
The second parameter to the StreamWriter
constructor indicates that the text is to be appended to the file if
the file already exists.
13.58.247.31