巧用AWK处理二进制数据文件

AWK是Unix下的一款功能强大的文本格式化和抽取工具。利用这个工具,可以对复杂的文本文件进行整理,提取其中的全部或者部分数据,按照需要的格式予以显示。需要说明的是,AWK的强大功能只针对纯文本文件。对于带有很多不可显示字符的二进制数据文件,单凭AWK就无能为力了。这时我们需要其他工具的帮助。

在Unix下,还有一个工具叫做OD,其全称是“display files in octal format”,也就是说它能将各种文件以8进制的方式显示出来。如果设置不同的选项,它还能将文件以16进制方式显示。

此外为了方便处理,我们还需要用到另外一个工具,sed。这也是一个Unix下的传统文本处理工具。在这里我们主要用到它的文本替换功能。

通过组合以上三种工具,我们就可以完成我们用AWK处理二进制数据文件的任务了。

笔者手中有一个数据文件,FXT,其数据结构如表 1所示。

Table 1

起始位 长度 说明
0 8 账号
8 7 金额
15 3 操作员号

如果用普通的文本编辑器打开这个数据文件,看到是一串数字和一堆难以理解的字符。根本就无法分辨金额是多少。为了方便读者理解这个文件,我们用od来查看这个文件 (见List1)。

List 1

# od -An -v -tx1 FXT 
     32 35 38 35 36 30 30 39 00 00 05 00 00 00 0c 31 
     30 31 0a 32 35 38 30 30 32 33 34 90 20 20 80 20 
     20 0d 31 30 32 0a … 

稍微解释一下Od的命令参数意义。-An表示不在每行左边显示偏移量;-v 表示每行都要显示;-tx1表示输出时以16进制方式输出,一次一个字节。

根据数据结构定义,我们可以看出前面8个字节(32 35 38 35 36 30 30 39)代表账号,而且账号部分是由可显示的ASCII码组成的,翻译后的结果就是25856009;接下来7个字节(00 00 05 00 00 00 0c)代表金额。最后的c代表credit,就是贷方的意思。它所代表的实际金额是500,000.00。紧接着的3个字节代表操作员号,也是由可显示的 ASCII码组成的。0a是换行符的ASCII代码,表示一条记录结束。

可以看出,正是由于金额部分是由不可显示的ASCII码组成,导致了无法用常规方法来提取数据文件中的数据

那么应该如何利用以上的工具来处理这类数据文件,并且按照可以理解的方式来生成新的纯文本的数据文件呢?

OD 已经将整个数据文件清楚地显示出来了。它输出的格式不符合我们的要求。比如说,本来在一行的记录,先在给分成了几行;本来连在一起的字符,现在中间出现了空格。这样AWK就不好处理了。所以,为了使AWK能够方便的处理,我们在正式提取数据之前,必须生成AWK可以处理的中间文件

从List1可以看出,OD在显示文件时,每一行前都有一个制表符,而且原来记录之间的换行符也变成了对应的ASCII码0a。那么我们的任务就要去掉制表符,而且要恢复正确的记录间的换行符。这一步可以通过以下命令完成。

# od -v -An -tx1 fxt |sed ‘s/  //‘|sed ‘s/0a/,/‘|awk -f org.awk >fxt.a 
OD显示出的结果通过管道被送给sed。Sed将每行开头的制表符先去掉,然后将0a字符替换为逗号。然后交给org.awk做格式化处理。 
# cat org.awk 
BEGIN{ 
ORS="" 
} 
{ 
 for(i=1;i<17;i++) 
  { 
  if($i==",") 
   printf("\n"); 
  else 
    printf("%s", $i);} 
}

通过定义ORS=””,我们可以保证输出的每个字段之间没有分隔符。(ORS=Output Record Separator)。然后,检查每一行中是否存在逗号,如果有则输出一个换行符。这样我们就把od显示的结果转化成以下内容。

# cat fxt.a 
32353835363030390000050000000c313031 
32353830303233349020208020200d313032 

怎么样,这样的格式就顺眼多了吧。

生成了中间文件,下一步就应该做正式的数据提取工作了。在实际工作中,很可能原始数据文件中的数据结构和我们需要转换的目标数据结构不完全一致。这时除了提取数据,还要在输出格式上进行一番加工。例如以上的数据文件,如果要按照以下数据结构(Table 2)输出的话,那么就可以参照程序ck1.awk。

