Recipe12.6.Accessing Part of a File Randomly


Recipe 12.6. Accessing Part of a File Randomly

Problem

When reading a file, you sometimes need to move from the current position in a file to a position some number of characters before or after the current position, including to the beginning or the end of a file. After moving to this point, you can add, modify, or read the information at this new point in the file.

Solution

To move around in a stream, use the Seek method. The following method writes the string contained in the variables theFirstLine and theSecondLine to a file in this same order. The stream is then flushed to the file on disk:

 public static void CreateFile(string theFirstLine, int theSecondLine) {     using (FileStream fileStream = new FileStream("data.txt",             FileMode.Create,             FileAccess.ReadWrite,             FileShare.None))     {         using (StreamWriter streamWriter = new StreamWriter(fileStream))         {             streamWriter.WriteLine(theFirstLine);             streamWriter.WriteLine(theSecondLine);             streamWriter.Flush( );         }     } } 

If the following code is used to call this method:

 CreateFile("This is the first line.", 1020304050); 

the resulting data.txt file will contain the following text:

 This is the first line. 1020304050 

The ModifyFile method, shown in Example 12-2, uses the Seek method to reposition the current file position at the end of the first line. A new line of text is then added between the first and second lines of text in the file. Finally, the Seek method is used to place the current position pointer in the file to the end, and a final line of text is written to this file.

Example 12-2. ModifyFile method

 public static void ModifyFile(int theSecondLine) {     // Open the file for read/write.     using (FileStream fileStream =                 File.Open("data.txt",                     FileMode.Open,                     FileAccess.ReadWrite,                     FileShare.None))     {        Using (StreamWriter streamWriter = new StreamWriter(fileStream))        {            // Backup over the newline.            int offset = streamWriter.NewLine.Length;            // Backup over the second line.            offset += (theSecondLine.ToString( ).Length);            // Make negative.            offset = -offset;            // Move file pointer to just after first line.            streamWriter.BaseStream.Seek(offset, SeekOrigin.End);            StringBuilder stringBuilder                 = new StringBuilder("This line added by seeking ");            stringBuilder.AppendFormat(                "{0} chars from the end of this file.",offset);                 streamWriter.WriteLine(stringBuilder);            streamWriter.Flush( );            streamWriter.BaseStream.Seek(0, SeekOrigin.End);            streamWriter.WriteLine("This is the last line" +                ", added by seeking to the end of the file.");        }     } } 

If the following code is used to call this method:

 ModifyFile(1020304050); 

the resulting data.txt file will contain the following text:

 This is the first line. This line added by seeking -12 chars from the end of this file. This is the last line, added by seeking to the end of the file. 

The next method, ReadFile, reads the file that we just created. First, the current position pointer in the file is moved to the end of the first line (this line contains the string in the variable theFirstLine). The ReadToEnd method is invoked reading the rest of the file (the second and third lines in the file) and the results are displayed:

 public static void ReadFile(string theFirstLine) {     using (StreamReader streamReader = new StreamReader("data.txt"))     {         streamReader.BaseStream.Seek(           theFirstLine.Length + Environment.NewLine.Length, SeekOrigin.Begin);         Console.WriteLine(streamReader.ReadToEnd( ));     } } 

The following text is displayed:

 This line added by seeking -12 chars from the end of this file. This is the last line, added by seeking to the end of the file. 

If you are wondering where the line of text that reads:

 1020304050 

is located, it was overwritten when we did the first Seek while writing data to this file.

Discussion

File seeking is the placement of the pointer to the current location in an opened file anywhere betweenand includingthe beginning and ending bytes of a file. Seeking is performed through the use of the Seek method.

This method returns the new location of the file pointer in the file.

Seeking is performed in one of three ways: as an offset from the beginning of the file, as an offset from the end of the file, or as an offset from the current location in the file, as shown here:

 public static void MoveInFile(int offsetValue) {     Using (FileStream fileStream =               File.Open("data.txt",                   FileMode.Open,                   FileAccess.ReadWrite,                   FileShare.None));     {         Using (StreamWriter streamWriter = new StreamWriter(fileStream))         {             // Move from the beginning of the file.             streamWriter.BaseStream.Seek(offsetValue, SeekOrigin.Begin);             // Move from the end of the file.             streamWriter.BaseStream.Seek(offsetValue, SeekOrigin.End);            // Move from the current file pointer location in the file.            streamWriter.BaseStream.Seek(offsetValue, SeekOrigin.Current);         }     } } 

offsetValue may be any positive or negative number as long as it does not attempt to force the file pointer before the beginning of the file or after the end. The SeekOrigin.Begin enumeration value starts the offset at the beginning of the file; likewise, the SeekOrigin.End value starts the offset at the end of the file. The SeekOrigin.Current value starts the offset at the current location of the file pointer. You must take extra care not to force the file pointer to a point before the start of the file when using the seek method with a negative offset, since this action could move the file pointer before the beginning of the file. If you think about it logically, you should be giving positive values when specifying SeekOrigin.Begin and negative values when specifying SeekOrigin.End; any value makes sense for SeekOrigin.Current, so long as it doesn't cause the pointer to roll past the beginning of the file. To prevent an IOException from being thrown in this circumstance, you can test for this condition in the manner shown in Example 12-3.

Example 12-3. Testing for the beginning or end of a file

 long offsetValue = -20; using (FileStream fileStream =            File.Open("data.txt",                FileMode.Open,                FileAccess.ReadWrite,                FileShare.None)) {    Using (StreamWriter streamWriter = new StreamWriter(fileStream))    {        if ((offsetValue + streamWriter.BaseStream.Position) >= 0)        {           streamWriter.BaseStream.Seek(offsetValue, SeekOrigin.Current);        }         else         {           Console.WriteLine("Cannot seek outside of the file.");        }        if ((offsetValue + streamWriter.BaseStream.Length) >= 0)        {           streamWriter.BaseStream.Seek(offsetValue, SeekOrigin.End);        }        else        {           Console.WriteLine("Cannot seek outside of the file.");        }        if (offsetValue >= 0)        {            streamWriter.BaseStream.Seek(offsetValue, SeekOrigin.Begin);        }        else         {            Console.WriteLine("Cannot seek outside of the file.");         }    } } 

To seek to the beginning of a file, use the following code:

 streamWriter.BaseStream.Seek(0, SeekOrigin.Begin); 

To seek to the end of a file, use the following code:

 streamWriter.BaseStream.Seek(0, SeekOrigin.End); 

In these calls, the SeekOrigin enumeration value sets the file pointer to the beginning or end of a file. The offset, which is zero, does not force the file pointer to move. With this in mind, realize that using zero as an offset to SeekOrigin.Current is pointless because you don't move the pointer at all, and you are killing clock cycles to no effect.

See Also

See the "FileStream Class," "StreamReader Class," "StreamWriter Class," "BinaryReader Class," "BinaryWriter Class," and "SeekOrigin Enumeration" topics in the MSDN documentation.



C# Cookbook
Secure Programming Cookbook for C and C++: Recipes for Cryptography, Authentication, Input Validation & More
ISBN: 0596003943
EAN: 2147483647
Year: 2004
Pages: 424

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net