Question

I was originally going to read a file through an array, pin to struct, convert and display. I have been trying to find another solution (I have removed the original details here to cause less confusion). I have a .bin File and I can correctly identify how many records are in the file by using a simple sum and FileInfo.

I've looked at:

Files imported have the same structure and look similar to the following: _(note screenshots show the amount of rows in the file and I have then calculated the rows for DataGridView table using a sum from here I produced Adding rows to second column ONLY - populating data using a for loop:

long Count = 1;
FileInfo Fi = new FileInfo(import.FileName);
long sum = (Fi.Length / 1024) - Count;

for (int i = 0; i < sum; i++)
{
    DataGridView1.Rows.Add(null, Count++);
    ReadWeldRecs(sum); // Added after question was published & called in ReadWeldRecs
}

First shows a total of 21 rows and the second being 9:

enter image description here enter image description here


I have a method called DecodeDate and another called ReadWeldRecs, DecodeDate is fed through ReadWeldRecs which is then activated via a button click event. I know what date should be displayed, but when it comes to viewing the result from DecodeDate, it is wrong. I want to be able to read the date inside the file. import.FileName is the filename (@Kevin) that that been opened using OpenFileDialog and the date is displayed at position 5 in the file.

My first go:

The Date is displayed as: 22/08/2123

But should be: 21/10/2008

I've thought, may be it's an issue with the location? But I'm sure it's position 5.

Update: Turns out I was looking in the wrong location... Duh.

private DateTime DecodeDate(int Start)
{
    int Year = Strings.Asc(Strings.Mid(import.FileName, Start, 1)) + 2000;
    int Month = Strings.Asc(Strings.Mid(import.FileName, Start + 1, 1));
    int Day = Strings.Asc(Strings.Mid(import.FileName, Start + 2, 1));

    return DateAndTime.DateSerial(Year, Month, Day);
}

Original:

This is the original VB code which worked fine in the out-dated program: (I looked at this mostly to reconstruct the DecodeDate method in C#...)

Public Function DecodeDate(Start As Integer) As Date
    YYear = Asc(Mid$(ImportRecord, Start, 1)) + 2000
    MMonth = Asc(Mid$(ImportRecord, Start + 1, 1))
    DDay = Asc(Mid$(ImportRecord, Start + 2, 1))
    DecodeDate = DateSerial(YYear, MMonth, DDay)
End Function

ImportRecord is defined as the following: (global string)

Open ImportFileName For Random As #1 Len = Len(ImportRecord)
// ...
Get #1, Index + 1, ImportRecord
// ...
.Date = DecodeDate(5) 

Current:

private void ReadWeldRecs(long RecordNumber)
{
    byte[] Rec = new byte[1024];

    using (FileStream Fs = new FileStream(import.FileName, FileMode.Open, FileAccess.Read))
    using (BinaryReader Br = new BinaryReader(Fs))
    {
        int Rec_Len;

        RecordNumber = 0; // Start with Record 0
        while (true)
        {
            Fs.Seek(RecordNumber * 1024, SeekOrigin.Begin); // Position file to record
            Rec_Len = Br.Read(Rec, 0, 1024); // Read the record here

            if (Rec_Len == 0) // If reached end of file, end loop
            {
                break;
            }

            if (Rec[0] != 0) // If there is a record, let's display it
            {
                Label_Date1.Text = DecodeDate(Rec, 28).ToShortDateString();
            }

            RecordNumber++; // Read first record ++
        }

        Br.Close();
        Fs.Close();
    }
}

Plus @Kevin's updated solution :)


However, also this has resolved a major issue I still another where I am trying to go by the guidelines and template of @Kevin's solution for my other method DecodeString.

In VB:

Public Function DecodeString(Start As Integer, Length As Integer) As String
Dim Count As Integer
Dummy = Mid(ImportRecord, Start, Length)
For Count = 1 To Len(Dummy)
  If (Mid$(Dummy, Count, 1) = Chr$(0)) Or (Mid$(Dummy, Count, 1) = Chr$(255)) Then
    Mid$(Dummy, Count, 1) = Chr$(32)
  End If
Next
DecodeString = Trim(Dummy)
End Function

Again, note I'm looking at using the solution as a template for this

Était-ce utile?

La solution

A couple of things...

  1. Is this C# or VB you are looking for since it's tagged as C# but Strings and DateAndTime are VB.
  2. Are you just parsing the date out of a string that is the filename?
  3. Is the date value really represented as the ASCII value of the characters? Really? That seems extremely odd... This means that to get year = 8, month = 10, day = 20 your filename would be something like abcde[BackspaceCharacter][LinefeedCharacter][Device control 4]. I definitely don't think you have control characters in there unless of course it was binary data stuffed into a string in the first place.

I'm going to assume the answers to these questions are... C#, Yes, and No. It would be easy to convert from C# -> VB if that's an incorrect assumption. I don't think I've used anything specific to C# here. Note I've spread this out onto several lines for readability on the forum.

private DateTime DecodeDate(int Start)
{
    // If your filename is 'ABC-01234 2008-10-21@012345.bin' then Start should = 10
    DateTime date;
    var datePart = filename.Substring(Start, 10);
    var culture = CultureInfo.InvariantCulture;
    var style = DateTimeStyles.None;
    // note since you are telling it the specific format of your date string
    // the culture info is not relevant.  Also note that "yyyy-MM-dd" below can be
    // changed to fit your parsing needs yyyy = 4 digit year, MM = 2 digit month
    // and dd = 2 digit day.
    if (!DateTime.TryParseExact(datePart, "yyyy-MM-dd", culture, style, out date))
    {
        // TryParseExact returns false if it couldn't parse the date.
        // Since it failed to properly parse the date... do something 
        // here like throw an exception.
        throw new Exception("Unable to parse the date!");
    }
    return date;
}

After looking up DateAndTime there is another way for you to get the numbers you are seeing without there being control characters in the filename... but in that case I still don't think you want to use DateAndTime.DateSerial since it only obfuscates the error. (if you accidentally give it 25 for the month the date it returns has january for the month and adds 2 years to the year portion)

If this doesn't solve it... give the format of your filename and it will be easier to figure out what exactly the problem is.

EDIT: Based on your updates... looks like you are trying to parse the binary data...

An easier way to do this with the .NET framework methods on the BinaryReader like BinaryReader.ReadByte, .ReadDouble, etc..

For your code though you'll need to pass in the byte array so you can pull the values out of it. Style wise you want to limit or eliminate usage of global variables...

private DateTime DecodeDate(byte[] bytes, int start)
{
    // Note that Length is 1 based and the indexer is 0 based so 
    if (bytes.Length < start + 3)
        throw new Exception("Byte array wasn't long enough");
    int year = Convert.ToInt32(bytes[start]) + 2000;
    int month = Convert.ToInt32(bytes[start+1]);
    int day = Convert.ToInt32(bytes[start+2]);

    return new DateTime(year, month, day);
}

And then your call to it looks like this...

Label_Date1.Text = DecodeDate(Rec, 5).ToShortDateString();

I wanted to mention a couple of style pointers... These are as much personal preference as anything but I find them helpful.

  1. Don't use globals or severely limit usage of them - import is used in your method but is defined somewhere else - this makes it harder to see what your code is doing and for you to trouble shoot it.
  2. Name local variables starting in lowerCase in order to make it easier to see what is a method and what is a variable.
  3. Be VERY careful with While(true) - its better if you can re-write it to be clearer when you exit the loop.

ONE MORE THING! You said your date was at position 5. In VB arrays generally start at 1 so the 5th element is array index 5... in C# arrays start with 0 so the 5th element would be index 4. I can't know which you should be checking from your code since I don't know the structure of the data so just keep this in mind.

EDIT 2: For DecodeString it's both simple and hard... the code is very simple... however... strings are MUCH more complex than you would think. To convert from string to binary or vice versa you have to choose an encoding. Encodings are an algorithm for converting characters into binary data. Their names will be familiar to you like ASCII, UTF8, Unicode, etc. Part of the problem is that some programming languages obfuscate encodings from you and much of the time programmers can be blissfully ignorant of them. In your case it seems like your binary file is written in ASCII but it's impossible for me to know that... so my code below works if it's in ASCII... if you run into cases where your string isn't decoded properly you may have to change that.

private static string DecodeString(byte[] bytes, int start, int length)
{
    return Encoding.ASCII.GetString(bytes, start, length);
}

Bonus reading - Joel Spolsky's post on Encodings

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top