Table 2

起始位 长度 说明
0 12 账号
12 1 ,
13 1 借贷标志,0表示贷,1表示借
14 1 ,
15 14 金额
29 1 ,
30 3 操作员号

# cat ck1.awk 
BEGIN{ 
FS="\n"; } 
function trans(s) 
{ 
 if((a=substr(s,1,2)-30)<0) 
  a=0; 
 for(i=3;i<length(s);i+=2) 
 { 
 if((b=substr(s,i,2)-30)<0) 
   b=0; 
 a=(a b); 
 } 
 return a; 
} 
{ 
 actno=trans(substr($1,1,16));#帐号 
 amt=substr($1,17,13);#金额 
 cdflag=substr($1,30,1); #借贷标志 
 if(cdflag=="d") 
    cdflag=1; 
 if(cdflag=="c") 
    cdflag=0; 
 oper=trans(substr($1,31,6));#操作员号 
  printf("%-12s,%s,%014d,%s\n", actno,cdflag,amt,oper); 
 } 

一个AWK程序分成三个部分,开始,中间处理和结束部分。开始部分用BEGIN{}表示,结束部分用END{}表示,而中间部分用{}围起来即可。BEGIN部分是在正式处理开始之前,做的一些准备工作。而END就是在全部记录都处理完毕之后,做的一些扫尾的工作。

大家可以看到,AWK的语法和C语言很相近,还可以定义函数。而事实上AWK的确在很多地方上都类似于C,所以对C语言有基础的读者应该很快就能掌握AWK的用法。

首先,在开始部分,我们将字段分隔符FS设为\n,就表示将一行作为一个字段来处理

其次,我们定义了一个函数trans。其功能主要是将用ASCII码方式表示的数字字符给还原成正常的数字形式。数字0-9的ASCII码对应为 30-39。所以要将一个ASCII码转化成对应的数字,只要将该ASCII码减去30即可。例如1的ASCII码为31,那么31 - 30 =1 就很快得出了31所对应的数字。Trans函数正是应用了这一原理,将ASCII码转换成对应的数字,然后将这些数字连接起来,形成账号或者操作员号,返回给调用者。

在正式处理部分,我们大量的运用了substr函数。这是个内置函数,是用于提取字符串中的部分字符。要记住,通过OD的转换之后,我们这里用2位数字代表一个字节。所以账号虽然是8个字节,而我们提取时,要取16个字节。

此外,由于账号和操作员号现在是以ASCII方式表示,所以需要通过trans函数翻译一下。而金额部分由于是直接由16进制ASCII码值来表示了,反倒不用再翻译了。

输出时用到了printf函数。这个函数的用法和标准的C语言printf函数用法完全一样。根据输出数据结构的要求,printf对帐号和金额进行了修饰,使得帐号左对齐且长度为12个字节,用空格对后面不足部分进行填充;而金额部分用前导零填充。

执行以上程序,就得到了我们想要的结果。

# awk -f ck1.awk fxt.a 
25856009  ,0,00000050000000,101 
25800234  ,1,00000208020200,102 

时间: 2024-10-11 00:04:17

巧用AWK处理二进制数据文件的相关文章

matlab读取二进制数据文件的方法

matlab可以直接读取二进制数据文件,并且可以将其加入到矩阵中. 如果对c语言十分熟悉的话,应该对fopen,fclose,ftell,fseek,fread,fwrite,feof 这些函数非常熟悉了,幸运的是在matlab中仍然可以使用这些函数来读入实验数据. 现在假定有一个数据文件叫data.dat,它的前面2k是存放参数的,我们做数据处理的时候 需要跳过去,后面的数据是16位整数类型的,每组数据有512个.现在要把该数据文件的 所有数据读入一个nx512的矩阵中,n的个数不定,根据数据

BLOB存储图片文件二进制数据是非对错

