1.2.3 BufferViewer

Demo展示

本节实现了一个最简单的十六进制查看工具——BufferViewer,如图1-8所示。

BufferViewer实现了如下功能。

·打开任意文件,查看其十六进制数据。

·随机生成100字节的十六进制数据,并查看。

·将十六进制转换为Base64编码表示。

·将Base64编码表示转换回十六进制表示。

·判断Base64编码是否合法。

完整的代码可以在ch01/01_BufferViewer中查阅。

img

图1-8 BufferViewer界面

使用VS2022打开rtcbook.sln,在Solution Explorer中,右键单击01_BufferViewer工程,单击Set as Startup Project,再次右键单击Build,即可运行启动。

工程说明

作为本书的第一个示例工程,我们详细介绍一下各个代码文件的作用。

源码的组织形式如图1-9所示。

主界面使用了WTL(Windows Template Library),这是微软维护的一个轻量级UI库,只有纯头文件,无须库文件。

主窗口的实现位于MainDlg.h中,这是一个派生自WTL的对话框类。

我们可以通过消息函数处理表的宏定义,为其添加各种消息函数,从而实现从UI操作到功能的调用。

img

为了方便代码复用,本书将所有示例工程的公用代码都放置于Common文件夹下。这些公用代码具有很强的复用性,可以被多个示例同时使用。

依据公用代码的功能不同,Common下又分为多个文件夹。

·Base:缓存、文字编码、线程等。

·Video:视频相关算法。

·Audio:音频相关算法。

·File:文件格式相关。

·Data:数据处理相关。

·Net:网络相关算法。

img

图1-9 源码的组织结构

我们看一段从文件加载到Buffer并显示其十六进制格式的代码。

img

第3行,CFileDialog表示WTL提供打开文件对话框用来获取用户所选择的文件。

第7行,DFile类是一个文件封装处理类,用来加载文件到内存。

第8行,DXP类有两个方法:ws2s和s2ws,用作Unicode与MBCS之间字符集的转换,下一节会详细介绍。

第9~10行,我们最多读取100 KB的数据到内存中。

第11行,DBuffer类将数据从文件读取到内存中。

第12行,我们使用DBuffer的ToHexList方法,将其转换为十六进制表示。

第13行,m_hex是一个CEdit的成员对象,这也是WTL提供的一个封装类,用来实现一个多行的文本框。我们将转换好的文本内容贴上去展示。

经过这些基础类的封装,整个代码的易读性都得到了明显的提升。

以二进制/十六进制形式查看任何数据,是我们理解任何计算机技术的基础。

只有细到每一比特,才能真正理解计算机程序的运行规律。

总结

本节我们介绍了比特和字节的基本概念,了解了十六进制与Base64编码。为了能方便地管理字节,我们设计了一个基于引用计数的Buffer类。我们开发了一个十六进制查看器,用来演示Buffer类。后续,我们会经常用它来分析各种媒体文件格式。

参考阅读

1.斯坦福大学的Sean Eron Anderson搜集了大量有关bit的算法。

2.Programming Pearls(1999年),More Programming Pearls(1988年),Jon Louis Bentley著,介绍了一些关于位运算的黑科技。

3.Base64编码的细节,请参考RFC 4648。

4.WTL的atlmisc.h中有一个CString类的实现,包含了更多字符串操作的内容。

5.WinHEX是Windows平台下一款好用的十六进制编辑器。macOS平台下则有hexdump显示工具。

练习题

1.[30分钟](交换字节序)编写一个交换字节序的函数。

2.[2小时](Bitset)设计一个Bitset类,支持按bit来访问4字节长度的二进制数据。

3.[1人天](BitStream)设计一个BitStream类,支持按bit来获取一段长度的比特流。

4.[1人天](Base58编码)Base58是比特币采用的一种二进制编码方式,它使用了哪些字符编码?有什么优势?

5.[30分钟](判断2的幂)写一个函数,判断一个整数是否是2的幂。

6.[30分钟](求二进制数1的个数)写一个函数,求一个整数中有多少个二进制的1。

7.[30分钟](无分支的截取)写一个函数,给定任意无符号整数v,若v大于255,则返回255,否则返回v,不能使用条件判断或问号表达式。

8.[30分钟](无分支的绝对值)写一个函数,给定任意整数v,返回v的绝对值,不能使用条件判断或问号表达式。