测井曲线wis文件格式转换为ASCII文本格式小软件的开发(C与Python结合开发)

在油田开发地质研究工作中,测井曲线是必不可少的数据文件之一。Forward软件是测井软件中非常优秀的产品,Forward中使用的是wis二进制格式,可是很多其他类型的软件不能直接识别wis格式,所以需要我们将wis格式文本格式。Forward软件有一个transfer小软件,不能实现批量处理,每次只能转换一个文件,但实际工作中通常一次要转换上百个文件,这就有点不方便了,因此,我在业余时间,阅读了Forward软件中自带的wis格式说明(如下,第一章内容),利用C和Python编写了一个可以批量处理的小软件。

第1章  WIS测井数据文件格式

WellBase平台底层数据文件使用WIS格式,WIS文件分为文件头、对象入口记录和对象数据三部分。数据存放以块为单位。

WIS文件能存放三种类型的对象,根据对象的类型分为通道对象、表对象和流对象。通道对象一般用来存放采集数据和处理结果(如测井曲线),表对象用来存放二维表数据(如解释结论),流对象用来存放二进制数据块(如解释参数,用户数据)。

1.1  WIS文件结构

1.1.1  文件标识

WIS文件标识从文件偏移零开始,为10个字节的字符。当前版本的标识为WIS 1.0

1.1.2  文件头结构

头结构紧接文件标识。描述WIS文件的公共信息。结构定义如下:

typedef struct tagWIS_HEAD

{

WORD         MachineType;

WORD         MaxObjectNumber;

WORD         ObjectNumber;

WORD         BlockLen;

DWORD      EntryOffset;

DWORD      DataOffset;

DWORD      FileSize;

DWORD      TimeCreate;

char             Reserved[32];

}WIS_HEAD;

 

偏移               字节数             描述

0                     2                       机器类型=1 PC  =2SUN  =3IBM

                                               =4HP

2                     2                       允许记录的最大对象数。缺省为512个,该值可以在文件产生时给出。

4                     2                       当前记录的对象总数(包括删除和抛弃的对象)。

6                     2                       块长。WIS文件对象占用的磁盘空间以块为单位,该值指示每一数据块的字节数。

8                     4                       对象入口记录从文件开始的偏移量。

12                   4                     对象数据记录从文件开始的偏移量。

16                   4                       WIS文件的字节数大小。

20                   4                       WIS文件产生的时间。

24                   32                     保留字节。

1.1.3  对象入口

对象入口描述每个对象的公共信息,开始位置由头结构给出。每个对象的描述信息前后相连。结构定义如下:

typedef struct tagWIS_OBJECT_ENTRY

{

char             Name[16];

long             Status;

short            Attribute;

short            SubAttribute;

DWORD      Position;

DWORD      BlockNum;

DWORD      TimeCreate;

DWORD      TimeWrite;

char             Reserved[32];

}WIS_OBJECT_ENTRY;

 

偏移            字节数               描述

0                  16                         对象的名称,以零结尾的字符串。

16                4                          对象的状态:=0为正常; =1为抛弃; =2为删除。

20                2                    对象的主属性:=1为通道对象; =2为表对象; =3为流对象。

22                2                          对象的子属性,描述对应主属性的子属性。

24                4                          对象数据体从文件开始处的偏移量。

28                4                          对象数据体占用磁盘的块数。

32                4                          对象产生的时间。

36                4                          对象最近修改的时间。

40                32                         保留字节。

1.1.4  对象数据体

对象数据体记录各个对象的具体特性及数据。根据不同的主属性分三种类型。对象数据体在WIS文件中的位置由对象入口指定。

 

1.1.5  通道对象

通道对象用来存放采集和计算结果数据(如测井曲线)。分为通道信息和通道数据两部分。

