【问题标题】:Convert hex string to bytearray and write to file将十六进制字符串转换为字节数组并写入文件
【发布时间】:2014-06-09 09:43:23
【问题描述】:

我使用 C# 编写了一个应用程序来从串行端口读取数据并以十六进制字符串格式在文本框中显示数据。最后,我将所有数据保存到一个二进制文件中。如果数据很大(可能 > 20mb),则会引发内存不足错误。我该如何解决这个问题?这是我的代码:

private void btn_Save_Click(object sender, EventArgs e)
{
    SaveFileDialog save_log = new SaveFileDialog();
    save_log.DefaultExt = ".bin";
    save_log.Filter = "Binary File (*.bin)|*.bin";
    // Determine if the user selected a file name from the saveFileDialog.
    if (save_log.ShowDialog() == System.Windows.Forms.DialogResult.OK &&
        save_log.FileName.Length > 0)
    {
        try
        {
            string hexString = Content.Text.ToString();
            FileStream stream = new FileStream(save_log.FileName, FileMode.Create, FileAccess.ReadWrite);
            stream.Write(Hex_to_ByteArray(hexString), 0, Hex_to_ByteArray(hexString).Length);
            stream.Close();
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }
}

private byte[] Hex_to_ByteArray(string s)
{
    s = s.Replace(" ", "");
    byte[] buffer = new byte[s.Length / 2];
    for (int i = 0; i < s.Length; i += 2)
    {
        buffer[i / 2] = (byte)Convert.ToByte(s.Substring(i, 2), 16);
    }
    return buffer;
}

【问题讨论】:

  • 如果您有从串口读取的数据(可能是byte[] 格式),为什么不保留它并在需要时将其写入文件?这样,您不必将十六进制字符串转换回二进制。
  • 这是个好主意,但它不能解决我的问题。
  • 不要自己做所有事情。使用encoding。这个来源可能会帮助你codeproject.com/Questions/695963/…

标签: c# .net winforms io


【解决方案1】:

您创建了两次字节数组。此外,这么长的字符串上的.Replace 也无济于事。你可以避免这一切:

try
{
    var stream = new FileStream(
        save_log.FileName,
        FileMode.Create,
        FileAccess.ReadWrite);

    WriteHexStringToFile(Content.Text, stream);

    stream.Close();
}
catch (Exception ex)
{
    MessageBox.Show(ex.Message);
}

private void WriteHexStringToFile(string hexString, FileStream stream)
{
    var twoCharacterBuffer = new StringBuilder();
    var oneByte = new byte[1];
    foreach (var character in hexString.Where(c => c != ' '))
    {
        twoCharacterBuffer.Append(character);

        if (twoCharacterBuffer.Length == 2)
        {
            oneByte[0] = (byte)Convert.ToByte(twoCharacterBuffer.ToString(), 16);
            stream.Write(oneByte, 0, 1);
            twoCharacterBuffer.Clear();
        }
    }
}

另外,请查看 Encoding 和/或 BinaryFormatter,它们可能会为您完成所有这些工作。

更新:

首先,请注意,您将兆字节数据存储在字符串中的整个想法是无稽之谈,您不应该这样做。您应该以较小的部分处理您的数据。由于这种废话,由于在线编译器的资源限制,我无法为您提供工作演示(例如在 IDEONE 上)。我已经在我的机器上测试了代码,如您所见,我什至可以处理 50 MB 的字符串——但这完全取决于您可用的内存量。如果你做这样的事情,那么在每台机器上都很容易达到可用内存的限制。而且您在这个特定问题中询问的方法是无关紧要的 - 问题是因为您在 Content.Text 字符串中填充了大量数据。当内存快满时,OutOfMemoryException 几乎可以出现在代码中的任何位置。

您可以在浏览器中查看整个图片以查看所有详细信息。

【讨论】:

  • @Green OutOfMemoryException 不可能仅由此代码产生。你在哪一行看到它?
  • @Green 请尝试更新版本。我已经删除了所有不必要的内存分配。
  • @Green 我很乐意测试代码,但请提供更多详细信息。行数应显示在异常消息/异常详细信息中。什么文件大小会触发异常?你是什​​么意思“逐行”?您只指出了一行 - 所以它是 foreach 行上的 OutOfMemory 异常?
  • @Green 是的,程序中的每条信息都使用内存。 RichTextBox(和任何其他控件,不包括一些专门为大数据设计的非常精细的控件 - 但我不知道有任何控件)还需要将它显示在内存中的数据以及许多其他关于其外观的信息存储起来,格式等。WinForms 控件并非旨在处理此类字节数。
  • @Green 有很多可能性,但需要注意两件重要的事情:1)您在 GC 环境中工作(例如更多信息 here)。您清除StringBuilder 的事实并不意味着内存立即被释放。因此,在您使用RichTextBox 的情况下,可能分配了两倍的内存。 2) 控件通常是“重”的,RichTextBox 肯定是“重”的(意思是它使用大量资源)。所以它使用比string 更多的内存。
【解决方案2】:

使用 IEnumerable。这将避免大字节数组。

我不知道 Content.Text 中有什么。如果是字节数组,或许可以改

static internal IEnumerable<byte>Hex_to_Byte(string s)

进入

static internal IEnumerable<byte>Hex_to_Byte(byte[] bytes)

稍微修改一下代码

FileStream stream = new FileStream(save_log.FileName, FileMode.Create, FileAccess.ReadWrite);
foreach( byte b in Hex_to_Byte(hexString) )
    stream.WriteByte(b);
stream.Close();



    static internal IEnumerable<byte>Hex_to_Byte(string s)
    {
        bool haveFirstByte = false;
        int firstByteValue = 0;

        foreach( char c in s)
        {
            if( char.IsWhiteSpace(c))
                continue;

            if( !haveFirstByte)
            {
                haveFirstByte = true;
                firstByteValue = GetHexValue(c) << 4;
            }
            else
            {
                haveFirstByte = false;
                yield return unchecked((byte)(firstByteValue + GetHexValue(c)));
            }

        }

    }
    static string hexChars = "0123456789ABCDEF";
    static int GetHexValue(char c)
    {
        int v = hexChars.IndexOf(char.ToUpper(c));
        if (v < 0)
            throw new ArgumentOutOfRangeException("c", string.Format("not a hex char: {0}"));
        return v;
    }

【讨论】:

    猜你喜欢
    • 2021-10-31
    • 2019-02-12
    • 1970-01-01
    • 2017-06-17
    • 2018-05-09
    相关资源
    最近更新 更多