Click here to Skip to main content
15,898,371 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I am struggling with strange behavior of a method using filestream read. The filestream object length is sometimes different from the actual file length. If the method is called after starting in debug then it consistently doesn't work right. If I call an open file dialog for the same path but different file first then the object length is correct until the program is stopped and restarted. There is no connection between the two methods except they use the same directory. It appears that it is reading the file more than once. It never throws an error even when the file length is wrong. Is there some known filestream bug?

Here's the method.

C#
private List<ushort> loadDesignFiles(string ext)
       {
           ushort[] intbuf = new ushort[1];
           const int MSB = 0;
           const int LSB = 1;
           int groupNum;
           FileInfo[] fi = null;

           DirectoryInfo dir = new DirectoryInfo(userDirectory);

           try
           {
               fi = dir.GetFiles("*." + ext);
               //clear all previous files
               desAllGroups.Clear();

               foreach (FileInfo thisFi in fi)
               {
                   //get group number from the file
                   int.TryParse(thisFi.ToString().Substring(5, 2), out groupNum);

                   FileStream inFile = new FileStream(thisFi.ToString(), FileMode.Open);

                   //This buffer is half the length of the input file
                   //since it is the combination of every two bytes
                   intbuf = new ushort[inFile.Length / 2];
                   byte[] wordbuf = new byte[2];

                   //combine every two consecutive bytes into int16
                   //low address is MSByte plus design curve number, next is LSByte
                   //MSB upper 4 bits contain design curve number in D14, D13 (0-3)

                   for (int j = 0; j < intbuf.Length; j++)
                   {
                       inFile.Read(wordbuf, 0, 2);
                       intbuf[j] = (ushort)(wordbuf[LSB] | (wordbuf[MSB] << 8));
                   }

                   //close file after use
                   inFile.Close();
                   //concat all groups
                   //new curves start a multiples of 2010d (7DAh)
                   //first char in each curve file is curve number
                   desAllGroups.AddRange(intbuf);
               }
           }
           catch (IOException ex)
           {
               MessageBox.Show(ex.Message);
           }
           catch (Exception ex)
           {
               MessageBox.Show(ex.Message);
           }

           if (desAllGroups.Count == 0)
               MessageBox.Show("No Design Files Found", "Warning");
           else MessageBox.Show("Loaded " + fi.Length + " Design Files", "Note");
           return desAllGroups;
       }
Posted
Updated 7-Jul-10 7:13am
v2
Comments
William Winner 7-Jul-10 13:52pm    
Just one comment about your Try/Catch block. Encircling all of that code within the Try/Catch won't always provide you with the information you may need later to track down your errors. In some instances with compiling, the line numbers will not reflect the actual line numbers for your code. So, trying to track down an error can be tricky.

Ideally, you should enclose something only if you think it would throw an exception. You should also try to handle issues that could occur before they throw an exception.

For instance, desAllGroups.Clear() will only throw an exception is desAllGroups is null. So, that doesn't need to be within a Try/Catch block.

It takes more work to isolate your Try/Catch blocks, but doing so could prevent a lot of headaches in the future if you end up with a bug.

Also, if you're not going to treat the different types of exceptions differently, then why have multiple catch blocks?
Phil Gaudet 7-Jul-10 15:24pm    
Thanks for the critique. I am a long way from becoming an expert (if ever). I think I discovered an error that did not fall into the IOException catagory and added the generic catch handler without deleting the original one.

I've personally never seen this particular issue, but I've seen code written by other developers who had to santize the value of the file stream length (probably due to the issue you are talking about).

I'd approach it from two possible ways:

1) Try using FileInfo.Length (e.g. thisFi.Length)

2) Capture the return value of Read and use it to just keep reading until you're out of data.

Btw, there's no reason for the intbuf as you're just adding the values to the List<ushort>. Just have one ushort local variable and add that immediately to the list. Secondly, I'd use a using statement for the FileStream to ensure you close your files out in a timely manner. I hope that helps.
 
Share this answer
 
v2
Thanks for the quick reply.
Well, just after I posted this I found the reason for the problem. It is ignoring the dir path that I give it and reading a couple of files in the \bin\debug folder that happen to have the same names. I changed the line

FileStream inFile = new FileStream(userDirectory+thisFi.ToString(), FileMode.Open);

to add the correct directory and that fixed it.


