c# - 将从 .bin 文件读取的数据解码到字段中

标签 c# arrays visual-studio-2008 struct .net-3.5

我原本打算通过数组读取文件,固定结构,转换并显示。我一直在尝试寻找另一种解决方案(我已删除此处的原始详细信息以减少困惑)。我有一个 .bin 文件,我可以通过使用简单的总和和 FileInfo 来正确识别文件中有多少条记录。

我看过:

导入的文件具有相同的结构,看起来类似于以下内容:_(注意屏幕截图显示了文件中的行数,然后我使用我生成的 Adding rows to second column ONLY - populating data using a for loop 中的总和计算了 DataGridView 表的行数:

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
}

第一个显示总共 21 行,第二个显示 9 行:

enter image description here enter image description here


我有一个名为 DecodeDate 的方法,另一个名为 ReadWeldRecsDecodeDate 通过 ReadWeldRecs 提供,然后通过按钮单击事件激活。我知道应该显示什么日期,但是当从 DecodeDate 查看结果时,它是错误的。 我希望能够读取文件内的日期。 import.FileName 是使用<打开的文件名 (@Kevin) em>OpenFileDialog 并且日期显示在文件中的位置 5 处。

我的第一次:

日期显示为:22/08/2123

但应该是:2008年10月21日

我想,可能是位置的问题?但我确定它是位置 5。

更新:原来我找错了位置......呃。

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);
}

原文:

这是原始 VB 代码,在过时的程序中运行良好:(我主要是为了在 C# 中重建 DecodeDate 方法...)

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 定义如下:(全局字符串)

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

当前:

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();
    }
}

加上@Kevin的更新解决方案:)


但是,这也解决了我的另一个主要问题,我正在尝试按照@Kevin的解决方案的指南和模板来实现我的其他方法DecodeString

在 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

再次注意,我正在考虑使用该解决方案作为模板

最佳答案

有几件事......

  1. 您要查找的是 C# 还是 VB,因为它被标记为 C#,但 Strings 和 DateAndTime 是 VB。
  2. 您只是从文件名字符串中解析日期吗?
  3. 日期值是否真的表示为字符的 ASCII 值?真的吗?这看起来非常奇怪......这意味着要获得年 = 8,月 = 10,日 = 20,你的文件名将类似于 abcde[BackspaceCharacter][LinefeedCharacter][Device control 4]。我绝对不认为其中有控制字符,除非它首先是填充到字符串中的二进制数据。

我将假设这些问题的答案是... C#、是和否。如果这是一个不正确的假设,那么从 C# 转换为 VB 会很容易。我认为我在这里没有使用任何特定于 C# 的东西。请注意,为了论坛上的可读性,我已将其分散到几行中。

private DateTime DecodeDate(int Start)
{
    // If your filename is 'ABC-01234 <a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="a49694949c899594899695e49495969790918ac6cdca" rel="noreferrer noopener nofollow">[email protected]</a>' 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;
}

查找 DateAndTime 后,还有另一种方法可以让您在文件名中不包含控制字符的情况下获取所看到的数字...但在这种情况下,我仍然认为您不想使用 DateAndTime.DateSerial 因为它只会混淆错误。 (如果您不小心将月份指定为 25,则返回的日期为该月份的 1 月,并在年份部分添加 2 年)

如果这不能解决问题...请提供文件名的格式,这样会更容易找出问题所在。

编辑:根据您的更新...看起来您正在尝试解析二进制数据...

通过 BinaryReader 上的 .NET 框架方法(例如 BinaryReader.ReadByte、.ReadDouble 等)更简单地执行此操作。

对于您的代码,您需要传入字节数组,以便可以从中提取值。就风格而言,您希望限制或消除全局变量的使用...

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);
}

然后你对它的调用看起来像这样......

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

我想提一些风格建议...这些都是个人喜好,但我发现它们很有帮助。

  1. 不要使用全局变量或严格限制它们的使用 - import 在您的方法中使用,但在其他地方定义 - 这使得您更难看到您的代码在做什么,并且给您带来麻烦射击它。
  2. 以小写字母开头命名局部变量,以便更容易看出什么是方法、什么是变量。
  3. 要非常小心 While(true) - 如果您可以在退出循环时重写它以使其更加清晰,那就更好了。

还有一件事!你说你的日期位于位置 5。在 VB 数组中通常从 1 开始,所以第 5 个元素是数组索引 5...在 C# 数组中从 0 开始,所以第 5 个元素元素将是索引 4。我不知道您应该从代码中检查哪个,因为我不知道数据的结构,所以请记住这一点。

编辑2: 对于 DecodeString 来说,它既简单又困难……代码非常简单……但是……字符串比您想象的要复杂得多。要从字符串转换为二进制,反之亦然,您必须选择一种编码。编码是将字符转换为二进制数据的算法。它们的名称对您来说很熟悉,例如 ASCII、UTF8、Unicode 等。部分问题在于某些编程语言会混淆您的编码,而大多数时候程序员可能对它们一无所知。在你的情况下,你的二进制文件似乎是用 ASCII 编写的,但我不可能知道这一点...所以我下面的代码如果是 ASCII 就可以工作...如果你遇到字符串未正确解码的情况,你可能必须改变这一点。

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

额外阅读 - Joel Spolsky's post on Encodings

关于c# - 将从 .bin 文件读取的数据解码到字段中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20350117/

相关文章:

c# - 网络服务发现

c# - 检测实体是否附加到数据上下文

c# - 更正串联查询。 C# 和 MySQL

c++ - 结构中指针数组的析构函数

c - 使用 strlen 时出现段错误

c# - 如果在 MVC 的第二个数组中找不到,则从第一个数组中删除

c# - 返回 List<T> 泛型类型

c# - 如何在 Visual Studio 中为项目引用设置 "Specific Version"属性

c++ - 如何从命令行编译 Visual Studio 项目?

visual-studio - Visual C++ 2008 64 位编译器?