LoginSignup
0
0

More than 5 years have passed since last update.

Fortranのdeferred length characterのバージョン依存性

Posted at

Fortran2003から導入されたdeferred length character、つまり可変長文字列ですが、gccのバージョンでちょっと挙動が変わっていたので、まとめてみます。

比較したバージョン

  • GCC 4.8.4
    • AtCoderのコードテスト
  • GCC 4.8.5
    • CentOS 7
  • GCC 5.4.0
    • Ubuntu 16.04

基本

program main
   implicit none
   integer :: W
   character(len=:), allocatable :: S
   W = 10
   allocate(character(len=W) :: S)
   write(*, *) S
   write(*, *) "string len: ", len(S)
   S = "hoge"
   write(*, *) S
   write(*, *) "string len: ", len(S)
end program main

これを実行すると以下の出力を得ます。

 X�؉�X�
 string len:           10
 hoge
 string len:            4

可変長文字列なのでメモリの割当が必要となります。allocateを呼び出す場合は

allocate(character(len=W) :: S)

とします。ですがわざわざallocateしなくてもS = "hoge" とすると必要な分だけallocateされます。

可変長文字列の可変長配列

入力

AtCoderでよくある、1行目に$H$と$W$、続く$H$行で長さ$W$の文字列が入力される状況を考えます。

3 4
abcd
efgh
ijkl

GCC5.4.0

5.4.0では以下のようにして文字列を取得できます。

program main
   implicit none
   integer :: W, H, y
   character(len=:), allocatable :: S(:)
   read(*, *) H, W
   allocate(character(len=W) :: S(H))
   read(*, *) S
   do y = 1, H
   write(*, *) S(y)
   enddo
end program main

長さWの文字列のH個の配列を allocate(character(len=W) :: S(H))としておけば、
read(*, *) Sの一回でH行の文字列をすべて読み込めます。

ちなみに2行目3文字目のみを取り出すには

   write(*, *) S(2)(3:3)

とします。

GCC 4.8では・・・

次のように、ちゃんと配列を割り当てられたかどうかをチェックするコードを挟んで見ます。

program main
   implicit none
   integer :: W, H, y
   character(len=:), allocatable :: S(:)
   write(*, *) "Input height and width"
   read(*, *) H, W
   allocate(character(len=W) :: S(H))
   write(*, *) "Height (array size): ", size(S)
   write(*, *) "Width (string len): ", len(S(1))
   read(*, *) S
   do y = 1, H
       write(*, *) S(y)
   enddo
end program main

これを実行して、上で紹介した入力を与えてやると、

  • 4.8.4
 Input height and width
 Height (array size):            3
 Width (string len):            0



  • 4.8.5
 Input height and width
 Height (array size):            3
 Width (string len):      4196752
abcd (めちゃ長い空白)
以下略

全然ダメです。特に4.8.4はデータをもたせることすらできません。

回避策

Type Array(失敗)

インテルコンパイラーに妙に詳しい人が、Typeの中にDeferred length characterをメンバにもたせ、そのTypeの動的Arrayを使うと良いと提案していました。

   type str
      character(len=:), allocatable :: t
   end type str
   type(str), allocatable :: S(:)

これをGCC4.8.5でコンパイルすると、

type_array.f08:6.40:

      character(len=:), allocatable :: t
                                        1
エラー: Deferred-length character component 't' at (1) is not yet supported

とのことです。使えなかった。それにこの方法は

S = "hogehoge"

といった代入もできないので、できたらやりたくないところ。

固定長文字列配列

もうdeferred length characterは諦め、従来通りの長めに取った固定長文字列を使います。

program main
   implicit none
   integer :: W, H, y
   character(len=100), allocatable :: S(:)
   write(*, *) "Input height and width"
   read(*, *) H, W
   allocate(S(H))
   write(*, *) "Height (array size): ", size(S)
   write(*, *) "Width (string len): ", len(S(1))
   read(*, *) S
   do y = 1, H
      write(*, *) trim(S(y))
   enddo
end program main        

これを上の入力で実行すると、出力は

 Input height and width
 Height (array size):            3
 Width (string len):          100
 abcd
 efgh
 ijkl

と、固定長の長さが確認できます。
また、read(*, *) Sの1回で、入力の各行がSの各成分に代入されました。
S(1)にすべて押し込まれるといったことがなくて安心。

ちなみに

可変長文字列の可変長配列ですが、代入してしまう場合の注意です。

   character(len=:), allocatable :: S(:)
   S = ["hoge", "fugafuga"]

これはエラーです。

    S = ["hoge", "fugafuga"]
               1
Error: Different CHARACTER lengths (4/8) in array constructor at (1)

以下のようにスペースを入れて水増しします。

   S = ["hoge    ", "fugafuga"]
   write(*, *) S
   write(*, *) "Height (array size): ", size(S)
   write(*, *) "Width (string len): ", len(S(1))

これの出力は以下のようになります。

 hoge    fugafuga
 Height (array size):            2
 Width (string len):            8

おわりに

Fortranの文字列操作は、Cに比べるとかなりやりやすい方ですが、現代的な言語に比べるとやはり見劣りしますね。

0
0
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0