在常规的字符串定义里,字符串的长度是固定的,即便其内容只有一部分,剩余的长度也会被填充为空格。使得我们的字符串每次使用都需要trim,也无法延伸得更长。因此,为了保证足够使用,我们往往会定义很长的字符串以供使用。
而递延长度字符串(deferred-length string)则可以摆脱这些束缚。他与可分配数组(allocatable array)用法相似,但更灵活,用法也更简单。
二. 简单的递延长度字符串例子
下面我们直接看一个例子
Program www_fcode_cn Implicit None Character(len=:) , allocatable :: str str = "Welcome" !// 自动allocate write(*,*) str , len(str) str = str // " to Fcode.cn" write(*,*) str , len(str) deallocate( str ) write(*,*) str , len(str) !// 此处出错 End Program www_fcode_cn
在这个例子里,str 被定义为一个递延长度字符串,长度为 : (冒号),即暂时不指定长度(注意不是星号,要与虚参字符串的自动长度相区别)。具有 allocatable 属性。读者可认为这是递延长度字符串的固定写法。需注意的是,递延长度字符串不能有初始值。像这样的写法是不合法的:
Character(len=:) , allocatable :: str = "MyTest"
递延长度字符串在定义以后,尚未分配空间,还不能直接使用。我们有两种方法分配空间:
1. 使用 allocate 语句,此时分配的字符串长度由分配时指定(稍后再详说)
2. 直接对其赋值,此时分配的字符串长度由等号右边决定(例如上例的 str = "Welcome" )
在上例中,我们的 str 就自动分配了 7 个长度,并填入 “Welcome”
之后执行 str = str // " to Fcode.cn" ,则编译器自动将其扩展为 19 个长度,并在后方填入内容,变为 “Welcome to Fcode.cn”。正因为它如此方便,所以称之为“递延长度”字符串,表明它的长度是可以延伸的。
递延长度字符串使用时,虽然可以直接赋值进行自动分配,省去 Allocate 语句,但不能省略 deallocate 语句,否则内存无法释放。
当递延字符串释放之后,我们就不能再访问它了,否则会出现内存违例。(如上例的最后一句话)
三. 使用 allocate 分配
我们可以通过直接赋值的方法自动分配递延长度字符串,但在某些情况下,我们还是需要先 allocate 分配。
例如,字符串一开始就需要读入。由于未分配,因此长度为 0,读入任何内容,都不会获得值。读者可测试以下代码:
Program www_fcode_cn Implicit None Character(len=:) , allocatable :: str read(*,*) str write(*,*) str , len(str) End Program www_fcode_cn读者会发现执行后,虽然我们输入了字符,但并没有被 str 获取,因为它的长度是 0
由于 read 语句并不知道会输入多少字符串,所以它并不能自动分配。此时,我们就需要在 read 语句前加入 allocate 语句,并且指定其长度,此处的长度可以使用变量。
Program www_fcode_cn Implicit None Character(len=:) , allocatable :: str integer :: i i = 3+3 allocate( character(len=i)::str )!//可用变量 read(*,*) str write(*,*) str , len(str) End Program www_fcode_cn在上例中,我们分配了6个长度,并通过read语句获取其值。我们会发现,如果输入的长度超过了6,会被截断。而如果不到6,剩余部分会被填充为空格。因此,read语句并不改变递延长度字符串的长度。
那么,是不是递延长度对于需要 read 读取的字符串就没啥意义了呢?当然不是,通常的解决方法是,先分配足够长的长度进行 read,然后再 trim 。
Program www_fcode_cn Implicit None Character(len=:) , allocatable :: str allocate( character(len=128)::str )!//可用变量 read(*,*) str write(*,*) str , len(str) str = trim(str) !// 之后与本文第一个例子就一样了 write(*,*) str , len(str) End Program www_fcode_cn在这个例子中,有一句 str = trim(str),这句话在常规的字符串里是没有意义的。因为常规字符串的长度是确定的,虽然等号右边做了trim,但左边的长度依然没有改变。
但是,在递延长度字符串里,这句话就有意义了。它表示,删除str尾部的空格,并缩短 str 的长度。
运行这个代码,可以看到,我们分配了128个长度,在 read 语句后,str 的长度是 128,并有若干空格。而经过 trim 以后,它的长度变为 14。(注意此处是 len(str) 而非 len_trim(str) )
四. 其他问题
很多朋友会觉得上面例子里的 allocate 比较长,书写比较麻烦。于是会有这种写法:
str = repeat(" ",128)
它表示让 str 的值为 128 个空格(也可以是其他内容),由于直接赋值隐含了 allocate 操作,所以与 allocate 语句是等效的。
此外,一些较陈旧的编译器(CVF等)可能尚不支持递延长度字符串。
也有一些编译器对 type 结构体内使用递延长度字符串支持得有问题(应该是bug)
如下例子:
Program www_fcode_cn Implicit None Type :: STS Character(len=:) , allocatable :: str End Type STS Type(STS) :: st allocate( character(len=6) :: st%str ) !allocate( st%str , source = "Welcome" ) !st%str = "Welcome" write(*,*) st%str , len(st%str) End Program www_fcode_cn如果您遇到第 7 行语句中的写法不被支持,那么可以用上例第8行或第9行的写法代替。