WIS文件将在一定时空内对某一采集或计算的物理信息数据集统称为通道数据。通道信息描述通道数据的存放形式,分为基本信息和维信息,基本信息描述信息的基本物理含义,维信息描述信息的时空特性,可以等间隔(连续)或非等间隔(离散)。最大允许有四维信息,通道信息共占用一个块空间,结构定义如下:  

typedef struct tagWIS_CHANNLE

{

char            Unit[8];

char            AliasName[16];

char            AliasUnit[16];

WORD       RepCode;

WORD       CodeLen;

float           MinVal;

float           MaxVal;

WORD       Reserved;

WORD       NumOfDimension;

WIS_CHANNEL_DIMENSION DimInfo[4];

}WIS_CHANNEL;

 

偏移      字节数        描述

0                  8                          对象的单位,以零结尾的字符串。

8                  16                         对象的别名,以零结尾的字符串。

24                16                         单位的别名,以零结尾的字符串。

40                2                          对象数据类型,参见3.2.1

42                2                          数据类型的长度。

44                4                          对象的最小值(测井曲线缺省左刻度值)

48                4                          对象的最大值(测井曲线缺省右刻度值)

52                2                          保留字节。

54                2                          对象维信息数。

56                4*56                     对象维信息。

 

通道维信息结构定义如下:

typedef struct tagWIS_CHANNLE_DIMENSION

{

char             Name[8];

char             Unit[8];

char             AliasName[16];

float             StartVal;

float             Delta;

DWORD      Samples;

DWORD      MaxSamples;

DWORD      Size;

WORD         RepCode;

WORD         Reserved;

}WIS_CHANNEL_DIMENSION;

 

偏移      字节数        描述

0                  8                          维的名称,以零结尾的字符串。

8                  8                          维的单位,以零结尾的字符串。

16                16                         维的别名,以零结尾的字符串。

32                4                          维的开始值。

36                4                          维的采集或计算增量。对于离散数据,该值为0,数据中记录该维的值。

40                4                          维的数据采样点数。如果该值为0,采样点数为可变值,数据中记录该值。对于第一维数据,该值不能为0

44                4                          维的数据采样最大点数。该值仅当采样点数信息为0(可变采样点)时有效,该维信息在数据中所占用的字节数通过该值计算。

48              4                          该维上每一采样点所占用的字节数。

52                2                          维的数据类型,参见3.2.1

54                2                          保留字节。

 

 

通道数据从通道描述信息的下一块开始。

下面为一个包含深度和时间维的物理信息数据体的存放顺序。第一维为深度,第二维为时间。

 

[A1]+[N2]+[B1]+X1+[B2]+X2+···+[BN]+XN+

[A2]+[N2]+[B1]+X1+[B2]+X2+···+[BN]+XN+

···

···

[AN]+[N2]+[B1]+X1+[B2]+X2+···+[BN]+XN

 

其中:

A1A2··· AN代表深度值,当深度维信息结构中的Delta为零时,记录此值。

N2代表当前深度点上的时间采样点数,当时间维信息结构中的采样点数为零时,记录此值。

B1B2··· BN代表时间值,当时间维信息结构中的Delta为零时,记录此值。

X1X2··· N代表物理信息的值。

1.1.6  表对象

表对象用来存放二维表数据,分为表信息和表数据体两个部分。表信息由不同的表项组成,每一表项称为字段。表信息结构定义如下:

typedef struct tagWIS_TABLE

{

DWORD      RecordCount;

DWORD      FieldCount;

WIS_TABLE_FIELD *pField;

}WIS_TABLE;

  

偏移      字节数         描述

0                  4                            表的记录数。

4                  4                            表的字段数。

8                  4                            指向字段信息结构的指针。

 

 

 

字段信息结构定义如下:

typedef struct tagWIS_TABLE_FIELD

{

char             Name[32];

WORD         RepCode;

WORD         Length;

DWORD      Reserved;

}WIS_TABLE_FIELD;

 

偏移      字节数        描述

0                  32                字段的名称,以零结尾的字符串。

