LoginSignup
11
10

More than 3 years have passed since last update.

Fortranで整数を文字列に変換する

Last updated at Posted at 2020-02-19

概要

1234などの整数を,文字列"001234"に変換する方法をまとめました.

ネットでよく見る既存の方法では,文字列の長さをあらかじめ決めておく必要があります.また,そのようにすると,文字列の長さが整数の桁数より大きい場合に空白が詰められるので,それを文字列操作関数で消去する必要があります.

一方で,数値計算では,計算結果を連番result0001.dat, result0002.dat等として,0埋めして出力することもよくあります.

そこで,整数の桁数に依存せず,0埋めにも対応した変換方法を作る事にしました.

変換の手順

変換は以下の手順で行います.少し手間ですが,何故このような手順になっているかは,以降の節で説明します.

  1. 整数numの桁数を取得する
  2. 1.で取得した「整数numの桁数」の桁数を取得する
  3. 「整数numの桁数」を文字列に変換する
  4. 3.で変換した文字列を用いて,write文の書式を作成する
  5. 整数numを文字列に変換する

前提知識

内部ファイル

Fortranには,整数(あるいは実数)と文字列の相互変換を行う関数は存在していませんが,内部ファイル,特に内部write文とよばれる機能を利用すると,簡単に変換できます.

Fortranのwrite文は,装置番号と書式を指定し,変数あるいはリテラルを指定書式で装置へ出力します.

write(装置番号,書式) 変数あるいはリテラル

装置番号という呼称は,おそらく過去の状況を引き継いでいるだけで,実際にはファイル等と装置番号を対応付けて利用します.この装置番号に文字列変数を与えることで,整数(あるいは実数)が文字列に変換されます.このような変数の使い方を内部ファイルとよび,変数へデータを出力するwrite文を内部write文とよびます.

program main
    implicit none

    character(3) :: str

    write(str,"(I3)") 456
    print *,str ! 456
end program main

このことから,write文を画面/ファイル出力機能と捉えるより,データの移動を行う機能と捉えた方が理解しやすいように思います.

書式

0埋めをするには,write文の書式指定において,書式指定子I,桁数に続いて,0埋めする桁数を.で結合します.

program main
    implicit none

    character(5) :: str

    write(str,"(I5.4)") 456 !456を5桁で表示し,1~4桁まで(当該桁に数がなければ)0埋めする
    print *,str ! ␣0456
end program main

このとき,書式を指定する桁数に変数を用いることはできません.書式は文字列で指定するので,桁数を文字列として文字列変数に代入しておけば,桁数を実行時に制御できます.

program main
    implicit none

    character(5) :: str
    character(:),allocatable :: fmt

    fmt = "(I5.4)"     ! 書式 5桁で表示し,1~4桁まで(当該桁に数がなければ)0埋めする
    write(str,fmt) 456
    print *,str        ! ␣0456

    fmt(5:5) = "5"     ! 書式の5文字目を5に置き換える "(I5.4)" →"(I5.5)"
    write(str,fmt) 456
    print *,str        ! 00456
end program main

自動再割付文字列

Fortranの文字列変数は,allocatable属性を付与することで,文字列リテラルあるいは変数が代入されたときに,文字列の長さに応じて自動で割り付けられるようになります.

program main
    implicit none

    character(:),allocatable :: str

    print *,len(str)       ! 0  ※gfortranは32767
    str = "dynamic string"
    print *, str, len(str) ! dynamic string          14

    str = "string is not an array of characters"
    print *, str, len(str) ! string is not an array of characters          36
end program main

allocatableな文字列が自動で(再)割付されるのであれば,内部write文に自動再割付文字列を与えれば,整数の桁数に応じて自動で割り付けられるように思うでしょうが,そうは問屋が卸しません.

再自動割付文字列が自動で再割付されるのは,変数あるいはリテラルが=で代入されるときのみです.

program main
    implicit none

    character(:),allocatable :: str

    write(str,"(I3)") 456 ! 実行時エラー
    print *,str
end program main

そのため,内部write文に用いる文字列変数は,事前に整数の桁数を取得して,その桁数に応じた長さで割り付けておく必要があります.

整数から文字列への変換手法

上述の手順に沿って,整数を受取り,それを文字列に変換し,その文字列を返す関数int32_to_string()を作成します.引数は4バイト整数,戻り値は文字列とします.戻り値の文字列の長さは,整数の桁数に応じて動的に変化するようにします.

