none
DBF BinaryWriter Problem RRS feed

  • Question

  •  

    I got a a problem. I'm trying to update a dbf file using the binarywriter.  The following code snippet is from a dollar conversion dll that I am creating.  It reads a field (example morgtage field) and formats the field as a dollar amount, and then updates the dbf file. 

     

    Please note, I am familiar with the dbf file structure.  I have created a binary writer in vb.net.  I read and write to dbf files all the time (using vb.net).  On the other hand, I am new to C#.   There is nothing wrong with my dll in vb.net I am just re-writing my application in C# (how fun this is).

     

    VB.NET CODE

     

    If (temp.Contains("$")) Then

    temp = temp.Substring(1, temp.Length - 1)

    End If

    DollarValue = Convert.ToDecimal(temp)

    temp = String.Format("{0:C}", DollarValue)

    icount = Field_length - temp.Length

    For count As Integer = 0 To icount - 1

    temp = temp + " "

    Next

    mark = fs_Reader.Position - Field_length

    bw.Seek(mark, SeekOrigin.Begin)

    bw.Write(temp, 0, temp.Length)

     

     

    C# CODE

     

    if (_fieldValue.Contains("$"))

    {

    _fieldValue = _fieldValue.Substring(1, _fieldLength - 1);

    }

    _fieldValue = string.Format("{0:c}", Convert.ToDouble(_fieldValue));

    _fieldValue = Convert.ToString(_fieldValue);

    _wpos = _pos; 

    _bw.Seek(Convert.ToInt32(_wpos), SeekOrigin.Begin);

    _bw.Write(_fieldValue);

     

     

    Please let me know if I need to post more code.  The problem I am having is, it does not appear to be writing to the dbf file. The field is updated but with a blank value.  In visual basic when I write to the dbf file I specified the value, the starting postion, and the field size.  But C# appears to be a little different.

     

    Also, I do have another question.

     

    In visual basic my binaryreader and binarywriter variables use the same filestream.

    fs_Reader = New FileStream(Path, FileMode.Open)

    br = New BinaryReader(fs_Reader)

    bw = New BinaryWriter(fs_Reader)

     

     

    Is it ok to do the same in C#? Or will it cause additional problems?

    FileStream _fs = new FileStream(filePath, FileMode.Open);

    BinaryReader _br = new BinaryReader(_fs);

    BinaryWriter _bw = new BinaryWriter(_fs);

     

     

    Thanks.

     

     

     

    Sunday, April 27, 2008 12:50 AM

Answers

  • I see what's happening. VB.NET is doing type conversions for you, but C# requires explicit conversions.

     

    1. _pos is declared as type int, but _fs.Position is type long. VB.NET does this conversion for you automatically, but the problem is that you have one conversion of the assignment of _fs.Position to _pos and then incur the overhead of another conversion when doing _fs.Seek(_pos, SeekOrigin.Begin);.  It would be better to declare _pos like this, which eliminates the overhead of the two conversions and solves your problem:

     

    Code Snippet
    long _pos;

     

    2. The second one is a little tricky because the compiler error doesn't pinpoint the exact problem, but hovers around where the real problem is, which resolves to another type conversion error. The first parameter to _bw.Write(_fieldValue,0,_fieldLength); is _fieldValue, which is type string. VB.NET will convert this automatically to an array of characters and it works fine. However, C# requires an explict conversion or that you pass in the proper type. The error message you received was because of a type mismatch in the first parameter and the C# compiler tried to be helpful by picking out the Write method overload that it thinks you needed. As it happens there is an overload of Write that takes three parameters and the first is byte[]. However, C# wasn't smart enough because there is also an overload that takes 3 parameters with the first one being a char[]. So, the easiest way to solve this problem is to use the string method, ToCharArray() to get the right type, something like this:

     

    Code Snippet
    _bw.Write(_fieldValue.ToCharArray(),0,_fieldLength);

     

    Joe

    Sunday, April 27, 2008 6:39 PM