Your comment about intbuf leads me to a second question. I really want to create a separate list for each file found. Is there a way to make a list declaration in a loop that can create a unique identifier for each list (such as listFile1, listFile2 etc) or do I need to create an vector array of lists?
 
Share this answer
 
v2
Comments
Kythen 7-Jul-10 16:57pm    
Instead of using the FileStream constructor, you could use FileStream inFile = thisFi.OpenRead() instead. I would also advise using FileInfo.Name or FileInfo.FullPath instead of ToString() to get the file name.
You would do something like the following. I did notice your original code had an issue with the for loop not iterating over "Length / 2". So, that may have given you some of your problems as well. Plus, with this code, you'll want to verify the following:

-End of file case is handled properly.
-Byte to ushort conversion is valid for your case (ie. little endian vs. big endian) - I believe it is, but double check.

private Dictionary<string, List<ushort>> loadDesignFiles(string ext)
{
    Dictionary<string, List<ushort>> dctAllGroups;
    List<ushort> lstWords;
    byte[] wordbuf;
 
    dctAllGroups = new Dictionary<string, List<ushort>>();
    wordbuf = new byte[2];
 
    foreach (string strFileName in Directory.GetFiles(userDirectory, "*." + ext))
    {
        dctAllGroups[strFileName] = lstWords = new List<ushort>();

        using (FileStream inFile = File.OpenRead(strFileName))
        {
            while (inFile.Read(wordbuf, 0, 2) == 2)
            {
                // Combine every two consecutive bytes into uint16
                lstWords.Add(BitConverter.ToUInt16(wordbuf, 0));
            }
        }
    }
 
    if (dctAllGroups.Count == 0)
        MessageBox.Show("No Design Files Found", "Warning");
    else
        MessageBox.Show("Loaded " + dctAllGroups.Count + " Design Files", "Note");
 
    return dctAllGroups;
}

The more you develop and read other people's code, the better you'll get. Just keep in there.
 
Share this answer
 
Comments
Phil Gaudet 7-Jul-10 17:01pm    
Thanks, this is more sophisticated than the stuff I do except during those short lucid caffeine moments. So the string in the dictionary is used to index the ushort list? The data is big-endian. Does the BitConverter default to that? In the statement "read(buf, 9,0) = 2 what is the significance of the 2"? Does it return a 0 at the EOF?
Andrew Rissing 7-Jul-10 17:53pm    
The path of the file is the index to lookup the value in the dictionary. The BitConverter should be doing exactly what you were doing in code. The Read method returns 0 when it cannot read any more data. The 2 is because you're reading two bytes out.
I modified your code to fix the endian problem and use just the file name less the path for the dictionary key. I found a Microsoft example that reverses the bytes. That appears to work fine. Thanks Andrew.

XML
private Dictionary<string, List<ushort>> loadDesignFiles(string ext)
     {
         Dictionary<string, List<ushort>> dctAllGroups;
         List<ushort> lstWords;
         byte[] wordbuf;
         dctAllGroups = new Dictionary<string, List<ushort>>();
         wordbuf = new byte[2];
         foreach (string strFullFileName in Directory.GetFiles(userDirectory, "*." + ext))
         {
             int startFn = strFullFileName.LastIndexOf("\\")+1;
             int sizeFn = strFullFileName.Length - startFn;
             string strFileName = strFullFileName.Substring(startFn, sizeFn);
             dctAllGroups[strFileName] = lstWords = new List<ushort>();
             using (FileStream inFile = File.OpenRead(strFullFileName))
             {
                 while (inFile.Read(wordbuf, 0, 2) == 2)
                 {
                     // Combine every two consecutive bytes into uint16
                     wordbuf = ReverseBytes(wordbuf);
                     lstWords.Add(BitConverter.ToUInt16(wordbuf, 0));
                 }
             }
         }
         if (dctAllGroups.Count == 0)
             MessageBox.Show("No Design Files Found", "Warning");
         else
             MessageBox.Show("Loaded " + dctAllGroups.Count + " Design Files", "Note");
         return dctAllGroups;
     }
     private static byte[] ReverseBytes(byte[] inArray)
     {
         byte temp;
         int highCtr = inArray.Length - 1;
         for (int ctr = 0; ctr < inArray.Length / 2; ctr++)
         {
             temp = inArray[ctr];
             inArray[ctr] = inArray[highCtr];
             inArray[highCtr] = temp;
             highCtr -= 1;
         }
         return inArray;
     }
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900