最近写程序不知道怎么写envi标准格式文件的头文件,在网上搜了半天,也没找到相关的信息。找到一个 ENVI_SETUP_HEAD函数,也不知怎么用。下面的内容可能以后用的着,先留着吧。
引用自:http://bbs.esrichina-bj.cn/ESRI/viewthread.php?tid=56472&page=1
在IDL中读写数据
介绍IDL中的常用的输入和输出程序。IDL中的基本原则是:“只要有数据,就可以将其读进IDL”。IDL对格式没有要求,也不要求对数据进行准备。这使得IDL成为目前功能最强、最灵活的科学可视化分析语言。
具体来说,包含以下内容
1. 如何打开文件进行读写
2. 如何查找文件
3. 如何获得文件I/O的逻辑设备号
4. 如何获得机器的独立文件名
5. 如何读写ASCII或格式化的数据
6. 如何读写二进制的或二进制数据
7. 如何处理大型数据文件
8. 如何读写通用的文件格式,如GIF和JEPG文件
1.打开文件进行读写
IDL中的所有输入和输出都是通过逻辑设备号完成的。可以把一个逻辑设备设想为一个管道,这个管道连接着IDL和要读写的数据文件。要从一个文件中读写数据,必须首先把一个逻辑设备号连接到一个特定的文件。这就是IDL中三个Open命令的作用:
openr 打开文件进行读。
openw 打开文件进行写。
openu 打开文件进行更新(也就是说,读和/或写)。
这三个命令的语法结构是完全相同的。首先是命令名,后面是一个逻辑设备号和要与该逻辑设备号相连的文件名。例如,将文件名temp596.dat和逻辑设备号20相连以便可以在此文件里写入内容。如下:
OpenW, 20,’temp596.dat’
将会看到Open命令更常用的书写方式。例如:
OpenR, lun, filename
此例中,变量lun保存了一个有效的逻辑设备号,变量filename代表一个机器特定的文件名,这个文件名将和此逻辑设备号联系起来。
注意,变量filename是一种机器特定的格式。这意味着如果它含有特定的目录信息,它必须用本地机器的语法来表达。而且它在某些机器(比如,UNIX机器)上具有大小写敏感性,因为在这些机器上文件名有大小写敏感性。
查找和选择数据文件
IDL被广泛使用的原因之一,是IDL可以在许多不同的计算机操作系统中运行。但由于不同的操作系统有不同的文件命名习惯(而且,特别用确定子目录的不同方式),这在以独立于机器的方式指定文件名方面提出了挑战。幸好,IDL提供了一些工具可让这项工作变得容易些。
1.1选择文件名
也许获得机器独立文件名最容易的方法是用Pickfile对话框。IDL命令允许用机器上自身的选择文件的图形对话框来交互式地从文件名列表中选择一个文件名。例如,从本地目录.pro文件列表中选择一个文件名,可以键入如下命令:
IDL>filename=Dialog_Pickfile(Filter=’*.pro’,/Read)
注意,这个命令在IDL5.0以前的版本中命名为Pickfile。
IDL5.2版通过关键字Multiple,赋予Dialog_Pickfile选择多个文件名(若它们存在于同一个目录下)的能力。使用了正常的依赖于平台的选择文件方式。例如,在用WinDOws操作系统的计算机上,通常先选择第一个文件,接着用Shift键和鼠标点击来选择在第一个文件和第二个文件之间的所有文件,或者用Control键和鼠标点击来选择一个额外的文件。
IDL>filename=Dialog_Pickfile(Filter=’*.pro’,/Read,/Muitiple)
如果要打开文件来写而不是去读,在对话框中,可用Write关键字代替Read关键字。甚至可以推荐一个缺省的文件名,键入:
IDL>outfile=Dialog_Pickfile(File=’default.dat’,/Write)
从这个对话框中返回的是带绝对路径的文件名,其形式与运行IDL的机器有关。也就是说,它使用机器自身的文件命名语法。键入以下命令就可以看到:
IDL>Help, filename, outfile
注意,Dialog_Pickfile对话框中有一个“取消”按钮。若选择“取消”按钮,对话框会返回一个空字符串。所以在打开文件读写之前,总是希望检查返回的名字是否为空。
IDL>IF outfile EQ ‘’ THEN Print, ’Whoops!’
1.2 选择目录名
在IDL5.2中,Dialog_pickfile得到改进,因而它也能用于选择目录名而不仅是一个文件名。设置Directory关键字,在选择窗口内只列出目录而没有文件。
IDL>directory=Dialog_Pickfile(/Directory)
2.寻找文件
另一个有用的命令是FindFile命令。此命令返回一个包含所有符合给定文件要求的文件名的字符串数组。这在IDL程序中用于自动匹配并打开文件的任务中非常有用,或者是在任何时候不知道一个目录下有多少个文件的情况下,用于在该目录下创建一批文件的任务中也是非常有用的。
例如,要打印出当前目录下所有数据文件的长度(按字节计),可键入IDL代码:
Files=findfile(‘*.dat’,count=numfiles)
If numfiles eq 0 then message,‘no data files here!’
FOR j=0,numfiles-1 DO BEGIN
Openr, 10, files(j)
fileinfo=fstat(10)
print,fileinfo.size
Close, 10
ENDFOR
需要重点指出的是:FindFile命令中文件说明(’*.dat’)是以相对路径名给出,而不是绝对路径名。因此,命令返回也是相对路径名,而不是绝对路径名。这不同于Pickfile对话框,Pickfile总是返回绝对路径名。
构造文件名
获得机器独立文件名的第三个很有用的IDL命令是Filepath命令。例如,假设要打开文件galaxy.dat,它在IDL主目录下的 examples/data子目录中。可以为此文件构造一个机器独立的绝对路径名,键入:
IDL>galaxy=filepath(‘galaxy.dat’,Subdirectory=[‘examples’,’data’]
如果想从其它的目录开始而不是IDL主目录,可以用Root_Dir关键字指定开始的目录名。例如,要在当前目录的子目录coyote中构造此文件的一个路径名,可以键入:
IDL>cd,current=thisdir
IDL>galaxy=filepath(‘galaxy.dat’,root_dir=thisdir,$
Subdirectory=’coyote’)
注意:Filepath命令并没有实际找到此文件,它只是构造了一个文件路径名。构造的文件名甚至在机器中可以不存在。
3.获取逻辑设备号
在IDL中所有文件输出和输入都是在一个逻辑设备号上完成的。一个Open命令的作用是将一个特定的文件(通过其文件名来指定)和一个逻辑设备号相关联。有128个逻辑设备号可供使用。它们被分成两类,一类可以直接使用,另一类用Get_Lun和Free_Lun命令获取和管理。
① 1-99 :可在Open命令中直接使用
② 100-128:通过Get_Lun和Free_Lun命令获取和管理
3.1 直接使用逻辑设备号
要直接使用逻辑设备号,只能在1-99中选择一个号,并用Open命令使用它。要做的是选择一个当前没有使用的号。例如,可用逻辑设备号5来打开当前目录的子目录coyote中的galaxy.dat文件,键入:
IDL>CD,current=thiedir
IDL>filename=Filepath(Root_Dir=thisdir,$
Subdirectory=’coyote’,’galaxy.dat’)
IDL>Openr, 5, filename
一旦1-99中的某个逻辑设备号分配给一个文件后,它就不能再分配了,直到该逻辑设备号被关闭或退出IDL(这将自动关闭所有打开的逻辑设备号)。
当完成了对逻辑设备号的操作(也就是说,不想再对文件进行读写),可用Close命令关闭它,并使其可以重新使用:
IDL>Close, 5
3.2 让IDL管理逻辑设备号
多数情况下(特别在IDL程序中),最好让IDL管理逻辑设备号。可使用Get_Lun和Free_Lun命令完成此项功能。有两种方法让IDL返回一个逻辑设备号。可以直接使用Get_Lun命令。如:
IDL>Get_Lun,lun
IDL>OpenR,lun,filename
或者,用带关键字Get_Lun的Open命令来间接完成:
IDL>OpenR,lun,filename,/Get_Lun
这个命令运用隐含的Get_Lun命令将作为结果的逻辑设备号存入变量Lun中。这是逻辑设备号最常用的获取方法,特别是在IDL程序中。(注意,变量名不一定是Lun。可以自己给出喜欢的名字。如果打开了几个文件,就需要几个不同的名字。)
当完成了对逻辑设备号的操作(也就是说,不想再对文件进行读写),可用Free_Lun命令关闭它。如:
IDL>Free_Lun,Lun
使用Get_Lun和Free_Lun命令的好处在于不必记住哪个逻辑设备号是可用的或没被使用的。Get_Lun程序保证返回一个有效的逻辑设备号(假设同时已经打开的文件少于28个)。如果是在过程和函数中打开文件,一般最好使用Get_Lun命令。因为如果直接选择某个特定的逻辑设备号,不能保证它是可用的或没有被使用的。
3.3 判断哪些文件和哪些逻辑设备号相连
使用带Files关键字的Help命令,可以很容易判断哪些文件和哪些逻辑设备号相连。
IDL>Help,/Files
4.读取带有文件头的非格式化文件
有时一个文件包含有文件头信息,读取里面的数据需要绕过文件头。
对于头文件可将其看成是一个字符串。
pro readheaderfile,size=size
cd,'c:\'
; file =dialog_pickfile() ;读取头文件目录
print,file
openr,lun,file,/get_lun ;打开头文件,将其读入内存,赋予内部逻辑号lun
header =fstat(lun) ;fstat函数用来统计文件的信息,将其赋予一个结构变量
help,header ,/structure
print,header.size ;结构变量中的size即为文件的大小
size=header.size
free_lun,lun
end
pro readfile
cd,'c:\'
readheaderfile,size =size
;读取波谱文件
; file1 =dialog_pickfile() ;读取波谱文件目录
file1='spect1.txt'
print,file1
openr,lun,file1,/get_lun ;打开文件,将其读入内存,赋予内部逻辑号lun
point_lun,lun,size ; 定位到size处,有时候需要调整位置,如果数据前有空格也算一个字符
spect =findgen(2,3) ;定义输出文件的大小,2列3行的文件,可以根据数据大小随意设置
readf,lun ,spect ; 读取文件中的数据
print,spect
openw,lun,'c:\spect.txt',/get_lun; 建立一个输出文件
printf,lun,spect ;将数据保存到data.txt中
close,/all
end
5. 读写格式化数据
IDL在读写格式化数据方面有两种格式化文件之区分:自由文件格式和确定的文件格式。格式化文件有时叫做ASCII文件或者纯文本文件。
自由文件格式:自由格式文件用逗号或空白(tab键和空格键)分开文件中的每个元素,这没有确定的文件格式正规。
确定的文件格式:确定的格式文件是用格式说明按照给定的规范进行编排的。IDL格式说明和FORTRAN或C程序中的格式说明类似。
5.1 写自由格式文件
在IDL中,写一个自由格式文件极其容易。只要用PrintF命令将变量写入文件即可。这和在显示窗口上用Print命令打印变量的形式几乎相同。IDL在写文件时自动在数据的元素间加入空格。
例如,键入下面这些命令,创建数据并写入文件:
IDL>array=FIndGin(25)
IDL>vector=[33.6,77.2]
IDL>scalar=5
IDL>text=[‘array’, ’vector’, ’scalar’]
IDL>header=’Test data file.’
IDL>created=’created: ’ + SysTime()
接着,将一个逻辑设备号(让IDL自己选择一个并放到变量lun中)和特定文件相连来打开文件写入,键入:
IDL>OpenW , lun, ’test.dat’,/Get_Lun
最后,将数据写入文件,并关闭数据文件,键入:
IDL>PrintF, lun, header
IDL>PrintF, lun, created
IDL>PrintF, lun, array, vector, scalar, text
IDL>Free_Lun, lun
用一个文本编辑器打开文件检查。类似于如下所示:
Test data file.
created: Tue Nov 28 15:50:58 2000
0.000000 1.00000 2.00000 3.00000 4.00000 5.00000
6.00000 7.00000 8.00000 9.00000 10.0000 11.0000
12.0000 13.0000 14.0000 15.0000 16.0000 17.0000
18.0000 19.0000 20.0000 21.0000 22.0000 23.0000
24.0000 33.6000 77.2000 5
array vector scalar
注意:IDL 在数组变量的每个元素间设置空白区,并使每个新变量另起一行开头。IDL在缺省情况下使用80列宽度。如果需要不同的列宽度,可以用带Width关键字的OpenW命令设置。
5.2 读自由格式文件
许多ASCII文件是自由格式文件。例如,从电子数字表程序中保存的文件大多是自由格式文件。一种特殊的自由格式数据是从键盘或标准输入读取的数据。
在IDL中,有两种命令可以读取自由格式文件。
① Read:读取从标准输入或键盘上读入自由格式数据
② ReadF:从文件中读入自由格式数据
ascii文件格式属于自由文件格式的一种,对于其读取可以采用read_ascii(),返回的是一个结构变量
data=read_ascii('aa.txt',data_start=5);其实数据是从第6行开始的,也就是在读取的时候跳过头文件部分
或者:
filetemplate =ascii_template(file)
data =read_ascii('aa.txt',template=filetemplate)
其他的自由格式文件则直接可用readf读取
readf可以同时读取多个文件,将其放入到一个文件中。
IDL>ReadF, lun, header, data, vector
对于确定格式文件,应用format关键字即可。
5.3 读取自由格式文件的规则
无论是从键盘上还是从文件中读取自由格式的数据,IDL遵循下列7种规则:
(1)如果读入到字符串变量中,那么,在当前行剩下的所有字符都将读入该变量中。
观察上面数据文件的第一行,此行内有三个单词。此项规则意味着,一旦IDL开始为字符串变量读入数据,那么IDL将一直读到行末,且不会停止。其原因是在IDL的所有数据类型中,字符串变量可以是任意尺寸的,而且空格符也是ASCII字符。
打开该数据来读入,试着只读入一个单词,如下:
IDL>Open, lun, ‘test.dat’, /Get_Lun
IDL>word = ‘’
IDL>ReadF,lun,word
IDL>Print, word
所看到的是整个第一行都被读入到word变量中。
Test data file.
(如果想将第一行分解成单个的单词,可以用IDL的字符串处理程序来完成。)这种可以读到行末的功能是IDL的一个优点。为什么这样说呢?首先,将文件指针重置到文件的开头。可用Point_Lun命令实现此项功能。键入:
IDL>Point_Lun, lun, 0
现在可将数据文件的前两行读入到header变量中。键入:
IDL>header = StArr(2)
IDL>ReadF, lun, header
IDL>Print, header
这些命令将读出文件的前两行,并将文件指针定位到数组的起始处。可以在同一行上同时输出头两行的内容。
Test data file. created: Tue Nov 28 15:50:58 2000
(2)输入数据必须用逗号或空白分隔(空格键或tab键)。
在test.dat数据文件中所用到的就是这种格式的数据。IDL在数组变量中的每个元素间插入5个空格。
(3)输入通过数字变量完成。数组和结构都可作为数字变量的集合。
这意味着,如果正在读入的变量中,比如说,10个元素,IDL将从数据文件中读入10个分开的数值。它将采用以下的两条规则来确定这些数据存在于文件的何处。
(4)如果当前读入行是空的,并且还有变量要求输入,则读取另一行。
(5)如果当前读入行不是空的,但是没有变量要求输入,则忽略此行剩下的数。
为了解其含意,可键入:
IDL>data = FltArr(8)
IDL>ReadF, lun, data
IDL>Print, data
将看到:
0.00000 1.00000 2.00000 3.00000 4.00000 5.00000
6.00000 7.00000
在此例中,IDL从文件中读入8个分开的数据值。当读取到第一行数据的末端时,它自动进入到第二行(规则4),因为还有更多的数据要求读入。当数据读入到第二行的中部时,规则5起作用了。如果现在还要读入更多的数据,那将从数据的第三行开始,因为第二行的其它部分被忽略了。键入:
IDL>data = FltArr(3)
IDL>ReadF, lun, vector3
IDL>Print, vector3
将看到:
12.0000 13.0000 14.0000
变量vector3包含的数值为12.0、13.0和14.0。现在,文件指针定位在文件中的第四数据行上(规则5)。
(6)尽量将数据转换为变量所希望的数据类型。
要了解这是什么意思,将第四、第五行数据读入到一个字符串数组,以便让文件指针定位在文件中的第六行数据(即起始数值为33.6000的那行)。键入:
IDL> dummy = StrArr(2)
IDL> ReadF, lun, dummy
假设想读入两个整型值。IDL尽量将数据(在此情况下,为浮点数)转换为整型。
键入:
IDL> ints = IntArr(2)
IDL> ReadF, lun, ints
IDL> Print, ints
将看到:
33 77
注意,浮点数被简单的截取成整数。在转换处理过程中并没有采用四舍五入的规则来保证最接近的整数值。
7. 复数数据必须有实数和虚数两部分,用逗号分隔,并用括号括起来。如果仅仅提供了单个数值,它将被认为是实数部分,而虚数设置为0。例如,可通过键盘读入复数数据:
IDL>value = ComplexArr(2)
IDL> Read, value
: (3, 4)
: (4.4, 25.5)
IDL> print, value
在结束这部分学习之前,确保已将test.dat文件关闭。键入:
IDL>Free_Lun, lun
5.4 读写自由格式文件的实例
学会用IDL读写数据最容易的方法是看一些实例。下面实例说明在读入文件头、处理以列排列的数据以及处理已读入IDL的数据这些方面的常用IDL技巧。
读一个简单数据文件
从读刚创建的test.dat文件中的数据开始。首先,创建用于读文件中数据的变量。
IDL>header=strarr(2) ; two header lines.
IDL>data=fltarr(5,5) ; floating point array.
IDL>vector=intarr(2) ; two-element integer vector.
IDL>scalar=0.0 ; floating-point scalar.
IDL>string_var=’’ ; a string variable.
注意,此时数据变量将是5*5的数组。而在文件中是以25个元素的矢量保存的。用这种方法读入数据相当于读入25个元素的矢量,并将其重新格式化为5*5的数组。记住IDL中的数据是按行存储的。
根据规则一,不能将文件末端的一行文本读到三个元素的字符串数组中。不得不将其读入单个字符串变量,然后用IDL的字符串处理命令将该文本字符串分解到后续的变量中。
可立即从文件中读出所有数据,键入:
IDL>openr, lun, ’test.dat’, /Get_Lun
IDL>ReadF, lun, header, data, vector, scalar, string_var
IDL>free_Lun, lun
要将包含文件末行文本的字符串变量转换成三个元素的字符串数组,首先要将变量前后两头的空白字符除去。键入:
IDL>thisstring=strtrim(string_var,2)
有时若将字符串转换成字节型数组,字符串的处理就会容易些。可以先处理字节型数组,最后再将其转换成字符串。当然也可使用其它方法,但是这种方法在这里更好一些。
IDL>thisarray=Byte(thisstring)
IDL>help,thisarray
这是19个元素的字节型数组。要知道空格的ASCII码字符,可用IDL查出:
IDL>blank=Byte(“ ”)
IDL>print,blank
IDL>help,blank
注意,Byte命令处理一个字符串后的返回值总是一个数组。在此例中,是一个元素的数组。为了以后不至于混淆,将其转换成一个数值。
IDL>blank=Blank[0]
输出空白字符的ASCII码值是32。
可用Where命令显示字节型数组中的空白字符。
IDL>vals=Where(thisarray EQ blank)
最后,将字符串转换成三个元素的数组。
s=strarr(3)
s[0]=string(thisarray[0:vals[0]-1])
s[1]=string(thisarray[vals[0]+1:vals[1]-1]
s[2]=string(thisarray[vals[1]+1:*]
5.5 写列格式数据文件
在文件中数据按列储存是不稀奇。需要了解如何用IDL读写这种数据。IDL将会给粗心的程序员一个惊喜。要知道究竟是怎么回事,可以写一个列格式数据文件。用下列命令将数据读入IDL:
IDL>data=LoadData(15)
这个数据是一个有三个字段的结构:lat, lon和temp。每个字段都是41个元素的浮点矢量。可用如下命令从结构中提取矢量:
IDL>lat=data.lat
IDL>lon=data.lon
IDL>temp=data.temp
接着,打开一个写入数据文件,键入:
IDL>OpenW,lun,’column.dat’, /Get_Lun
需要将三列数据写入这个文件,通过自由格式输出。这可以在一个循环中完成。
IDL>printf, lun, ’column data: lat, lon, temp’
IDL>FOR j=0, 40 DO printf, lun,lat[j], lon[j], temp[j]
IDL>free_lun, lun
column.dat文件的前四行应如下:
Column data: lat, lon, temp
33.9840 –86.9405 36.9465
26.2072 –121.615 20.1868
42.1539 –103.733 231.604
5.6 读列格式数据文件
到目前为止一切正常。当试着将列格式数据读入 IDL时问题就出来了。可能得按如下操做。首先,创建要读入数据的变量。
IDL>header=’’
IDL>thislat=fltarr(41)
IDL>thislon=fltarr(41)
IDL>thistemp=fltarr(41)
打开column.dat文件读首行:
IDL>OpenR, lun, ’column.dat’ ,/Get_Lun
IDL>ReadF, lun, header
由于是用一个循环将数据放进文件的,所以也可能会用一个循环从文件中将数据读出。
IDL>FOR j=0, 40 DO ReadF, lun, thislat[j], thislon[j], thistemp[j]
但这不奏效。虽然上面的命令没有错误,但没有数据读入变量(如打印变量值,它们将是零)。其原因是IDL中有一个严格的规则,即不能带下标的变量来读入内容。原因是IDL 将带下标的变量作为值而不是作为变量的引用传递给象ReadF这样的IDL 程序。以数值传递的数据不能在被调用的子程序中改变,因为被调用的子程序只是获得该数据的备份,而不是获得该数据的指针。要改变这种属性需要对IDL进行大改,然而这是不可能的。
有两种方法解决这个问题。第一种是将数据读入一个循环中的临时变量。这种方法最好是运用文本编辑器将命令输入文件中来完成,因为很难在IDL命令行上编写多行循环。可用Point_Lun命令将文件指针返回到数据文件的起始处。
IDL>Point_Lun, lun, 0
在文本文件loopread.pro中输入下列命令。
temp1=0.0
temp2=0.0
temp3=0.0
ReadF, lun, header
FOR j=0, 40 DO BEGIN
ReadF,lun, temp1, temp2, temp3
Thislat[j]=temp1
Thislon[j]=temp2
Thistemp[j]=temp3
ENDFOR
END
执行文本文件中的代码:
IDL>.Run loopread
将原始矢量的值和刚读入矢量的值打印出来,将发现它们是相同的。例如:
IDL>Print, lat, thislat
虽然这个方法奏效,但它不是最好的方法。主要是因为它用了一个循环,循环在IDL中很慢。41次循环的速度也许无所谓,但如果是41,000次循环,执行速度将是个麻烦问题。
较好的方法是将数据一次性读到3*41的数组内,然后在用IDL提供的数组处理命令将矢量从这个较大数组中提出。要知道这是如何实现的,可用下面命令将数据文件指针返回到头:
IDL>Point_Lun, lun, 0
接下来,将数据一次性读到3*41的浮点数组:
IDL>header=’’
IDL>array=fltarr(3,41)
IDL>ReadF, lun, header, array
用数组下标将大数组的矢量分离出来:
IDL>thislat=array[0,*]
IDL>thislon=array[1,*]
IDL>thistemp=aarry[2,*]
IDL>Free_lun, lun
注意,这些新的矢量是列矢量(也就是说,是1*41的二维数组。)键入:
IDL>Help, thislat, thislon, thistemp
要使这些列矢量转换成更熟悉的行矢量,可用Reform命令将1*41 的数组转换成41*1的数组。当多维数组的最后维为1时,IDL 便舍弃这个维。键入:
IDL>thislat=ReFORm(thislat)
IDL>thislon=ReFORm(thislon)
IDL>thistemp=ReFORm(thistemp)
IDL>Help, thislat, thislon, thistemp
5.7 创建读列格式数据的模板
由于许多人都用有列格式数据文件,IDL5引进了一个新的程序以便更容易读此类数据文件。提供帮助的是两个新命令:ASCII_Template和Read_ASCII命令。ASCII_Template命令是个组件程序,可以引导读者按步骤定义自己的列数据。可以给每列数据命一个名字,告诉IDL数据类型,甚至可以跳过一些列。运行该程序的结果是用结构变两的形式生成了数据文件的模板。这个模板可以传给Read_ASCII命令,数据就会按模板规范来读取。其结果是一个根据模板的规范读出来的带有字段名的IDL结构变量。要知道它如何运用到上面文件中,可键入:
IDL>fileTemplate=ASCII_Template(‘column.dat”)
按照出现在显示器上的组件对话框模式指导进行操作。其形式有三页,在第一页上可以看到数据文件的样本行,左边有它们的行号。在标有Data Starts at Line的文本组件中:输入2。这允许在文件中跳过一行的文件头。在组件的右下角选择Next按钮进入下一页。
注意在这页里,每行的字段数列出的是三,并可选择White Space按钮作为数据分隔符。若信息正确,就点击Next 按钮进入最后一页。
在这页中,数据组可以被命名并且可以被指定数据类型。如果想在数据中跳过一列或多列,可在界面的右上角的下拉式列表框Type中将其设为Skip Field类型。通过界面右上角的Name文本组件,将三个字段分别命名为Latitude,Longitude和Temperature。这些字段都是浮点类型。
当完成命名和选择数据列的数据类型后,选择界面上的Finish按钮。运行结果是一个用于描述文件中数据的IDL结构变量,它们可被用作Read_ASCII命令的输入。
IDL>help, fileTemplate, /structure
要读文件中的数据,可使用Read_ASCII命令。
IDL>data = Read_ASCII (‘column.dat’, Template= fileTemplate)
数据立刻被读取,结果是包含三个字段latitude, longitude, temperature的IDL结构变量。
IDL>Help, data, structure
如果要提出结构中的矢量,可键入:
IDL>thislat=data.latitude
IDL>thislon=data.longitude
IDL>thistemp=data.temperature
ASCII_Template和Read_ASCII命令既可用于自由格式的数据文件,也可用于下面将要讨论的确定格式的数据文件。
5.8 用确定的文件格式写入
读写确定文件格式可同样用ReadF和PrintF命令,它们刚才已用于自由格式文件,但现在文件格式已由Format关键字明确声明。(在读写标准输入和输出时,也可将Format关键字用于Read 和Print命令)。
Format关键字的语法和在FORTRAN程序中使用的格式规则类似。尽管格式规则很复杂,这儿却有许多共同之处。如果使用过FORTRAN代码来读写数据的话,便会很快地熟悉它们。
(1) 一些共有的格式说明符
在IDL中有许多格式说明符,有一些是共有的。如矢量数据的定义:
IDL>data=Findgen(20)
① I 修饰整型数据。
将数据按整数输出,以每个两位,每行 5个,每个之间有两个空格的形式打出:
IDL>thisFormat=’(5(I2,2x),/)’
IDL>Print, data, Format=thisFormat
② F 修饰浮点数据。
将数据按浮点数输出,以小数点后两位,每行一个的形式打出。(这个数要为小数点留出一位)
IDL>thisFormat=’(f5.2)‘
IDL>Print,data,Format=thisFormat
③ D 修饰双精确数据。
将数据按双精度输出,按每行写5个,每个数之间有4个空格,小数点右边有10位的形式打出。
IDL>thisFormat=’(5(D13.10, 4x))’
IDL>Print, data*3.2959382, Format=thisFormat
④ E 用科学记数法描述浮点数据(如:116.36E4)
IDL>thisFormat=’(E10.3)’
IDL>Print, data*10E3, Format=thisFormat
⑤ A 描述字符型数据。
将数字转换成字符串,并以四个字符长的字符串形式写出,每个字符串用两个空格分开,每行4个字符串:
IDL>thisFormat=’(4(A4, 2x))’
IDL>Print,StrTrim(data,2), Format=thisFormat
⑥ NX 跳过n个空格字符。
(2)写用逗号分隔的确定格式数据文件
有时数据文件必须用确定格式书写,以方便它们被其它软件读取。用逗号分隔的数据文件就是这类文件的典型代表。例如,要把上面读到的数据写成此类文件。下面就是如何实现。可用Format.dat作为写入的文件名,键入:
IDL>OpenW, lun, ’Format.dat’, /Get_Lun
创建一个逗号字符串变量,如:
IDL>comm.=’ , ‘
以确定格式写出文件,用10位宽并有三位小数的浮点数,后接一个逗号和两个空格,键入:
IDL>thisFormat=’(F10.3, A1, 2x, F10.3, A1, 2x, F10.3)’
IDL>FOR j=0,40 DO PrintF,lun, thisLat[j],comma,$
thisLon[j],comma,thisTemp[j],Format=thisFormat
IDL>Free_lun,Lun
数据文件前三行如下所示:
48.000, -121.128, 36.946
44.843, -108.133, 163.027
29.865, -109.668, 89.870
(3)读出用逗号分隔的确定格式文件
要读取刚创建的确定格式数据文件,键入:
IDL>OpenW, lun, ’Format.dat’, /Get_Lun
IDL>thisFormat=’(2(F10.3, 3X), F10.3)’
IDL>array=Fltarr(3, 41)
IDL>ReadF, lun, array, Format=thisFormat
IDL>Free_lun, lun
这很简单,注意只要按上面的格式把逗号当作空格跳过即可。
(4)从字符串中读取格式数据
ReadS是一个有用的IDL命令,可以从字符串变量而不是从文件中为自由格式或确定格式读取数据。ReadS运用了和命令Read和ReadF相同的读取格式数据规则。也就是说,使用ReadS 就象从数据文件中读取一样,所不同的是所读的对象是一个字符串变量。
当大量信息需从文件头部读取时,此命令特别有用。例如,假设ASCII 数据文件的第一行说明了数据文件的行数和列数,随后是采集数据的时间。例如:
10 24500 12 June 1996
此文件头可以从文件中读取,并且可创建一个大小正确的数组来读取数据。如下:
firstLine=’’
ReadF,lun,firstLine
columns=0
rows=0
date=’’
ReadS,firstLine,columns,rows,date
dataArray=FltArr(columns,rows)
6.读写非格式化数据
迟早,数据会越来越多。若这样,就要开始思考更好的数据储存方法。非格式化数据(有时叫二进制数据)比格式化数据紧凑得多,经常用于大数据文件。有两种命令读写非格式化数据,它们与早期用来读取格式数据文件的ReadF和 Print 命令等效。它们是ReadU和WriteU命令。
非格式化数据文件基本是以一长串的二进制字节存在文件中。这些字节的含义(也就是说,这些字节如何翻译成特定数据类型和结构的)很艰难的描述的,除非刚开始就知道文件写入的是什么内容。在文件里面,各字节都很相似。将字节读入正确类型和结构的变量中就可理解了。原则上这很容易做到,因为多数数据类型有给定的字节长度。例如,每个浮点值有4个字节。IDL整数有两个字节,等等。
要读取非格式数据文件,简单定义变量,打开文件读取,并用ReadU命令将字节一个接一个地读入变量中。如果给定了变量的数据类型和组织结构,每个变量按其要求从文件中读出相应的字节数。例如,一个5个元素的浮点矢量将从文件中读取五(元素数)乘四(一个浮点值的字节数)共二十个字节。
6.1 读取非格式化图像数据文件
例如,假设想读取储存在coyote子目录下的几个非被格式化图像数据文件中的一个。这些文件碰巧包含的是字节型数据,但它们也能容易地包含整数与浮点数。下面的一些命令被用来打开其中之一,galaxy.dat文件。星系图像的字节已被组织成一个256*256字节的数组。(这个代码已假定coyote子目录存在于IDL主目录中。)
IDL>filename=Filepath(Root_Dir=!Dir, $
Subdirectory=’coyote’,’galaxy.dat’)
IDL>OpenR, lun, filename, /Get_Lun
此幅图像的结构为一个256*256的数组。如果事先不知道这些,为正确地读取这个数据,将会花费很大的精力。因为在数据文件里没有信息提示这些字节是如何被组织起来的。
在研究非格式化数据文件之前,如果不知道它有多大,那么只有一件事情要做。这也就是它包含多少字节。例如,FStat(文件的状态)命令能告知文件的字节数。
IDL>fileInfo=Fstat(lun)
IDL>Print,fileInfo.size
65536
在这个文件里有65536个字节,但这没有告知这个数据的结构是怎样组织的。例如,这些字节能被组成128*512的二维数组,也可以被组成64*64*16的三维数组。没有办法知道这些。绝望的程序员有时尝试用各种数组组合来显示数据。如果这个看起来不对,就试着用另外一个,等等。
在此例子中,这些数据被组成256*256字节型数组。因此图像变量能够这样设定:
IDL>image=BytArr(256,256)
现在,从文件里读取数据,然后关掉文件,如下:
IDL>ReadU, lun, image
IDL>Free_Lun, lun
显示数据,键入:
IDL>Window,Xsize=256,Ysize=256
IDL>Tvscl, image
6.2 写非格式化图像数据文件
假设在图像上进行了一些处理,想将结果保存在另一个数据文件里。例如,在图像上进行了一个Sobel边界增强操作,如下:
IDL>edge=Sobel(image)
Sobel操作不仅可以帮助图像增强它的边缘,而且被返回的图像不再是字节类型。事实上,它是整数数据,键入:
IDL>Help, image, edge
在IDL中整数占两个字节,因此如果这个数据被写入一个数据文件,这个文件的大小将会是原来的字节型数据文件的两倍。这些对于那些试图读取处理后的图像数据文件的人来说可能有些混淆。所以可以考虑在文件里给出一些信息,告诉用户关于这种数据的相关类型以及怎样组织的。这个文件信息可以按如下定义:
IDL>fileInfo =’Sobel Edge Enhanced, 256 by 256 INTEGERS’
这些字符串可以在文件里写在图像数据的前面。
但是,字符串fileInfo也是一串字节。在此例中,它是一个有31个字节的字符串。如果不知道关于此非格式化文件的这一点,以后从文件里读取数据将会非常困难。例如,设或许认为信息字符串是30个字节长。这样,在读完文件信息字符串之后,继续读出的每一个整数(两个字节)将会完全是错误的。
为避免这么多的限制,大多数非格式化数据文件的文件头有着固定的大小(通常是256的倍数)。例如,假设决定所有的图像文件都有一个512个字节的头文件。可用IDL 里的Replicate和String命令创造一个有512个字节长的空格字符串。
IDL>header = String (Replicate (32B, 512))
字节值32是一个空格字符的ASCII值。把文件信息字符串插入这个长一点的文件头字符串中,从而建立具有一个正确尺寸的头文件。可以用IDL里的StrPut (把一个字符串放进另外一个字符串)命令。如下:
IDL>strPut, header, fileInfo, 0
最后,将文件头和数据写入一个新的非格式化数据文件中。如下:
IDL>OpenW, lun, ‘process.dat’
IDL>WriteU, lun, header, edge
IDL>Free_Lun, lun
当字符串被写进非格式化的文件之时,也就相当于,字符串含多少个字符,就有多少个字节被写进文件。
6.3 读取带有文件头的非格式化数据文件
假设想读取上面刚建立的文件,里面有512个字节的头文件信息,紧接着是256*256*2个字节的图像数据。可以将头信息看成是一个字符串,因为它含有关于文件里保存的数据类型的文本信息。
对于格式化的数据,文件头的变量可以被创建成空一个字符串或一个空字符串数组,以便数据文件的全部行能一次读入到文件头变量。对于非格式化的文件这是不可能的。事实上,非格式化字符串数据的规则是当从文件里读取字符串时,仅仅只是读取确定个数的字节去填满该字符串目前的长度。这样,一个文件头被定义为一个空字符串,将不会从非格式化的文件里读出什么。
这意味着在从文件中读取字符串之前,必须知道正在读的字符串长度。在刚创建的process.dat文件里,文件头有512个字节长。可以用String和Replicate命令建立一个合适长度的空白字符串去读入,象前面那样。但是更容易的方法是把文件头读进一个字节型数组变量,然后把它转变成一个字符串。键入:
IDL>OpenR, lun, ‘process.dat’, /Get_Lun
IDL>header =BytArr(512)
IDL>ReadU, lun, header
IDL>Print, String(header)
Sobel Edge Enhanced,256 by 256 INTEGERS
从这条信息里面,就能够建立正确的数据数组,并从文件里读出图像数据并显示它们。如下:
IDL>edgeImage =IntArr(256,256)
IDL>ReadU, lun, edgeImage
IDL>Free_Lun, lun
IDL>Window, XSize=256,YSize=256
IDL> TV, edgeImage
6.4 非格式化数据文件的一些问题
不幸的是,虽然处理非格式化的数据较方便,但同时在使用这种数据是也有一些相关的问题。首先,非格式化的数据与机器类型有很大关系。在SUN计算机上写的数据,如果不做任何处理的话,在SGI或者HP计算机上经常读不出来,而在PC或者Macintosh计算机上是肯定读不出来的。(ByteOrder命令能够用来解决许多这类问题。IDL5.1版将新的Swap_If_Big_Endian和Swap_If_Little_Endian关键字引入到Open命令中,可用于在各种各样的机器结构上编写代码来读取二进制数据。)
为了能在不同的机器结构上传递非格式化数据,IDL支持XDR(eXternal Data Representation,外部数据表示)文件格式。XDR格式是Sun Microsystems创建的公用的数据格式。在几乎所有的现代化计算机上都可用。它在二进制文件里存储了少量的元数据(数据本身的一些附加信息)。但是XDR文件仍然很简洁。
如果文件是用XDR非格式化的形式写的,数据文件在计算机之间很容易传递。换句话说,XDR非格式化文件成为跨机器结构的文件格式。
要读写XDR格式的文件,必须用XDR关键字打开。例如:把上面的process.dat文件写成XDR文件,可以键入:
IDL>OpenW, lun, ‘process.dat’, /Get_Lun, /XDR
常规的WriteU命令用来把数据写进文件:
IDL>WriteU, lun, header, edge
IDL> Free_Lun, lun
在XDR文件里字符串的长度被存储起来,并随着字符串本身一起被恢复。这意味着不必要象一般的非格式化文件那样,每次都初始化一个正确长度的字符串变量。例如,打开读取XDR文件里的信息,可以键入:
IDL>OpenR, lun, ‘process.dat’,/XDR
IDL> thisHeader = ‘’
IDL> thisData =IntArr(256,256)
IDL> ReadU, lun, thisHeader, thisData
IDL> Free_Lun, lun
6.5 用关联变量存取非格式化数据文件
大型的非格式化数据文件通常都有一系列的重复单元组成。例如,一个卫星每隔半小时就拍摄一幅512*600像素的浮点图像,并将这些图像一个接一个地存储在一个数据文件里,这个文件每隔一定的时间被下载一次。在数据文件里包含50-100M的数据是很寻常的。一个IDL关联变量通常是处理这种数据形式的最好方式(有时候是唯一的方式)。
IDL关联变量是把一个IDL数组或结构变量的组织结构映射到数据文件的内容上。文件被看作是这些重复单元的一个数组。第一个单元的索引号是0,第二个单元的索引号1等等。关联变量不象常规变量那样将整个数据组都存储在内存里。而是当一关联变量被引用时,IDL仅对需要的部分数据执行相关的输入或输出请求,这部分数据就是要读入内存的。
6.5.1 关联变量的一些优点
关联变量有以下几个优点:
① 当该变量被用于表达式时,才产生文件的输入和输出动作。不需要单独的读或写命令。
② 数据集的大小不受内存容量的限制,因为有时它可处理大型的数据集。对于物理存储器来说是太大的数据,通过把此数据分成块就能很容易地处理。
③ 不必提前声明用于映射该数据的数组或结构的数量。
④ 关联变量是效率最高的I/O形式。
6.5.2 定义关联变量
定义和使用关联变量,可按通常的方式打开数据文件,然后用Assoc命令创建关联变量。例如,打开位于IDL主目录下的coyote 子目录中的abnorm.dat文件。
IDL> filename = Filepath(Subdir=’coyote’, ‘abnorm.dat’)
IDL> OpenR, lun, filename, /Get_Lun
这个文件里含有16幅图像或16帧画面,每幅都是64*64个字节型数组。为这些数组创建关联变量:
IDL> image = Assoc(lun, BytArr(64,64))
Assoc命令的第一个参数是与image变量相关联的文件的逻辑设备号。第二个参数是文件中被重复的单元的描述。
这些文件在重复文件单元前通常有文件头信息,尽管此文件没有。如果这样的话,Assoc命令的第三个定位参数是给定文件头的大小或文件头在文件中的偏移量。例如,假设abnorm.dat文件的前4096个字节是文件头信息,并且希望跳过文件头,那么Assoc命令可以被写成这样:
IDL> image2 = Assoc(lun, BytArr(64,64), 4096)
注意,现在有两个变量,image和image2,与同一个数据文件关联。这在IDL中完全合法,事实上,这对于存取重复单元不一致的非格式化数据文件是一种好办法。通过改变在文件中的偏移量,可以用关联变量来实现随机读写数据。
显示上面image变量里的第五幅图像或画面,键入:
IDL> TvScl, image (4)
从数据文件里把数据读进一个临时变量,在其被显示后,被IDL删除。没有显式的ReadU命令,也不需要常规情况下IDL处理这幅图像所需要的永久内存。如果想从关联变量中创建一个变量,可按通常的方式建立这个变量。