子在一天一天虚度,生活也在一天一天中茫然 做人做事哪能尽如人意,付出多少收获多少虽然存在偏颇,但是不劳而获的心态是万万不对的,更不能去怨天尤人,低调为人.做好自己就可以了 改进你的系统的最好的方法是先避免做“蠢事”.我并不是说你或你开发的东西“蠢”,只是有些决定很容易被人们忽略掉其暗含的牵连,认识不到这样做对 系统维护尤其是系统升级带来多大的麻烦.作为一个顾问,像这样的事情我到处都能见到,我还从来没有见过做出这样的决定的人有过好的结果的. 图片,文件,二进制数据 既然数据库支持 BLOB 类型的

MySql通过二进制日志文件恢复数据

在<百度.阿里.腾讯如何承载PB级别大数据>的视频中了解到,大型网站的数据库每天都会定时的进行数据备份份.如果设置每天的0点进行数据备份,在两个数据备份周期期间数据库出现宕机情况,0点到宕机这个时间段的数据如何备份呢?在MySql中是通过数据库的二进制日志文件进行数据恢复的. MySql的二进制日志文件默认是关闭的,需要我们在MySql根目录下的my.ini文件中设置为开启状态.设置方式为在[mysqld]节点下,添加log-bin=mysql  binlog-do-db=spring,mys

WebSocket api与服务器返回的数据类型判断(文件、二进制数据)

一.为什么需要 WebSocket? 初次接触 WebSocket 的人,都会问同样的问题:我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处? 答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起. 举例来说,我们想了解今天的天气,只能是客户端向服务器发出请求,服务器返回查询结果.HTTP 协议做不到服务器主动向客户端推送信息. 这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦.我们只能使用"轮询":每隔一段时候,就发出一个

二进制文件文本文件和二进制数据

我们知道计算机是用二进制来做运算处理的,所以所有存储在计算机里面的东西都是二进制的. 我也知道这句话,但为什么总是听到别人说"二进制"文件和"文本"文件呢? 按照上面那句话来说计算机里面的都应该是二进制的啊! 底层存储的都是二进制的"数据",而不是二进制的文件. 列举一个二进制文件如下: 00000000h:0F 01 00 00 0F 03 00 00 12 53 21 45 58 62 35 34; .........S!EXb54 0000

巧用Mysqlbinlog恢复数据库数据

关于binlog的详解请参考:http://zlyang.blog.51cto.com/1196234/1833062 binlog日志用于记录所有更新了数据或者已经潜在更新了数据的所有语句.语句以"事件"的形式保存,它描述数据更改.当我们因为某种原因导致数据库出现故障时,就可以利用binlog日志来挽回(前提是已经配置好了binlog),接下来我们来配置 一.开启mysql-binlog日志 在mysql配置文件my.cnf加上如下配置 [mysqld] log-bin=mysql-

数据库中用varbinary存储二进制数据

问题描述:将图片.二进制文件内容等数据存储在数据库中,并能从数据库中取出还原为图片或文件,数据库存储二进制数据用varbinary字段. 分析:由于之前数据库中没有用过varbinary存储数据,首先要把varbinary搞懂了,其次就是图片类型与二进制类型之间的转换,文件类型与二进制类型之间的转换. 准备工作:     1.varbinary 与 binary的区别:             固定长度 (binary) 的或可变长度 (varbinary) 的 binary 数据类型.    

Atitit.&#160;二进制数据ascii表示法,与base64编码解码api&#160;设计标准化总结java&#160;php&#160;c#.net

Atitit. 二进制数据ascii表示法,与base64编码解码api 设计标准化总结java php c#.net 1. Base64编码,1 1.1. 子模式 urlsafe Or  url unsafe2 1.2. 其他的二进制数据表示法  bin2hex() ,Quoted-printable ,UUencode2 2. Base64常用api2 2.1. ------------解码api2 2.2. decode(String s, OutputStream out)2 2.3. 

使用 bcp 指定数据文件中的前缀长度

使用 bcp 指定数据文件中的前缀长度 当以本机格式将数据大容量导出到数据文件时,为使文件存储空间最为紧凑,bcp 命令将在每个字段前面使用一个或多个字符来指示字段的长度. 这些字符称为"长度前缀字符". bcp 的前缀长度提示 如果某个交互式 bcp 命令包含不带格式化文件开关 (-f) 或数据格式开关(-n.-c.-w 或 -N)的 in 或 out 选项,则该命令会提示输入每个数据字段的前缀长度,如下所示: Enter prefix length of field <fie