pure function int32_to_string(num) result(str)
    use, intrinsic :: iso_fortran_env
    implicit none

    integer(int32),intent(in) :: num
    character(:),allocatable :: str  ! retval

end function int32_to_string

整数numの桁数を取得する

まず,引数として与えられる整数の桁数を取得します.Fortranにはそういう関数がないようなので,作成します.十進整数の桁数を取得するには,log10を計算して小数点以下を打ち切り,+1するだけです.

pure function get_digits_of(num) result(num_digit)
    implicit none
    integer(int32),intent(in) :: num
    integer(int32) :: num_digit

    num_digit = int(log10(dble(num))) + 1
end function get_digits_of

作成した関数を使い,引数numの桁数を得ます.Extra_Digitsは追加する桁です.result01000.txtなどのように,連番の長さを制御するために用いるだけなので,必須ではありません.

    integer(int32),parameter :: Extra_Digits = 2 ! 追加する桁
    integer(int32) :: num_digits                 ! 整数numの桁数

    num_digits = get_digits_of(num) + Extra_Digits

整数numの桁数の桁数を取得する

整数numを文字列に変換する書式文字列fmtを作る前準備として,「numの桁数」の桁数を取得します.
例えば,整数2147483647を文字列"002147483647"として出力するには,書式として"(I12.12)"という文字列が必要です.書式の12を作るために,整数の12から2桁の文字列"12"を作る必要があります.

8バイト整数でも最長20桁なので,わざわざ桁数の桁数を取得しなくても,2と決め打ちしておけば問題ないように思います.しかし,将来的に100桁の整数を取り扱う可能性がないわけではないので(そして,Fortranはその頃まで生き残っていそうなので),決め打ちはしないようにしました.

    integer(int32) :: dgt_digits  ! 整数numの桁数num_digitsの桁数

    dgt_digits = get_digits_of(num_digits)

整数の桁数を文字列に変換する

整数numの桁数を文字列に変換します.このとき,書式をI0としておけば,自動的にnumの桁数を認識してくれます.
内部write文に用いる文字列変数は,あらかじめ割り付けておく必要があります.

    character(:),allocatable :: str_digits

    allocate(character(dgt_digits)::str_digits)
    write(str_digits,'(I0)') num_digits

書式を作成する

整数numの桁数の文字列を連結して,書式を作ります.これは代入演算ができるので,書式用の文字列fmtallocateしておく必要はありません.

    character(:),allocatable :: fmt

    fmt = "(I"//str_digits//"."//str_digits//")"

整数numを文字列に変換する

ようやく準備ができたので,内部write文で整数を文字列に変換します.

    allocate(character(num_digits)::str)
    write(str,fmt) num

実行結果

作成した関数に4バイト整数の最大値を渡し,2桁追加して文字列に変換したところ,正しく実行されました.

print *, int32_to_string(huge(0_int32)) ! 002147483647

関数全景

pure function int32_to_string(num) result(str)
    use, intrinsic :: iso_fortran_env
    implicit none

    integer(int32),intent(in) :: num
    character(:),allocatable :: str  ! retval

    integer(int32),parameter :: Extra_Digits = 2 ! 追加する桁数
    integer(int32) :: num_digits                 ! 整数numの桁数
    integer(int32) :: dgt_digits                 ! 整数numの桁数num_digitsの桁数
    character(:),allocatable :: str_digits
    character(:),allocatable :: fmt

    num_digits = get_digits_of(num) + Extra_Digits ! 整数numの桁数を取得
    dgt_digits = get_digits_of(num_digits)         ! 整数numの桁数num_digitsの桁数を取得

    ! 整数の桁数を文字列に変換
    allocate(character(dgt_digits)::str_digits)
    write(str_digits,'(I0)') num_digits

    ! 書式を作成
    fmt = "(I"//str_digits//"."//str_digits//")"

    ! 整数numを文字列に変換
    allocate(character(num_digits)::str)
    write(str,fmt) num
end function int32_to_string

pure function get_digits_of(num) result(num_digit)
    implicit none
    integer(int32),intent(in) :: num
    integer(int32) :: num_digit

    num_digit = int(log10(dble(num)))+1
end function get_digits_of

まとめ

内部write文を使わない方が楽かもしれません.
Fortran 202Xでは,内部write文でも(代入以外でも)自動で割り付けるようになってほしいですね.

11
10
5

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
11
10