All replies

  • Hi,

     

    One of the things that might make it easier for you is to know that in .NET, languages are mostly just syntax and all of the functionality for their libraries is pushed out to the .NET Framework Class library. Since BinaryReader and BinaryWriter are part of the .NET Framework Class library, they work exactly the same, regardless of whether the language you are using is C#, VB.NET, or some other .NET language.

     

    This knowledge should help you because you have written two different algorithms for both C# and VB.NET, which isn't necessary. If you have it working with VB.NET, then getting the C# part working should be a matter of duplicating the functionality with new syntax.  For example, the following VB.NET part of the algorithm hasn't been duplicated in the C# code:

     

    Code Snippet

    icount = Field_length - temp.Length

    For count As Integer = 0 To icount - 1

    temp = temp + " "

    Next

    mark = fs_Reader.Position - Field_length

     

    You, could do this something like this: (Warning! I'm typing this on the fly and haven't compiled it)

     

    Code Snippet

    icount = Field_length - temp.Length;

    for (int count = 0; count < icount; count++)

    {

        temp += " ";

    }

    mark = fs_Reader.Position - Field_length;

     

    Mostly, the change was semi-colons on the end of the statement and the C# for loop. Instead of the above, you have this statement in the C# code, for which I have know idea where the variables came from:

     

    Code Snippet
    _wpos = _pos; 

     

    Then, the following VB.NET code:

     

    Code Snippet

    bw.Seek(mark, SeekOrigin.Begin)

    bw.Write(temp, 0, temp.Length)

     

    Isn't duplicated the same in the C# code, which is using different overloads. You could write it something like this: (Warning! code hasn't been compiled)

     

    Code Snippet

    _bw.Seek(mark, SeekOrigin.Begin);

    _bw.Write(_fieldValue, 0, _fieldValue.Length);

     

    The answer to your second question is Yes - for the reason I stated earlier that the objects you're using belong to the .NET Framework Class Library and work the same, regardless of Language.

     

    Joe

    Sunday, April 27, 2008 3:09 AM
  • Yes, I am perfectly aware that all .net languages compile into the same intermediate language.  I could use the vb.net dll in my current project.  However, there are a few reasons why I do not want to do this.  One reason is, I want to learn C#  I feel this would be benefitial later on.   The second reason is the vb.net project was basically written on the fly, it really never was thought out, and I am constantally rewriting and using the same code.  What I would like to do in C# is make my application more structured and use inheritance and get rid of the unnessary code that I have in vb.net.

     

    Sorry for the confusion, the C# snippet that I posted was after my panic attack Smile (I call it panic attack code).  My original code, during the compile process, received a lot of errors.  So, I changed things around and as you can see it was not correct. 

     

    So, with your advice, I went back and rewrote it the way I had it.  However, I do have a few issues.

     

    1.  I receive an error messge: "cannot implicitly convert type "long" to "int".  This error accures twice. 

    _pos = _fs.Position;

    _offSet = _fs.Position - _fieldLength;

     

    //note: _pos is a variable i used that references the current position in the filestream.

    //note: _offSet is a variable I used to place the filestream to the initial postion before it read the value in the dbf file.

     

    it appears that the _fs.Postion has the type long.  This was not an issue in vb.net

     

    2.  I receive an error message: Argument '1': cannot convert from 'string' to 'byte[]'

    _bw.Write(_fieldValue,0,_fieldLength);

     

    This is basically the two errors that caused me to write misleading code you see in my previous posting. 

     

     

     

    public class DollarConversion

    {

    public bool Process(string fieldName, string filePath)

    {

    Int32 _recordCount = 0;

    int _headerLength = 0;

    int _fieldCount = 0;

    int _pos = 0;

    int _fieldIndex = 0;

    Int32 _fieldLength = 0;

    int _offSet = 0;

    string _fieldValue = "";

    byte[] _byteArray;

    string _fieldName = "";

    int _icount = 0;

    bool _isNumeric = false;

    ArrayList _al = new ArrayList();

    ASCIIEncoding _encoding = new ASCIIEncoding();

    FileStream _fs = new FileStream(filePath, FileMode.Open);

    BinaryReader _br = new BinaryReader(_fs);

    BinaryWriter _bw = new BinaryWriter(_fs);

     

    try

    {

    //get number of records

    _fs.Seek(4, SeekOrigin.Begin);

    _recordCount = _br.ReadInt32();

     

    //get header length

    _fs.Seek(8, SeekOrigin.Begin);

    _headerLength = _br.ReadInt16();

    //get number of fields;

    _fieldCount = (_headerLength - 32) / 32;

    //add field lengths to an array

    for (int i = 0; i < _fieldCount; i++)

    {

    _pos = _pos + 32;

    _fs.Seek(_pos, SeekOrigin.Begin);

    _byteArray = _br.ReadBytes(10);

    _fieldName = _encoding.GetString(_byteArray);

    for (int ii = 0; ii < 9; ii++)

    {

    if (_byteArray[ii] != 0)

    {

    _icount += 1;

    }

    }

    _fieldName = _fieldName.Substring(0, _icount);

     

    //check to see if this is the field the user wants to convert

    if (_fieldName == fieldName.ToUpper())

    {

    _fieldIndex = i;

    }

    _offSet = _pos + 16;

    _fs.Seek(_offSet, SeekOrigin.Begin);

    _al.Add(_br.ReadByte());

    _icount = 0;

    }

    _fs.Seek(_headerLength, SeekOrigin.Begin);

    for (int i = 0; i < _recordCount; i++)

    {

    _br.ReadChar(); //skip this, used for record deletion flag

    for (int ii = 0; ii < _fieldCount; ii++)

    {

    _pos = _fs.Position;

    _fieldLength = Convert.ToInt32(_al[ii]);

    _fieldValue = _encoding.GetString(_br.ReadBytes(_fieldLength), 0, _fieldLength);

    if (ii == _fieldIndex)

    {

    if (_fieldIndex == ii)

    {

    _isNumeric = IsNumeric(_fieldValue);

    if (_isNumeric == false)

    {

    _br.Close();

    _bw.Close();

    _fs.Flush();

    _fs.Close();

    return false;

    }

    else

    {

    if (_fieldValue.Contains("$"))

    {

    _fieldValue = _fieldValue.Substring(1, _fieldLength - 1);

    }

    _fieldValue = string.Format("{0:c}", Convert.ToDouble(_fieldValue));

    _fieldValue = _fieldValue.PadLeft(_fieldLength,' ');

    _offSet = _fs.Position - _fieldLength;

    _bw.Seek(_offSet, SeekOrigin.Begin);

    _bw.Write(_fieldValue,0,_fieldLength);

    }

     

    }

     

     

    }

    }

     

    }

    _br.Close();

    _bw.Close();

    _fs.Flush();

    _fs.Close();

    return true;

    }

    catch (Exception)

    {

    throw;

    }

    }

    private bool IsNumeric(string fieldValue)

    {

    try

    {

    Convert.ToDouble(fieldValue);

    return true;

    }

    catch (Exception)

    {

    return false;

    }

    }

     

     

    }

     

     

     

    Sunday, April 27, 2008 1:19 PM
  • I see what's happening. VB.NET is doing type conversions for you, but C# requires explicit conversions.

     

    1. _pos is declared as type int, but _fs.Position is type long. VB.NET does this conversion for you automatically, but the problem is that you have one conversion of the assignment of _fs.Position to _pos and then incur the overhead of another conversion when doing _fs.Seek(_pos, SeekOrigin.Begin);.  It would be better to declare _pos like this, which eliminates the overhead of the two conversions and solves your problem:

     

    Code Snippet
    long _pos;

     

    2. The second one is a little tricky because the compiler error doesn't pinpoint the exact problem, but hovers around where the real problem is, which resolves to another type conversion error. The first parameter to _bw.Write(_fieldValue,0,_fieldLength); is _fieldValue, which is type string. VB.NET will convert this automatically to an array of characters and it works fine. However, C# requires an explict conversion or that you pass in the proper type. The error message you received was because of a type mismatch in the first parameter and the C# compiler tried to be helpful by picking out the Write method overload that it thinks you needed. As it happens there is an overload of Write that takes three parameters and the first is byte[]. However, C# wasn't smart enough because there is also an overload that takes 3 parameters with the first one being a char[]. So, the easiest way to solve this problem is to use the string method, ToCharArray() to get the right type, something like this:

     

    Code Snippet
    _bw.Write(_fieldValue.ToCharArray(),0,_fieldLength);

     

    Joe

    Sunday, April 27, 2008 6:39 PM
  • Joe,

     

    Thanks for your help. There were a few other changes I had to make, for example, the _offset variable was switched to long, but then I had to convert back to int32 :

    _bw.Seek(Convert.ToInt32(_offSet), SeekOrigin.Begin);

     

    Also, C# did not like the fact that I tried flushing the filestream before closing it:

    _fs.Flush();

     

    So, I removed that from the code.

     

    I do have it working but, I still have a few issues to work out.  Thanks again! You did a great job helping me.

    Monday, April 28, 2008 12:36 AM