oracle 范鑫_自己写得一个类似AUL的工具,附源代码(C 语言)
06年毕业,转眼也学习oracle 大概1年半得时间了,上次看到有人说可以恢复没有备份得truncate表,其实原理就是抽取数据得方式,因此自己也想写一个.通过几天得努力,终于写了一个初版 ,目前该版本支持得数据类型有: varchar2 ,number ,date 类型, 因为我看到得大部分系统表设计只涉及这些列类型(blob 除外 ,这个以后有空再写),现在只支持行迁移,不支持行连接(行连接我觉得一个设计好得系统不应该出现,可以使用大得block_size来避免,或做成多张表,因此也就没有花精力来做行连接),我这边程序测试得数据库版本为9201,数据块得大小为8k,其他数据块版本得,数据块情况没有测试过.
测试如下:建表
create table test_table
as
select * from all_objects;
SQL> desc test_table
名称 是否为空? 类型
----------------------------------------- -------- ----------------------------
OWNER NOT NULL VARCHAR2(30)
OBJECT_NAME NOT NULL VARCHAR2(30)
SUBOBJECT_NAME VARCHAR2(30)
OBJECT_ID NOT NULL NUMBER
DATA_OBJECT_ID NUMBER
OBJECT_TYPE VARCHAR2(18)
CREATED NOT NULL DATE
LAST_DDL_TIME NOT NULL DATE
TIMESTAMP VARCHAR2(19)
STATUS VARCHAR2(7)
TEMPORARY VARCHAR2(1)
GENERATED VARCHAR2(1)
SECONDARY VARCHAR2(1)
SQL> select count(*) from test_table;
COUNT(*)
----------
29280
SQL> set linesize 150
SQL> select * from test_table where rownum<10;
OWNER OBJECT_NAME SUBOBJECT_NAME OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE CREATED
------------------------------ ------------------------------ ------------------------------ ---------- -------------- ------------------ ----------
LAST_DDL_T TIMESTAMP STATUS T G S
---------- ------------------- ------- - - -
SYS /1005bd30_LnkdConstant 17286 JAVA CLASS 31-3月 -07
31-3月 -07 2007-03-31:00:38:40 VALID N N N
SYS /10076b23_OraCustomDatumClosur 7559 JAVA CLASS 31-3月 -07
31-3月 -07 2007-03-31:00:38:11 VALID N N N
SYS /10297c91_SAXAttrList 22542 JAVA CLASS 31-3月 -07
31-3月 -07 2007-03-31:00:39:39 VALID N N N
OWNER OBJECT_NAME SUBOBJECT_NAME OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE CREATED
------------------------------ ------------------------------ ------------------------------ ---------- -------------- ------------------ ----------
LAST_DDL_T TIMESTAMP STATUS T G S
---------- ------------------- ------- - - -
SYS /103a2e73_DefaultEditorKitEndP 13614 JAVA CLASS 31-3月 -07
31-3月 -07 2007-03-31:00:38:28 VALID N N N
SYS /1048734f_DefaultFolder 22345 JAVA CLASS 31-3月 -07
31-3月 -07 2007-03-31:00:39:31 VALID N N N
SYS /10501902_BasicFileChooserUINe 10173 JAVA CLASS 31-3月 -07
31-3月 -07 2007-03-31:00:38:19 VALID N N N
OWNER OBJECT_NAME SUBOBJECT_NAME OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE CREATED
------------------------------ ------------------------------ ------------------------------ ---------- -------------- ------------------ ----------
LAST_DDL_T TIMESTAMP STATUS T G S
---------- ------------------- ------- - - -
SYS /105072e7_HttpSessionBindingEv 22747 JAVA CLASS 31-3月 -07
31-3月 -07 2007-03-31:00:47:57 VALID N N N
SYS /106ba0a5_ArrayEnumeration 22797 JAVA CLASS 31-3月 -07
31-3月 -07 2007-03-31:00:48:02 VALID N N N
SYS /106faabc_BasicTreeUIKeyHandle 9849 JAVA CLASS 31-3月 -07
31-3月 -07 2007-03-31:00:38:18 VALID N N N
已选择9行。
SQL> spool off
程序抽取出来得数据:
SYS,/1005bd30_LnkdConstant,null,17286,null,JAVA CLASS,2007-03-31 00:38:40,2007-03-31 00:38:40,2007-03-31:00:38:40,VALID,N,N,N
SYS,/10076b23_OraCustomDatumClosur,null,7559,null,JAVA CLASS,2007-03-31 00:38:11,2007-03-31 00:38:11,2007-03-31:00:38:11,VALID,N,N,N
SYS,/10297c91_SAXAttrList,null,22542,null,JAVA CLASS,2007-03-31 00:39:39,2007-03-31 00:42:08,2007-03-31:00:39:39,VALID,N,N,N
SYS,/103a2e73_DefaultEditorKitEndP,null,13614,null,JAVA CLASS,2007-03-31 00:38:28,2007-03-31 00:38:28,2007-03-31:00:38:28,VALID,N,N,N
SYS,/1048734f_DefaultFolder,null,22345,null,JAVA CLASS,2007-03-31 00:39:31,2007-03-31 00:39:34,2007-03-31:00:39:31,VALID,N,N,N
SYS,/10501902_BasicFileChooserUINe,null,10173,null,JAVA CLASS,2007-03-31 00:38:19,2007-03-31 00:38:19,2007-03-31:00:38:19,VALID,N,N,N
SYS,/105072e7_HttpSessionBindingEv,null,22747,null,JAVA CLASS,2007-03-31 00:39:49,2007-03-31 00:48:00,2007-03-31:00:47:57,VALID,N,N,N
SYS,/106ba0a5_ArrayEnumeration,null,22797,null,JAVA CLASS,2007-03-31 00:39:52,2007-03-31 00:48:07,2007-03-31:00:48:02,VALID,N,N,N
SYS,/106faabc_BasicTreeUIKeyHandle,null,9849,null,JAVA CLASS,2007-03-31 00:38:18,2007-03-31 00:38:18,2007-03-31:00:38:18,VALID,N,N,N
..............................
..............................
RMAN920,XCF_P,null,30396,30396,INDEX,2007-10-06 10:12:55,2007-10-06 10:12:55,2007-10-06:10:12:55,VALID,N,N,N
RMAN920,XCF_U1,null,30397,30397,INDEX,2007-10-06 10:12:55,2007-10-06 10:12:55,2007-10-06:10:12:55,VALID,N,N,N
RMAN920,XDF,null,30412,30412,TABLE,2007-10-06 10:12:56,2007-10-06 10:12:57,2007-10-06:10:12:56,VALID,N,N,N
RMAN920,XDF_I_DF_KEY,null,30415,30415,INDEX,2007-10-06 10:12:57,2007-10-06 10:12:57,2007-10-06:10:12:57,VALID,N,N,N
RMAN920,XDF_I_HANDLE_STATUS,null,30416,30416,INDEX,2007-10-06 10:12:57,2007-10-06 10:12:57,2007-10-06:10:12:57,VALID,N,N,N
RMAN920,XDF_P,null,30413,30413,INDEX,2007-10-06 10:12:56,2007-10-06 10:12:56,2007-10-06:10:12:56,VALID,N,N,N
RMAN920,XDF_U1,null,30414,30414,INDEX,2007-10-06 10:12:56,2007-10-06 10:12:56,2007-10-06:10:12:56,VALID,N,N,N
共29280条数据
行迁移测试
有表发生了行迁移,查询出来得数据为:
.............
rrrrrrr .....其实这边有200多个r,就省略了
rrrrrrr.......
rrrrrrr......
rrrrrrr
rrrrrrr
rrrrrrr
b111
b111
b222
范 581 行
鑫3 582行
........
.......
........ 共1722行
程序抽取得数据为:
........
........
........
b111,
b111,
b222,
范, 1721行
鑫3, 1722行
可以看到 ,oracle 在做全表扫描得时候 ,是忽略行迁移得 ,即一直扫描行,并不会碰到行迁移,就会去读取目标行,因为目标行总会扫描到.
我得程序是顺序扫描得,当发生行迁移时,会去读目标行 ,所以抽取出来得数据不一样,程序抽取出来得数据时严格意义上得顺序得.
程序源代码如下:
#include
#include
#include
#include
//用于判断存储是大端方式还是小端方式
#define LITTLE 0
#define BIG 1
//定义block得大小为8k
#define BLOCK_SIZE 8
//itl 事务槽字节数,每个事务槽占用 24 个字节
#define ITL_SIZE 24
/*
整个block可以分为:block head+ data+tail
其中block head 又可分为:
1. block head 的基本信息 --即struct head 共占用44个字节
2. 具体的事务槽信息,具体大小不确定,计算方法为: head->itc*ITL_SIZE
3. 行字典 具体大小也不固定 ,根据行的多少来计算字节数
tail 在块的末尾: 共占用4个字节
可以看到:在block的中,具体的事务槽字节数目和行字典的字节数目都是会动态增长的,比如说对块的DML并发数量的增加,
会引起事务槽的增加,行的增加会影响行字典的增加,所以这就是为什么oracle 会从尾部开始插入行.如果将行从在前面开始
增加,势必会造成字符串的移动
*/
//head 结构用于对block的基本描述
struct head
{
unsigned char block_type; //一个字节
unsigned char unknown1[3]; // 3个字节
int rdba; //4个字节
int scn_front; //4个字节
unsigned short int scn_back; //2个字节
unsigned char seq;//对于该块进行的DML操作时同一个SCN号,seq次数会增加
unsigned char flg;
unsigned short int chkval; //校验所用
char unknown2[6];
unsigned int obj;
int csc;
int unused;
unsigned char itc; //事务槽的数目
char unknown3[7];
};
//row_dic结构用于对row_dictionary的描述
struct row_dic
{
unsigned char flag;
unsigned char ntab;
unsigned short int pre_nrows;
short int frre;
unsigned short int fsbo;
unsigned short int fseo;
unsigned short int avsp;
unsigned short int tosp;
//row_dictionary
unsigned short int offs;
unsigned short int nrows;
//行地址有可能为负,即ffff,-1,代表被删除的最后一行
short int pri[256]; //这里其实并不知道具体有多少行,但并不影响读取数据,这边是根据指针转换读取数据的
};
char col_type[255][20]={"varchar2","varchar2","varchar2","number","number",
"varchar2", "date", "date", "varchar2", "varchar2",
"varchar2", "varchar2", "varchar2"};
char support_type[3][20]={"varchar2","number","date"};
int real_col_cnt;
void parseNumber(unsigned char * head,int col_size,char *result);
void formatNumber(char *tmp_num1 ,int num_idx,int exp ,char *result);
int testLittleOrBig();
int vertify(int argc ,char **argv);
void parseRowMovment(unsigned char *p);
void parseRow(int fileid ,int blockid ,int rownum);
void parseDate(unsigned char *p,char *date);
/*
程序编制者: fxyj_2008 : 范鑫
程序版本: ver 1.0 试用版
*/
void main(int argc, char *argv[])
{
int fp;
unsigned char *p;
int i=0;
int temp;
int row;
int col;
unsigned int objectid;
char str[1024*BLOCK_SIZE+1];
struct head *phead;
struct row_dic *prow_dic;
int offset;
unsigned char *idx;
int col_cnt;
int col_size;
unsigned short int var_size;
char num[42];
long size;
long block_num;
char varchar2[4000];
char date[20];
/*
每个行由以下几个组成:
1. fb 占一个字节(行的相关信息)
2. lb 事务槽的指针
3. 列的数目
4. 列数据,列数据具体组成: 列长度+列数据
*/
unsigned char fb;
int lastpos=0;
str[1024*8]='\0';
if(argc<2)
{
printf("必须输入列得类型");
return;
}
if(vertify(argc,argv)==-1)
return;
else
{
for(i=1;i
strcpy(col_type[i-1],argv);
i=0;
}
/*
printf("enter the object id :");
scanf("%d",&objectid);
*/
objectid=30552;
real_col_cnt=argc-1;
if((fp=open("TST.DBF",O_RDONLY))==1)
return;
lseek(fp,0,SEEK_END);
size=tell(fp);
block_num=size/1024/BLOCK_SIZE;
lseek(fp,9*1024*BLOCK_SIZE,SEEK_SET);
for(int n=9;n
{
read(fp,str,sizeof(char)*1024*BLOCK_SIZE);
p=(unsigned char *)str;
phead=(struct head *)p; //将首地址转换为head 指针类型
prow_dic=(struct row_dic *)(p+sizeof(head)+ITL_SIZE*phead->itc); //获取行字典的首地址
/*获取行字典的偏移地址,在行字典中存放的行的位置其实是个偏移地址,偏移地址=44+事务槽占用的字节数据,所以每次有事务
槽扩展的时候行字典中行的位置都要做相应改变,以保证偏移地址的正确性
*/
offset=sizeof(head)+ITL_SIZE*phead->itc;
//由于文件系统提供的接口函数存在BUG ,主要是windows平台,所以这边需要写tell(fp);
lastpos=tell(fp);
if(phead->block_type==6)
{
// printf("%d\n",phead->obj);
//continue;
if(phead->obj!=objectid)
continue;
for(row=0;rownrows;row++)
{
// addr=(unsigned char *)(p+ps->pri[row]+92);
//行被删除标志,行字典中的行相对偏移地址
if((prow_dic->pri[row]nrows) || (prow_dic->pri[row]==-1))
{
continue;
}
fb=*(p+prow_dic->pri[row]+offset);
//行被删除标志,即fb的第5个字节为1,代表'D'
if((fb>>4)&0x01==1)
continue;
//如果fb的第6个字节为1,第3,4个字节为1,即H标志为1;F,L为1的情况下,表示行正常
if(fb==0x2c)
{
col_cnt=*(p+prow_dic->pri[row]+offset+2);
idx=p+prow_dic->pri[row]+offset+2+1;
for(col=0;col
{
col_size=*(idx);
idx=idx+1;
if(col_size==0xff) //FF 代表该值为null
{
printf("null");
if(col!=col_cnt-1)
printf(",");
continue;
}
if(strcmp(col_type[col],"varchar2")==0)
{
if(col_size==0xfe) //如果首字符大小是0xfe ,那么这个字符的大小用后两个字节表示
{
var_size=*((unsigned short int *)idx); //字符串真实大小
idx=idx+2;
col_size=var_size;
}
for(temp=0;temp
varchar2[temp]=*(idx+temp);
varchar2[temp]='\0';
printf("%s",varchar2);
}
else if(strcmp(col_type[col],"number")==0)
{
parseNumber(idx,col_size,num);
printf("%s",num);
}
else if(strcmp(col_type[col],"date")==0)
{
parseDate(idx,date);
printf("%s",date);
}
if(col!=col_cnt-1)
printf(",");
idx=idx+col_size;
}
if(col_cnt
{
for(temp=real_col_cnt-col_cnt;temp>0;temp--)
printf(",");
}
printf("\n");
}
//如果fb的第6个字节为1,第3,4个字节为0,即H标志为1;F,L为0的情况下,表示发生了行迁移,此时读取nrid
else if(fb==0x20)
{
parseRowMovment(p+prow_dic->pri[row]+offset+2+1);
continue;
}
else if((fb>>5)&0x01==0) //如果fb标志得H标志为0,表示是行迁移或行连接得数据,不处理
{
continue;
}
//如果fb标志得H标志为1,F标志为1 ,L 标志为0 ,表示发生了行连接,不处理
else if(((fb>>5)&0x01==1)&&((fb>>3)&0x01==1)&&((fb>>2)&0x01==0))
{
printf("系统暂不支持行连接\n");
continue;
}
}
}
}
close(fp);
}
int vertify(int argc ,char **argv)
{
int i;
int j;
int flag=-1;
for(i=1;i
{
for(j=0;j<3;j++)
if(strcmp(*(argv+i),*(support_type+j))==0)
{
flag=1;
break;
}
if(flag==-1)
return -1;
else
flag=-1;
}
return 1;
}
//用于测试存储方式 ,是大端存储还是小端存储
int testLittleOrBig()
{
int test=0x12345678;
unsigned char *p;
p=(unsigned char *)&test;
if(*p==0x12)
return BIG;
else
return LITTLE;
}
void parseRow(int fileid ,int blockid ,unsigned short int rownum)
{
int fp;
unsigned char *p;
int i=0;
int temp;
int col;
char str[1024*BLOCK_SIZE+1];
struct head *phead;
struct row_dic *prow_dic;
int offset;
unsigned char *idx;
int col_cnt;
int col_size;
unsigned short int var_size;
char num[42];
char varchar2[4000];
char date[20];
unsigned char fb;
str[1024*8]='\0';
if ((fp=open("TST.DBF",O_RDONLY))==1)
return;
lseek(fp,blockid*1024*BLOCK_SIZE,SEEK_SET);
read(fp,str,sizeof(char)*1024*BLOCK_SIZE);
p=(unsigned char *)str;
phead=(struct head *)p; //将首地址转换为head 指针类型
prow_dic=(struct row_dic *)(p+sizeof(head)+ITL_SIZE*phead->itc); //获取行字典的首地址
offset=sizeof(head)+ITL_SIZE*phead->itc;
fb=*(p+prow_dic->pri[rownum]+offset);
//行被删除标志,即fb的第5个字节为1,代表'D'
if((fb>>4)&0x01==1)
return;
//如果fb的第6个字节为0,第3,4个字节为1,即H标志为0;F,L为1的情况下,表示行正常
if(fb==0x0c)
{
col_cnt=*(p+prow_dic->pri[rownum]+offset+2);
//注意: 发生行迁移得时候 , 目的行存有原行得rowid,所以这边需要加6个字节
idx=p+prow_dic->pri[rownum]+offset+2+6+1;
for(col=0;col
{
col_size=*(idx);
idx=idx+1;
if(col_size==0xff) //FF 代表该值为null
{
printf("null");
if(col!=col_cnt-1)
printf(",");
continue;
}
if(strcmp(col_type[col],"varchar2")==0)
{
if(col_size==0xfe) //如果首字符大小是0xfe ,那么这个字符的大小用后两个字节表示
{
var_size=*((unsigned short int *)idx); //字符串真实大小
idx=idx+2;
col_size=var_size;
}
for(temp=0;temp
varchar2[temp]=*(idx+temp);
varchar2[temp]='\0';
printf("%s",varchar2);
}
else if(strcmp(col_type[col],"number")==0)
{
parseNumber(idx,col_size,num);
printf("%s",num);
}
else if(strcmp(col_type[col],"date")==0)
{
parseDate(idx,date);
printf("%s",date);
}
if(col!=col_cnt-1)
printf(",");
idx=idx+col_size;
}
if(col_cnt
{
for(temp=real_col_cnt-col_cnt;temp>0;temp--)
printf(",");
}
printf("\n");
}
//如果fb的第6个字节为0,第3,4个字节为1,0,即H标志为0;F为1 ,L为0的情况下,表示发生了行迁移中发生了行连接,行连接不处理
else if(((fb>>5)&0x01==0)&&((fb>>3)&0x01==1)&&((fb>>2)&0x01==0))
{
printf("行迁移中发生行连接,行连接暂不支持");
printf("\n");
return;
}
close(fp);
}
void parseRowMovment(unsigned char *p)
{
unsigned char rid[6]; //rowid,占6个字节 10个字节文件号+22个字节 blcokid+ 2个字节得行数
int fileid;
int blockid;
unsigned short int rownum;
int i=0;
unsigned int tmp;
int flag;
flag=testLittleOrBig();
if(flag==LITTLE)
{
for(i=0;i<4;i++)
rid=p[3-i];
rid[4]=p[5];
rid[5]=p[4];
}
tmp=*((unsigned int *)(rid));
fileid=tmp>>22;
blockid=tmp&0x003fffff;
rownum=*((unsigned short int *)(rid+4));
parseRow(fileid,blockid,rownum);
}
/*
date 在磁盘中存储共占用7个字节 ,ixora 上面资料获取
byte 1: century + 100
byte 2: year + 100
byte 3: month
byte 4: day of month
byte 5: hour + 1
byte 6: minute + 1
byte 7: second + 1
*/
void parseDate(unsigned char *p,char *date)
{
//2008-01-01 12:01:01
//年
*date=(*p-100)/10+'0'; *(date+1)=(*(p)-100)%10+'0';
*(date+2)=(*(p+1)-100)/10+'0'; *(date+3)=(*(p+1)-100)%10+'0';
*(date+4)='-';
//月
*(date+5)=(*(p+2))/10+'0'; *(date+6)=(*(p+2))%10+'0';
*(date+7)='-';
//日
*(date+8)=(*(p+3))/10+'0'; *(date+9)=(*(p+3))%10+'0';
*(date+10)=' ';
//时
*(date+11)=(*(p+4)-1)/10+'0'; *(date+12)=(*(p+4)-1)%10+'0';
*(date+13)=':';
//分
*(date+14)=(*(p+5)-1)/10+'0'; *(date+15)=(*(p+5)-1)%10+'0';
*(date+16)=':';
//秒
*(date+17)=(*(p+6)-1)/10+'0'; *(date+18)=(*(p+6)-1)%10+'0';
*(date+19)='\0';
}
/*
number 表示方法
1bit符号位+7bit指数位 数据位(100进制表示)
对于正数:
1bit符号位为1,接下来的一个字节也为1
数据位-1=真实数据
对于0
首个字节为0x80 ,没有数据位
对于负数
1bit符号位为0
数据位为补码表示,真实数据为101-数据位
具体如下:
00-3E 表示: number <= -1--从3E开始递减 , 3E表示1
3F-7F 表示: -1 < number < 0 --从3F开始递增 ,3F表示0
可以看到,正数和负数的划分在于看第一个bit
81-C0 表示: 0 < number < 1 --从C0开始递减,C0表示0
C1-FF 表示:number >= 1 --从C1开始递增,C1表示1
*/
void parseNumber(unsigned char * head,int col_size,char *result)
{
unsigned char tmp;
int num_idx=0;
int value;
int exp=0;
int p;
char tmp_num1[42];
tmp=*head;
head=head+1;
int k;
k=(tmp&0x80)>>7;
//数据>=0
if (k==1)
{
if(tmp==0x80)
{
result[0]='0';
result[1]='\0';
return;
}
else
{
for (p=0;p
{
value=head[p]-1;
tmp_num1[num_idx]=value/10+'0';
tmp_num1[num_idx+1]=value%10+'0';
num_idx=num_idx+2;
}
tmp_num1[num_idx]='\0';
exp=tmp-0xc0;
formatNumber(tmp_num1,num_idx,exp,result);
return;
}
}
else //数据<0
{
for (p=0;p
{
value=101-head[p];
tmp_num1[num_idx]=value/10+'0';
tmp_num1[num_idx+1]=value%10+'0';
num_idx=num_idx+2;
}
tmp_num1[num_idx]='\0';
exp=0x3e-tmp;
formatNumber(tmp_num1,num_idx,exp,result+1);
result[0]='-';
return;
}
}
void formatNumber(char *tmp_num1 ,int num_idx,int exp ,char *result)
{
int p=0;
char tmp_num2[42];
char prefix[40]="\0";
char post[40]="\0";
char const_c[]="0.";
if(exp>0)
{
if(2*exp==num_idx)
{
if(tmp_num1[0]=='0')
strcpy(result,tmp_num1+1);
else
strcpy(result,tmp_num1);
return;
}
else if(2*exp
{
for(p=0;p<2*exp;p++)
tmp_num2[p]=tmp_num1[p];
tmp_num2[p]='.';
for(;p
tmp_num2[p+1]=tmp_num1[p];
tmp_num2[num_idx+1]='\0';
if(tmp_num2[0]=='0')
strcpy(result,tmp_num2+1);
else
strcpy(result,tmp_num2);
return;
}
else
{
for(p=0;p
strcat(post,"00");
strcpy(tmp_num1+num_idx,post);
if(tmp_num1[0]=='0')
strcpy(result,tmp_num1+1);
else
strcpy(result,tmp_num1);
return;
}
}
else
{
if(exp==0)
{
strcpy(result,const_c);
strcpy(result+2,tmp_num1);
return;
}
else
{
for(p=0;p
strcat(prefix,"00");
strcpy(result,const_c);
strcpy(result+2,prefix);
strcpy(result+2+(exp*-1*2),tmp_num1);
return;
}
}
}
参考资料: 其中fb信息来自 grassbell 得 偷窥Data block 的物理结构,具体信息如下:
其中(摘自biti_rainy的关于block中数据的存储和重组的探究):
fb Flag Byte:
K = Cluster Key (Flags may change meaning if this is set to show HASH cluster)
C = Cluster table member
H = Head piece of row
D = Deleted row
F = First data piece
L = Last data piece
P = First column continues from previous piece
N = Last column continues in next piece
number 内部存储参考了 zhouwf 得部分章节
其他得基本都自己研究,具体研究方法其实也很简单
把数据块以16进制打印出来 ,然后用 alter system dump datafile N block M dump 出来 ,进行对比起来看
打印block 16进制得代码为,大家可以参考一下:
#include
#include
#include
char source[]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
void main()
{
int fp;
unsigned char *p;
int i=0;
int temp;
char str[1024*8+1];
str[1024*8]='\0';
if ((fp=open("TST.DBF",O_RDONLY))==1)
return;
lseek(fp,234*1024*8,SEEK_SET); //234 是我要打印得数据块
read(fp,str,sizeof(char)*1024*8);
p=(unsigned char *)str;
while(i!=1024*8)
{
temp=(*(p+i)&0xf0)>>4;
cout<
temp=*(p+i)&0x0f;
cout<
i++;
}
close(fp);
}
当然可以使用printf("%x",..) 函数直接打印16进制 ,但上面得代码我自己写了个打印16进制得,功能都一样;
[本帖最后由 fxyj2008 于 2008-1-14 14:21 编辑]
oracle 范鑫_自己写得一个类似AUL的工具,附源代码(C 语言)相关推荐
- java实现魔方_闲来无事,用java写了一个魔方小程序。附源码 | 学步园
闲来无事,用java写了一个魔方小程序.附源码 使用三维数组.相对来说还是简单.呵呵. import java.util.ArrayList; import java.util.List; impor ...
- 闲来无事,用java写了一个魔方小程序。附源码
闲来无事,用java写了一个魔方小程序.附源码 使用三维数组.相对来说还是简单.呵呵. import java.util.ArrayList; import java.util.List; impor ...
- python爬虫捕鱼网站_一个简易的爬虫工具,使用Python语言编写,用于zhihu全自动捕鱼...
简介 这是什么 这是一个简易的爬虫工具,使用Python语言编写,用于zhihu全自动捕鱼,理论上,你可以爬取你感兴趣的任何问题,而不仅仅是小姐姐. 如何使用 编程使用 请确保你的Python版本是3 ...
- 写接口给别人调用 推送数据到我们_我们写了一个超好用的抖音矩阵数据管理工具...
我最近跑了十来个抖音号,遇到一些问题,然后通过我们NB的程序员解决了.如果你也在做抖音矩阵,那这些问题你肯定也会遇到,所以我把解决问题的方法工具化了,给大家用.我遇到的最大的问题,就是账号数据的同步. ...
- aspose 转pdf表格大小乱了_自己写了一个小工具类:pdf转word,没有页数和大小限制,保真!...
昨天下午遇到一个问题,想把一个比较大的pdf转化为word,结果使用了各种工具都收费.想着干脆写一个小工具吧,一开始使用的python等等试了好几个网上的代码,结果全都失真.于是乎不得不花了一下午自己 ...
- github用相对路径显示图片_我写了一个开源工具, 让Github的README.md可以正常显示超大图片...
最终效果对比 图片替换前: 图片显示有好有坏,能否显示,全凭运气 图片替换后: 所有大图正常显示! 本项目永久开源地址 痛点: Github的README.md展示图片效果并不完美 为了让项目演示更生 ...
- 毕业时候写的一个PE解析小工具(MFC源码)
这么快就成了前年毕业的老家伙了.在整理硬盘里的代码和文档的时候翻出刚毕业时候写的一个小东西,想起来那时候在武汉的小河西村,暗无天日的租房里屌丝的写着程序的日子.一晃这么久了.还是混的这鸟样.悲伤逆流成 ...
- python打字_使用Python制作一个打字训练小工具
一.写在前面 说道程序员,你会想到什么呢?有人认为程序员象征着高薪,有人认为程序员都是死肥宅,还有人想到的则是996和 ICU. 别人眼中的程序员:飞快的敲击键盘.酷炫的切换屏幕.各种看不懂的字符代码 ...
- 用Python写了一个上课点名系统(附源码)
今天刷到了一个这样的短视频,我寻思我是不是也可以写一个类似的上课点名程序,想法经不起等待,说写就写~ 一.准备工作 1.Tkinter Tkinter 是 python 内置的 TK GUI 工具集. ...
- 搜索python代码的软件_用python编写一个高效搜索代码工具
用python编写一个高效搜索代码工具 大多码农在linux环境下使用grep+关键词的命令搜索自己想要的代码或者log文件.今天介绍用python如何编写一个更强大的搜索工具,windows下也适用 ...
最新文章
- 各种flash的不同
- 学python多贵_老男孩学习Python多少钱,学习Python贵吗?
- typedef的详细用法
- Pycharm-列出代码结构
- java全局变量和局部变量_Java 10 –局部变量类型推断
- webform窗体怎么实现session唯一标识_微信小程序用户登录和登录态维护的实现_javascript技巧...
- 本地安装的smushit,如何压缩图片
- android框架连接mysql_三层架构 android访问MSSQL数据库 程序 (服务器端)
- Atitit 减少财政支出之减少通讯支出 解决方案attilax总结
- 货币基金新规将出,限制T+0提现及支付额度
- 凸优化第六章逼近与拟合 6.1范数逼近
- java的打印语句_java打印输出语句是什么?
- (附源码)计算机毕业设计ssm大学生网络安全题库系统
- msdia80.dll文件出现在磁盘根目录下的解决方案
- Linux led_class子系统
- Win 批处理生成文件目录树
- 数字经济下,银行线上场景化建设的服务颗粒度、用户忠诚度和生态融合度
- 编程计算: 1!+3!+5!+...+(2n-1)!,要求阶乘计算调用fun函数实现, 数据输入及打印结果在主函数实现。阶乘计算fun函数原型为: long fun(int m); CQUPT题库
- VirtualBox - 让分辨率自适应窗口大小
- 2进制 16进制 计算机术语,十六进制转二进制计算器