前几天接手了一个解析oracle dmp文件的任务。oracle的dmp文件实际上是一堆16进制的代码,它用于oracle数据库的导入和导出。我们的需求是找出其中的规律,并抓取出其中的建表语句和insert语句。目前该解析器初步实现,11g下能跑出结果。下面就来揭示出oracle dmp文件结构的一些秘密吧。

        上图所示的便是oracle dmp文件的部分内容(需要说明的一点是,我是用Nodepad++打开的文件,但是默认情况下其打开是一堆乱码,需要下载其十六进制编辑器插件,在Nodepad++中是HEX-Editor),最左边一列是地址指针,之后的列便是其具体的内容。其中两个十六进制数是一对,表示一个字节。可以看出,不管是地址指针还是数据都是用十六进制表示的。

        其文件表结构大致如下所示:

        由上可知,红色的部分便是我们需要的数据,剩下的无用数据并不是真的无用数据,只是对于我们而言是不关心的数据而已。其中包括一些表的元数据、索引的建立等等。有一点需要说明的是,除了文件头部的固定头之外,上述图演示的是一张表的结构示意图。如果一个oracle dmp文件有多张表,则会有多个上述图所演示的表结构片段(不包括固定头)。这点需要留意。

        该解析器是使用RandomAccessFile来进行读取的,其中的readLine方法并不是每次读取一行数据,而是以“0a”作为分隔,每次读出两个“0a”之间的内容。这点需要特别留意。首先需要跳过一个0x90 * 0x10 + 0xD的固定头,来到下一个“0a”处。跳过之后来到下图红框之后的位置处:

        此时调用三次readLine方法分别获得表声明表创建insert批处理语句(从最后面的描述列也可以略知一二)。接下来就是拼接具体的insert语句了。抓到真实的数据字段,和insert批处理语句进行拼接即可。在我们调用了三次readLine方法后,当前指针来到了下面红框的位置:

        之后我们需要找到“00 00 00 00 00”,五个“00”这样结构的位置处,在其之后便是真实的数据字段位置了。在上图中便是蓝框框中的内容处。之后我们读两个字节,这里是“24 00”,该数据表示之后插入数据的字节数比如说“24 00”换成十进制是36(经过实测,“00 24”和“24 00”都代表十进制的36,这里需要进一步分析总结),表示之后插入数据是36个字节,如上面所示便是向后数的36个字节。之后继续读两个字节,也就是“10 00”,十进制是16,之后读取16个字节…以此类推。

        那么什么时候是一行数据读取完呢?当我们遇到两个“00”也就是“00 00”的时候,代表着一行数据的结束。上述所说过程如下图所示,其中红框是此时需要插入值的前后校验位、黄框是插入的真实数据、蓝框框中的便是一行结束的位置:

        之后便开始了下一行数据的解析,和之前的过程一样,这里不再赘述。那么什么时候是一张表的所有插入语句都结束的位置呢?当遇到“00 00 ff ff 0a”这样的结构时,则代表当前表的插入过程都结束了。如下所示:

        另外需要说明的一点是当我们在“00 00”后读到的下一字节是“fe”时,代表该位置是一个空字段。这点需要留意。

        此时我们有了insert的批处理语句,也有了真实的插入数据的语句。就可以将这两者结合,拼接出真正的insert语句了。比如说我们有了批处理语句:“INSERT INTO "TABLE1" ("A", "B")  VALUES (:1, :2)”和对应的两行真实数据:“'q1'、'q2'”和“'q3'、'q4'”,我们就可以使用正则来将这二者拼接,得到两条insert语句:“INSERT INTO "TABLE1" ("A", "B")  VALUES ('q1', 'q2')”和“INSERT INTO "TABLE1" ("A", "B")  VALUES ('q3', 'q4')”。

        至此,我们完成了一张表的解析工作。接下来,继续去寻找下一次表的创建语句、批处理语句和真实数据语句。不断读取数据,找出第一句是“TABLE”开头和第二句是“CREATE TABLE”开头的语句。如果有这样的结构,则表示下一张表的解析工作就此开始。之后的解析过程和上述所说的内容一样,这里也不再做讲解了。

        那么,什么时候才是一个dmp文件真正解析结束呢?请看下图:

        当我们不断readLine时读取出的字符串是“EXIT”时,此时代表着文件解析的结束。

        至此,一张oracle dmp文件解析的全过程就完成了。但是需要注意的是,上述我所说的文件结构只是最一般的情况,有时也会有特例出现。比如:一张表插入真实数据的结束不仅可以用“00 00 ff ff 0a”来表示,也可以用“00 00 3F 3F 0A”来表示;有时候明明两个字符的校验位是“0c 00”,也就是十进制的12,表示后面的12个字节是一个数据段,但通过查看发现,后面的24个字段其实是作为一个整体的(我感觉是跟中文的编码集有关,一个中文对应两个字节,但有些中文却不是这样的规则。待考证)。同时,对于Blob、Clob字段的解析,我们暂时是没有实现的。有兴趣的同学可以查看,Blob和Clob字段首先会跟着一堆的“00”,且个数不固定,然后是跟着一些十六进制数。这跟我们现有的解析规则发生了冲突(看来Oracle对于Blob和Clob字段有着特殊的处理)。尽管如此,上述的解析过程也可以让我们对oracle的dmp文件有了一个初步的认识。下面是我解析出上面这个oracle dmp文件结果的部分截图,感兴趣的同学可以自行尝试实现一下:

        GitHub源码:https://github.com/ACoolMonkey/oracle-dmp-parser

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