32                2                  字段值的浮点类型,参见3.2.1

34                2                  字段值的长度。

 

表数据体(记录)从表信息记录的下一块开始。

1.1.7  流对象

流对象用来存放二进制数据块。开始为4个字节的无符号长整形数,代表数据流的长度。接着为该流的二进制值。

 

第二章  WIS测井数据格式转换文本格式C程序代码

代码如下:

<textarea cols="61" rows="15" name="code" class="cpp">/* * Copyright (c) 2011, Sun Yunqiang * All rights reserved. * Software: wis2txt_win * Version: 1.0 * Author: Sun Yunqiang * English Name: Alex Sun * Release Date: 18/03/2011 */ /** ***************************************************************** */ /** This Program is for converting wis format file to txt format file */ /** Command: wis2txt_win.exe args1 args2 */ /** Description: */ /** wis2txt_win.exe: the executable file as command */ /** args1: the first argument that is the wis file name */ /** args2: the second argument that is the txt file name */ /** ***************************************************************** */ #include &lt;windows.h&gt; #include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include &lt;string.h&gt; #include &lt;ctype.h&gt; #include &lt;time.h&gt; #include &lt;math.h&gt; #define ROW 65536 #define COL 512 /* **************************************** */ /* Below blocks are the structs of wis file */ /* **************************************** */ typedef struct tagWIS_HEAD { WORD MachineType; // 0-PC 1-SUN 2-IBM 3-HP WORD MaxObjectNumber; WORD ObjectNumber; WORD BlockLen; DWORD EntryOffset; DWORD DataOffset; DWORD FileSize; time_t TimeCreate; char Reserved[32]; }WIS_HEAD; typedef struct tagWIS_OBJECT_ENTRY { char Name[16]; long Status; short Attribute; // 1-通道对象 2-表对象 3-流对象 short SubAttribute; // 1-曲线对象 2-波形对象 3-地层测试对象 4-时深时对象 DWORD Position; // 对象数据体从文件开始处的偏移量 DWORD BlockNum; time_t TimeCreate; time_t TimeWrite; char Reserved[32]; }WIS_OBJECT_ENTRY; typedef struct tagWIS_CHANNEL_DIMENSION { char Name[8]; char Unit[8]; char AliasName[16]; float StartVal; float Delta; DWORD Samples; DWORD MaxSamples; DWORD Size; WORD RepCode; WORD Reserved; }WIS_CHANNEL_DIMENSION; typedef struct tagWIS_CHANNEL { char Unit[8]; char AliasName[16]; char AliasUnit[16]; WORD RepCode; WORD CodeLen; float MinVal; float MaxVal; WORD Reserved; WORD NumOfDimension; WIS_CHANNEL_DIMENSION DimInfo[4]; }WIS_CHANNEL; typedef struct tagWIS_STREAM { DWORD Length; DWORD Offset; }WIS_STREAM; typedef struct tagWIS_TABLE_FIELD { char Name[32]; WORD RepCode; WORD Length; DWORD Reserved; } WIS_TABLE_FIELD; typedef struct tagWIS_TABLE { DWORD RecordCount; DWORD FieldCount; WIS_TABLE_FIELD *pField; }WIS_TABLE; typedef struct tagWIS_TABLE_DEFAULT_FILED { char Name[16]; char Alias[16]; char Unit[8]; char Type[8]; WORD Length; WORD Count; char DefVal[64][12]; } WIS_TABLE_DEFAULT_FIELD; typedef struct tagWIS_DEFAULT_TABLE { char Name[16]; char Alias[16]; char Attrb[8]; DWORD FieldCount; WIS_TABLE_DEFAULT_FIELD *pField; } WIS_DEFAULT_TABLE; /* 曲线的信息结构体*/ typedef struct tagCURVE_HEAD { char Name[16]; // 曲线的名字 char Unit[8]; // 曲线的单位 float startMD; // 曲线的起始深度 float stopMD; // 曲线的结束深度 int pointNum; // 数据样点数 int position; // 数据位置 struct tagCURVE_HEAD *next; } CURVE_HEAD; /* 曲线的数据结构体 */ typedef struct tagCURVE_DATA { char Name[16]; // 曲线的名字 char Unit[8]; // 曲线的单位 float Depth[ROW]; // 曲线的深度值 float Value[ROW]; // 曲线的值 struct tagCURVE_DATA *next; } CURVE_DATA; /* ************************************************************* */ /* Below block is the Main program of processing wis format file */ /* ************************************************************* */ int main(int argc, char *argv[]) { FILE *wisfile; // 输入文件 FILE *txtfile; // 输出文件 char *infilename = argv[1]; char *outfilename = argv[2]; // char *infilename = "demoa.wis"; // char *outfilename = "demoa.txt"; if(argc != 3 ) { printf("ERROR: Command parameters is not defined correctly!/n"); exit(1); } wisfile = fopen(infilename, "rb"); if(wisfile == NULL) { printf("ERROR: Read file terminated!/n"); exit(1); } txtfile = fopen(outfilename, "w"); if(txtfile == NULL) { printf("ERROR: Write file terminated!/n"); exit(1); } /* Process wis file, Read it and transfer it to text */ /** wis文件标识符从文件偏移0开始,为10个字节的字符 */ char wisid[10]; fread(wisid, 10, 1, wisfile); // printf("文件标识符: %s/n", wisid); fprintf(txtfile, "ASCII WIS %c%c%c/n", wisid[4], wisid[5], wisid[6]); /** 头文件紧接文件标识 */ WIS_HEAD *wishead; wishead = (WIS_HEAD *)malloc(sizeof(WIS_HEAD)); fseek(wisfile, 10, SEEK_SET); fread(wishead, sizeof(WIS_HEAD), 1, wisfile); // printf("对象入口记录从文件开始的偏移量EntryOffset: %d/n", wishead-&gt;EntryOffset); // printf("对象数据记录从文件开始的偏移量DataOffset: %d/n", wishead-&gt;DataOffset); // printf("当前记录的对象总数ObjectNumber: %d/n", wishead-&gt;ObjectNumber); /** 对象入口,位置由头结构中EntryOffset参数指定 最多512条曲线 */ /** curvePosition数组 保存曲线数据开始的位置 WIS_OBJECT_ENTRY-&gt;Position的值 */ /** curveName数组 保存每条曲线的名称 */ /** curveNum变量 文件中保存曲线的数目 */ int curvePosition[512]; int curveName[512][16]; int curveNum = 0; // curveNum is the identifer of the curves number fseek(wisfile, wishead-&gt;EntryOffset, SEEK_SET); // fseek(wisfile, 10+sizeof(WIS_HEAD), SEEK_SET); while(1) { WIS_OBJECT_ENTRY *objectEntry; objectEntry = (WIS_OBJECT_ENTRY *)malloc(sizeof(WIS_OBJECT_ENTRY)); fread(objectEntry, sizeof(WIS_OBJECT_ENTRY), 1, wisfile); if(objectEntry-&gt;Attribute == 0) { break; } else if(objectEntry-&gt;Attribute == 1) { // printf("通道类型: %d/n",objectEntry-&gt;Attribute); // printf("No. %d/n", curveNum); // printf("曲线对象的名称Name: %s/n", objectEntry-&gt;Name); // fprintf(outfile, "%s ", objectEntry-&gt;Name); // 打印曲线名字 // printf("对象数据体绝对偏移量Position: %d/n/n", objectEntry-&gt;Position); curvePosition[curveNum] = objectEntry-&gt;Position; int i=0; for(i=0; i&lt;16; i++) { curveName[curveNum][i] = objectEntry-&gt;Name[i]; } curveNum++; } else { continue; } } printf("/n%s文件的曲线数目: %d/n/n", infilename, curveNum); /* ****************************************************************** */ /* Below block read every curve head information and put it in a link */ /* ****************************************************************** */ /** 创建链表 data_node 开始读取数据 */ /** num变量 每条曲线的行数 */ CURVE_HEAD *first_head = NULL; float delta = 0.0; CURVE_HEAD *data_head = NULL; int num = 0; for(num = 0; num &lt; curveNum ; num++) // num相当于第几条曲线 { data_head = (CURVE_HEAD *)malloc(sizeof(CURVE_HEAD)); data_head-&gt;position = curvePosition[num]; fseek(wisfile, curvePosition[num], SEEK_SET); WIS_CHANNEL *channel; channel = (WIS_CHANNEL *)malloc(sizeof(WIS_CHANNEL)); fread(channel, sizeof(WIS_CHANNEL), 1, wisfile); // printf("对象的单位Unit: %s/n", channel-&gt;Unit); // printf("对象的别名AliasName: %s/n", channel-&gt;AliasName); // printf("单位的别称AliasUnit: %s/n", channel-&gt;AliasUnit); // printf("对象的最小值MinVal: %f/n", channel-&gt;MinVal); // printf("对象的最大值MaxVal: %f/n", channel-&gt;MaxVal); // printf("对象维信息数NumOfDimension: %d/n", channel-&gt;NumOfDimension); // printf("维的开始值StartVal: %f/n", channel-&gt;DimInfo[0].StartVal); // printf("维的增量值Delta: %f/n", channel-&gt;DimInfo[0].Delta); data_head-&gt;pointNum = channel-&gt;DimInfo[0].MaxSamples; // 深度采样点的个数 data_head-&gt;startMD = channel-&gt;DimInfo[0].StartVal; // 起始深度 delta = channel-&gt;DimInfo[0].Delta; // 间隔点值 data_head-&gt;stopMD = channel-&gt;DimInfo[0].StartVal + delta * channel-&gt;DimInfo[0].MaxSamples; // 结束深度 int i = 0; for(i=0; i&lt;16; i++) { data_head-&gt;Name[i] = curveName[num][i]; } for(i=0; i&lt;8; i++) { data_head-&gt;Unit[i] = channel-&gt;Unit[i]; } printf("No.%d/t", num+1); // 曲线编号 printf("%s/t", data_head-&gt;Name); // 曲线名 printf("%s/t", data_head-&gt;Unit); // 曲线单位 printf("%d/t", data_head-&gt;pointNum); // 采样点数 printf("%9.3f/t", delta); // 采样点间隔 printf("%9.3f/t", data_head-&gt;startMD); // 起始深度 printf("%9.3f/n", data_head-&gt;stopMD); // 结束深度 data_head-&gt;next = first_head; first_head = data_head; } /* *********************************************************************** */ /* Below block is for deciding the min and max Depth, and the max line num */ /* *********************************************************************** */ // 从这里开始判断所有曲线中最小深度和最大深度值 float minDepth = 0.0; float maxDepth = 0.0; minDepth = first_head-&gt;startMD; maxDepth = first_head-&gt;stopMD; for(data_head = first_head; data_head!= NULL; data_head=data_head-&gt;next) { if(minDepth&gt;data_head-&gt;startMD) { minDepth = data_head-&gt;startMD; } if(maxDepth &lt; data_head-&gt;stopMD) { maxDepth = data_head-&gt;stopMD; } } // printf("最小深度值:%f/n", minDepth); // printf("最大深度值:%f/n", maxDepth); // 利用最小最大深度值和间隔点值,确定输出的采样点数 float depth[ROW]; int lineNum = 0; lineNum = (maxDepth - minDepth) / delta; // printf("采样点个数:%d/n", lineNum); int row=0; for(row=0; row&lt;lineNum; row++) { depth[row] = minDepth + delta * row; } /* ******************************************************** */ /* Below block put every point value into a link as a curve */ /* ******************************************************** */ // 从这里开始将数据值放入曲线各自对应的链表里 CURVE_DATA *first_node = NULL; CURVE_DATA *data_node = NULL; data_head = first_head; for(num = 0; num &lt; curveNum ; num++) { data_node = (CURVE_DATA *)malloc(sizeof(CURVE_DATA)); int i = 0; for(i=0; i&lt;16; i++) { data_node-&gt;Name[i] = data_head-&gt;Name[i]; } for(i=0; i&lt;8; i++) { data_node-&gt;Unit[i] = data_head-&gt;Unit[i]; } // printf("曲线名称:%s/n", data_node-&gt;Name); // printf("曲线单位:%s/n", data_node-&gt;Unit); int startLine = 0; for(row=0; row&lt;lineNum; row++) { if( abs(depth[row]-data_head-&gt;startMD)&lt;= delta &amp;&amp; depth[row] &gt;= data_head-&gt;startMD ) { startLine = row; break; } } // printf("起始点:%d/n", startLine); // fseek(wisfile, data_head-&gt;position, SEEK_SET); fseek(wisfile, data_head-&gt;position+2*sizeof(WIS_CHANNEL)+464, SEEK_SET); // printf("测试样点数%d/n", data_head-&gt;pointNum); for(row=0; row&lt;lineNum; row++) { float data[1]; data[0] = -9999.000; if(row&gt;=startLine &amp;&amp; (row-startLine) &lt; data_head-&gt;pointNum) { fread(data, sizeof(float), 1, wisfile); data_node-&gt;Depth[row] = data_head-&gt;startMD + delta * (row-startLine); data_node-&gt;Value[row] = data[0]; } else { data_node-&gt;Depth[row] = minDepth + delta * row; data_node-&gt;Value[row] = -9999.000; } // printf("读取值:%f/n", data_node-&gt;Value[row]); } data_node-&gt;next = first_node; first_node = data_node; if(data_head-&gt;next == NULL) { break; } data_head = data_head-&gt;next; // printf("下一条曲线名:%s/n", data_head-&gt;Name); } /* **************************************************** */ /* Below block print all point value for every curve */ /* The important thing is how to decide the right depth */ /* **************************************************** */ // 从这里开始写入文本 // Print Curve Name fprintf(txtfile, "DEPTH "); for(data_node=first_node; data_node!=NULL; data_node=data_node-&gt;next) { fprintf(txtfile, "%9s ", data_node-&gt;Name); } fprintf(txtfile, "/n"); // printf("/n"); // Print Curve Unit fprintf(txtfile, "M "); for(data_node=first_node; data_node!=NULL; data_node=data_node-&gt;next) { fprintf(txtfile, "%9s ", data_node-&gt;Unit); } fprintf(txtfile, "/n"); // printf("/n"); // Print curve value for(row=0; row&lt;lineNum; row++) { if(depth[row] &gt; maxDepth) { break; } fprintf(txtfile, "%-9.4f ", depth[row]); for(data_node=first_node; data_node!=NULL; data_node=data_node-&gt;next) { // fprintf(txtfile, "%-9.4f ", data_node-&gt;Depth[row]); fprintf(txtfile, "%9.3f ", data_node-&gt;Value[row]); } fprintf(txtfile, "/n"); } // */ // Process Over fclose(wisfile); fclose(txtfile); printf("/n%s Complete!/n", infilename); // system("PAUSE"); // Program Over return 0; } </textarea>

最后,利用Python写好界面,并调用以上编译好的exe程序,这样就可以同时处理多个wis文件,界面如下:

右侧消息列表 会显示每个Wis文件处理的信息!

测井曲线wis文件格式转换为ASCII文本格式小软件的开发(C与Python结合开发)_第1张图片

当然转换后的结果文件会存到所选取的路径中。转换为文本后的曲线在进行处理或者运用就方便多了!

你可能感兴趣的:(c,struct,object,python,table,float)