导读:本期聚焦于小伙伴创作的《C#如何将文件转换为十六进制字符串并获取文件的Hex Dump?》,敬请观看详情。做渗透测试或协议分析时,经常需要查看二进制文件的原始字节分布。Hex Dump就是把文件内容按十六进制逐字节打印,并附带偏移量和可读字符。在C#里,这件事核心在于用FileStream按块读取字节数组,再用BitConverter或StringBuilder格式化成十六进制文本。不少人误以为直接读字符串再编码就能得到准确Dump,其实会破坏不可见字节。下面从底层读取原理讲起,给出单文件与分块两种实现,并说明大文件场景下如何避免内存暴涨,以及怎样把偏移、十六进制区和ASCII区对齐输出,方便人工核对。

在C#开发中,把任意文件转换成十六进制字符串并输出标准的Hex Dump,是分析二进制格式、排查数据损坏、对接底层协议时的常见需求。Hex Dump并不是简单把文件当文本读出来,而是要以字节为单位,将每个字节的值用两位十六进制表示,同时记录行偏移并尽量还原可打印字符。

C#如何将文件转换为十六进制字符串并获取文件的Hex Dump?

一、文件字节读取的底层原理

计算机中的文件在存储层面都是连续的字节流。C#提供了多种读取方式,其中最基础且可控的是FileStream类。它直接对接操作系统的文件句柄,支持以指定缓冲区大小分块读取,避免一次性把大文件装入内存。与之相对,File.ReadAllBytes虽然写法简单,但会在内部申请与文件等大的字节数组,遇到几百兆的文件就容易引发内存压力。

在获取Hex Dump时,我们关心的是原始字节而非字符编码。如果错误地使用File.ReadAllText配合Encoding去读,框架会按编码规则解释字节,像UTF-8遇到非法续字节可能变成替换符,导致Dump失真。因此正确做法是始终使用字节流API,拿到byte[]后再做格式化。

1.1 为什么需要偏移量

标准Hex Dump工具(如Linux的xxd)每行显示十六进制偏移,例如00000000、00000010。偏移量让我们能快速定位某个字节在文件中的绝对位置。在C#中实现时,只需维护一个long position变量,每读一行就加上该行字节数即可。

偏移量通常用8位十六进制展示,足以覆盖4GB以内的文件;更大的文件可以扩展到16位。格式化时用ToString("X8")就能补齐前导零,保证对齐美观。

二、基础实现:小文件整体转换

对于几兆以内的文件,可以放心使用File.ReadAllBytes一次性读取,然后用循环生成十六进制字符串。下面的示例演示了如何输出包含偏移、十六进制区和ASCII区的三栏式Dump。

using System;
using System.IO;
using System.Text;

class HexDumpDemo
{
    static void Main()
    {
        string path = @"D:test.bin";
        byte[] data = File.ReadAllBytes(path);
        StringBuilder sb = new StringBuilder();
        int bytesPerLine = 16;

        for (int i = 0; i < data.Length; i += bytesPerLine)
        {
            // 偏移量
            sb.Append(i.ToString("X8"));
            sb.Append("  ");

            // 十六进制区
            for (int j = 0; j < bytesPerLine; j++)
            {
                if (i + j < data.Length)
                {
                    sb.Append(data[i + j].ToString("X2"));
                    sb.Append(" ");
                }
                else
                {
                    sb.Append("   ");
                }
            }

            sb.Append(" ");

            // ASCII区
            for (int j = 0; j < bytesPerLine; j++)
            {
                if (i + j < data.Length)
                {
                    byte b = data[i + j];
                    sb.Append(b >= 32 && b < 127 ? (char)b : '.');
                }
            }

            sb.AppendLine();
        }

        Console.WriteLine(sb.ToString());
    }
}

上述代码把文件每16字节作为一行。十六进制区对每个字节调用ToString("X2")得到两位大写十六进制;ASCII区则判断字节是否落在可打印范围,否则用点号代替。这种方式逻辑直观,适合教学或处理配置类等小文件。

不过要注意,File.ReadAllBytes在文件超过可用内存时会抛出OutOfMemoryException。如果生产环境不确定文件大小,应当改用分块读取方案。

三、大文件方案:分块读取与流式输出

当文件可能达到数百兆甚至上吉字节时,必须用FileStream配合固定缓冲区逐步读取。这样内存中只保留一小块字节,处理完即丢弃。下面示例以8KB为缓冲,边读边写控制台或文件。

using System;
using System.IO;
using System.Text;

class StreamHexDump
{
    static void Main()
    {
        string path = @"D:bigfile.bin";
        int bufferSize = 8192;
        byte[] buffer = new byte[bufferSize];
        long position = 0;

        using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
        {
            int read;
            while ((read = fs.Read(buffer, 0, buffer.Length)) > 0)
            {
                DumpBlock(buffer, read, position);
                position += read;
            }
        }
    }

    static void DumpBlock(byte[] data, int count, long baseOffset)
    {
        int bytesPerLine = 16;
        for (int i = 0; i < count; i += bytesPerLine)
        {
            StringBuilder line = new StringBuilder();
            line.Append((baseOffset + i).ToString("X8"));
            line.Append("  ");

            for (int j = 0; j < bytesPerLine; j++)
            {
                if (i + j < count)
                {
                    line.Append(data[i + j].ToString("X2"));
                    line.Append(" ");
                }
                else
                {
                    line.Append("   ");
                }
            }

            line.Append(" ");

            for (int j = 0; j < bytesPerLine; j++)
            {
                if (i + j < count)
                {
                    byte b = data[i + j];
                    line.Append(b >= 32 && b < 127 ? (char)b : '.');
                }
            }

            Console.WriteLine(line.ToString());
        }
    }
}

该方案每次从流中读入最多8KB数据,调用DumpBlock处理这一段。由于baseOffset记录了全局偏移,输出的偏移量依旧连续正确。即使文件有数GB,程序常驻内存也仅为缓冲区大小。

如果希望结果写入文本文件而非控制台,只需把Console.WriteLine换成StreamWriter的写入即可。此外,还可以利用BufferedStream包裹FileStream来减少磁盘IO次数,提升吞吐。

四、性能与格式优化建议

在频繁生成Hex Dump的场景下,字符串拼接性能值得关注。示例里使用StringBuilder已经比直接加号拼接高效,但若追求极致,可以预分配字符数组并填充。另外,BitConverter.ToString(byte[])能一行输出十六进制并以短横线连接,虽然格式不同,但转换速度很快,适合只需要十六进制串而不需要ASCII区的情况。

byte[] sample = { 0x48, 0x65, 0x6C, 0x6C, 0x6F };
string hex = BitConverter.ToString(sample).Replace("-", " ");
Console.WriteLine(hex); // 输出 48 65 6C 6C 6F

上例展示了用BitConverter快速得到空格分隔的十六进制串。它的底层用查表法转换,比手动ToString稍快。但如果要完整Hex Dump版式,仍推荐自行控制布局。

最后提醒,某些文件含有BOM或压缩数据,十六进制视图里看到的前几个字节可能就是格式标记。理解这一点,才能在排查文件解析异常时,不被错误的字符推断误导。

五、常见误区总结

新手常把File.ReadAllText读到的字符串再转十六进制,这等于经历了一次编码往返,不可见字节全丢。还有人用string.Join配合Select写得很短,却忘了大文件会撑爆内存。正确思路始终是:字节进、字节出、格式化分离。

只要把握住FileStream分块、byte级格式化、偏移累计这三个要点,无论是小工具还是服务端批量处理,都能写出稳定高效的C# Hex Dump模块。

C#Hex_Dump文件读写修改时间:2026-08-02 22:42:34

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。