Fortran/文字列操作
モダンなFortranには、文字列やテキストデータを扱うためのさまざまな機能がありますが、これらの言語定義の機能のいくつかはコンパイラ開発者によって広く実装されていません。Fortranは科学計算用に設計されているため、新しいワードプロセッサを書くのには適していないことを覚えておく必要があります。
文字型
[編集]文字列をサポートするFortranの主要な機能は、組み込みデータ型character
です。文字定数はシングルクォートまたはダブルクォートで囲まれることができ、必要に応じて連続した2つのシングルクォートまたはダブルクォートを使用してエスケープすることができます。文字列の連結演算子は//
です(ただし、異なるKINDの文字エンティティを連結することはできません)。文字スカラー変数や配列が許可されており、文字変数にはサブ文字列を参照して抽出するためのサブ文字列表記があります。
例:
program string_1 implicit none ! 宣言 character (len=6) :: word1 character (len=2) :: word2 word1 = "abcdef" ! 代入 word2 = word1(5:6) ! サブストリング word1 = 'Don''t ' ! ダブルクォートでエスケープ write (*,*) word2//word1 ! 連結 end program string_1
上記の例では、2つのcharacter
変数word1
とword2
がそれぞれ長さ6と2の文字列として宣言されています。
character
の代入操作では、代入文の右辺が左辺よりも短い場合、左辺の残りの文字は空白で埋められます。右辺が左辺よりも長い場合、右辺は切り捨てられます。どちらの場合も、コンパイラや実行時にエラーが発生することはありません。
character
配列や共配列も許可されており、他のFortran配列と同じように宣言およびアクセスすることができます。配列のインデックスとサブ文字列表記を組み合わせる場合、配列のインデックスが最初に現れ、サブ文字列式が2番目に現れます。次の例の最後の行に示されているように。
character (len=120), dimension (10) :: text text(1) = 'This is the first element of the array "text"' text(2:3) = ' ' ! Elements 2 and 3 are blank. text(4)(20:20) = '!' ! Character 20 of element 4.
いくつかのプログラミング言語とは異なり、Fortranのcharacter
データと変数には文字列を終了する明示的な文字が必要ありません。また、C型言語とは異なり、Fortranのcharacter
データは埋め込みおよびエスケープされた制御文字(たとえば/n)を収容せず、出力制御のすべての処理は幅広いformat
サブシステムを介して行われます。
文字のコレーティングシーケンス
[編集]内部的に、Fortranは許可されているすべての文字のコレーティングシーケンスを維持します。非表示文字をコレーティングシーケンスに含めることができます。コレーティングシーケンスは言語規格で指定されていませんが、ほとんどのベンダーはASCIIまたはEBCDICをサポートしています。このコレーティングシーケンスにより、例えば'a'<'b'
などのレクシカル比較が行われ、文字'a'が文字'b'より前にあるかどうかが判断されますが、結果は基本的にベンダー固有です。したがって、ichar
やiachar
などの関数には以下で説明されているような違いがあります。
文字の種類
[編集]character
にはkind
もありますが、これはベンダー固有です。コンパイラがunicodeやロシア語アルファベット、日本語の文字などをサポートできるようにします。文字変数の長さや種類を指定する必要はありません。文字変数が長さや種類の指定なしで宣言された場合、結果はデフォルトの種類で1文字の変数になります。長さを示すためには単一の数字を使用し、長さと種類を示す場合はその順序で2つの数字を使用します。明示的に示すことがより明確ですが、少し冗長になります。次の例の6-8行に示されているように。コンパイラベンダーは、サポートされる文字の種類と、対応する文字セットへのアクセスに割り当てられる整数値を制御します。
program string_2 implicit none character :: one character (5) :: english_name character (5,2) :: japanese_name character (len=80) :: line character (len=120, kind=3) :: unicode_line character (kind=4, len=256) :: ebcdic_string !... end program string_2
組み込み関数selected_char_kind(name)
は、指定された名前の文字セットの正の整数の種類値を返します(例: デフォルト、ascii、kanji、iso_10646など)、しかし、サポートされる必要がある唯一のセットはdefault
であり、名前がサポートされていない場合は-1が返されます。残念ながら、ベンダーは一般にデフォルトの種類以上を実装するのに時間がかかることがありますが、例外的にgfortranなどがあります。
言語定義の組み込み関数とサブプログラム
[編集]Fortranには文字列操作、検索、変換をサポートする比較的限られた組み込み関数があります。ただし、必要に応じていくつかの強力な機能を構築するのに十分な基本的なセットがあります。小文字を大文字に変換する能力など、いくつかの奇妙な不足がありますが、これはこれらの概念が異なるcharacter
の種類で表される可能性がある多くの言語や文字セットに存在しないかもしれないため理解され、許容されます。配列のサイズを取得するsize
、下限を取得するlbound
、上限を取得するubound
など、すべてのデータ型の配列に適用される関数はここでは説明されていません。
achar
[編集]achar(i, kind)
は、指定された種類の文字のASCIIコレーティングシーケンスでi番目の文字を返します。整数i
は0 < i < 127の範囲内でなければなりません。Kindはオプションの整数です。Kindが指定されていない場合、デフォルトの種類が想定されます。 achar(72)
は値'H'を持ちます。 achar
の非常に便利な機能の1つは、改行(achar(13)
)などの非表示のASCII文字へのアクセスを許可することです。 achar
は常にASCII文字を返しますが、プロセッサのコレーティングシーケンスがASCIIでない場合でもです。Kindが存在する場合、結果の種類パラメータはkindで指定されたものになります。そうでない場合、結果の種類パラメータはデフォルトの文字の種類です。プロセッサが結果の値を結果の種類の種類で表現できない場合、結果は未定義です。achar
を使用することは、後述のchar
よりもポータブルであるため、強く推奨されます。
adjustl
[編集]adjustl(string)
は、stringから先頭の(左側の)空白を削除し、stringの右側に空白を追加して、結果が入力文字列と同じ長さになるように左揃えします。
adjustr
[編集]adjustr(string)
は、stringから末尾(右側)の空白を削除し、stringの左側に空白を追加して、結果が入力文字列と同じ長さになるように右揃えします。
char
[編集]char(i, kind)
は、指定された種類の文字のプロセッサコレーティングシーケンスでi番目の文字を返します。整数i
は0 < i < 127の範囲内である必要はありません。Kindはオプションの整数です。Kindが指定されていない場合、デフォルトの種類が想定されます。プロセッサが結果の値を結果の種類の種類で表現できない場合、結果は未定義です。
iachar
[編集]iachar(c, kind)
は、上記で説明したachar
の逆です。cは単一の入力文字で、iachar(c)
はデフォルトの整数としてASCII文字セット内のcの位置を返します。Kindはオプションの入力整数であり、kindが指定されている場合、iachar
によって返される整数の種類が指定されます。
ichar
[編集]ichar(c, kind)
は、上記で説明したCHARの逆です。cは単一の入力文字で、ichar(c)
は選択された文字セット内のcの位置をデフォルトの整数として返します。Kindはオプションの入力整数であり、kindが指定されている場合、ichar
によって返される整数の種類が指定されます。
index
[編集]index(string, substring)
は、string内のsubstringの最初のインスタンスの位置を表すデフォルトの整数を返します。左から右に検索します。バックとkindの2つのオプション引数があります。論理的なbackがtrueに設定されている場合、検索は右から左に行われ、整数kindが指定されている場合、index
によって返される整数はその種類になります。substringがstringに現れない場合、結果は0になります。
len
[編集]len(c, kind)
は、文字cの宣言された長さを表す整数を返します。これは、文字ダミー引数を受け取るサブプログラムで非常に役立ちます。 c
は文字配列にすることができます。Kindはオプションの整数であり、len
によって返される整数の種類を制御します。
len_trim
[編集]len_trim(c, kind)
は、cの末尾の空白を除いた長さを返します(先頭の空白は含まれます)。cがすべての空白の場合、結果は0になります。したがって、len_trim(adjustl(c))
のような式は、cの先頭と最後の非空白文字の間の文字数を数えるために使用できます。Kindはオプションの整数であり、len_trim
によって返される整数の種類を制御します。
new_line
[編集]new_line(c)
は、現在のプロセッサの改行文字を返す文字関数です。返される文字の種類はc
の種類と同じになります。関連する改行文字が含まれていない場合、空白文字が返される場合があります。この関数は、非常に特定の状況でのみ使用される可能性があります。
repeat
[編集]repeat(string, ncopies)
は、文字列の整数ncopiesを連結します。したがって、repeat('=',72)
は72個の等号の文字列です。Stringはスカラーでなければなりませんが、任意の長さにすることができます。文字列の末尾の空白が結果に含まれます。
scan
[編集]scan(string, set, back, kind)
は、文字列内のセットのいずれかの文字が最初に現れる位置を表すデフォルトの整数(またはオプションの種類の整数)を返します。右から左に検索するには、オプションの論理バックをtrueに設定する必要があります。stringは配列であり、その場合、結果は整数配列です。stringが配列の場合、setはstringと同じサイズおよび形状の配列であることができ、setの各要素がstringの対応する要素で検索されます。上記で説明したindex
は、セット内のすべての文字がセット内の文字の順序で見つかり、セット内の文字の順序で見つかるため、scan
の特別な場合です。
selected_char_kind
[編集]selected_char_kind(name)
は、指定された名前の文字セットの種類値を返す整数関数です。言語標準でサポートされているセットはname='DEFAULT'
だけです。名前がサポートされていない場合、結果は-1になります。
trim
[編集]trim(string)
は、末尾の空白が除去された文字列を返す文字型の関数です。stringがすべて空白の場合、結果の長さはゼロになります。
verify
[編集]verify(string, set, back, kind)
は、文字列内のセットに含まれない最初の文字の位置を表す整数関数です。したがって、verify
はscan
の逆の動作です。 verify
では、backとkindの両方がオプションであり、scan
で説明したのと同じ役割を果たします。文字列のすべての文字がセットにも含まれている場合、結果は0になります。stringが配列の場合、setも配列である必要があります。
正規表現
[編集]Fortranには文字データ用の言語定義の正規表現やソート機能はありません。Fortranには言語定義のテキストトークナイザーもありませんが、少しの工夫で、リスト指向の入力が部分的な解決策を提供できます。ただし、Cの正規表現ライブラリをラップしたFortranのライブラリがあります。
文字データの入出力
[編集]書式付き読み込み
[編集]文字データのread
はリスト指向または "a" または "an" の形式の編集記述子を使用してフォーマットすることができます。 "a" 形式では、幅はリストの対応する項目の幅から取得されます。 "an" 形式では、整数nが転送する文字数を指定します。一般的な編集記述子 "gn" も使用できます。
- 例
character (120) :: line open (10,"test.dat") read (10,'(a)') line ! 120文字までの行をlineに読み込む read (10,'(a5)') line(115:) ! 5文字を読み込んで、lineの末尾に配置する
書式付き書き込み
[編集]write
用のaおよびg編集記述子は上記のように説明されています。 "a" 形式では、末尾のすべての空白を含む文字列全体が書き込まれるため、trim
またはadjustl
またはその両方を使用するのが一般的です。
- 例
character (len=512) :: line !... write (10,'(a)') trim(adjustl(line))
内部読み込みおよび書き込み
[編集]Fortranには多くの隠れた秘密があり、そのうちの1つは、文字変数をファイルのように扱い、read
およびwrite
ステートメントを使用できるということです。したがって、数値を文字列に変換するための関数などがないことも説明できます。文字変数は '内部ファイル' として扱われます。
- 例
character (120) :: text_in, text_out integer :: i real :: x !... write (text_in,'(A,I0)') 'i = ', i ! フォーマット指定 !... read (text_out,*) x ! リスト指向
この内部読み取り/書き込みは型変換に加えて、内容が曖昧な形式のファイルを読み取るための非常に柔軟で確実な方法として使用できます。外部ファイルは文字列変数に行ごとに読み込まれ、行にscan
およびverify
を使用して存在する内容を判断し、次に文字列変数に対して内部ファイル読み込みが行われ、適切なreal
、integer
、complex
などに変換されます。
最近の拡張
[編集]character(:), allocatable
[編集]スカラー文字データのサイズを遅延(または "allocatable" )にすることができ、したがって特定の長さで宣言する必要がなくなります。結果として得られるスカラーは、明示的に割り当てることも、次の例に示すように自動的に割り当てることもできます。
- 例
character (:), allocatable :: string !... string = 'abcdef' !... string = '1234567890' !... string = trim(line) !...
以下のように、仮定される長さの要素の配列を宣言することもできます。
- 例
character (:), dimension (:), allocatable :: strings
ただし、この機能は慎重に使用する必要があり、いくつかの制限が適用されます。
実引数/仮引数の文字のタイプ
[編集]ロシージャが、その引数の長さが事前にわからない文字ダミー引数を持つ場合がよくあります。Modern Fortranでは、ダミー引数をlen=*
を使用して仮定された長さで宣言することができます。文字タイプの関数は、結果がダミー引数の長さに関連付けられる長さを仮定するように書くことができます。
- 例
call this('Hello') call this('Goodbye') !... subroutine this(string) implicit none character (len=*), intent (in) :: string character (len=len(string)+5) :: temp !... end subroutine
上記の例では、character
変数temp
は、実際の引数の長さに関係なく、5つの文字よりも長く宣言されています。次の例では、文字列を返す関数があり、この文字列の長さは、1つまたは複数の引数の長さに関連付けられています。
- 例
string = that('thing', 7) !... function that(in_string, n) result (out_string) implicit none character (len=*), intent (in) :: in_string integer, intent(in) :: n character (len=len(in_string)*n) :: out_string !... end function
文字列関数が文字列を返す必要があり、その文字列の長さが単純に入力と関連していない場合、上記のように、仮定された長さ、割り当て可能な形式を使用することができます。
文字パラメータ
[編集]character
パラメータは、明示的に長さを指定せずに宣言することができます。
character (*), parameter :: place = 'COEFF_LIST_initialise'
大文字・小文字変換のアプローチ
[編集]以上のアイデアのさらなる例を以下に示しますが、大文字・小文字変換の場合に適用されます。ASCII文字セットの関数iachar
およびachar
を使用して、文字列内の各文字を順番に確認します。
- 例
function up_case(in) result (out) implicit none character (*), intent (in) :: in character (:), allocatable :: out integer :: i, j out = in ! 全配列を転送 do i = 1, LEN_TRIM(out) ! 各文字 j = iachar(out(i:i)) ! ASCII位置を取得 select case (j) case (97:122) ! 小文字の文字 out(i:i) = ACHAR(j-32) ! 大文字にオフセット end select end do end function up_case
ASCII表現関数に依存しない代替手法は次のようになります。
- 例
function to_upper(in) result (out) implicit none character (*), intent (in) :: in character (:), allocatable :: out integer :: i, j character (*), parameter :: upp = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' character (*), parameter :: low = 'abcdefghijklmnopqrstuvwxyz' out = in ! すべての文字を転送 do i = 1, len_trim(out) ! すべての非ブランク j = index(low, out(i:i)) ! ith文字はlowにあるか if (j>0) out(i:i) = upp(j:j) ! はい、それからuppに置き換える end do end function to_upper
どのルーチンが速いかは、index
およびiachar
のインストラクションの相対速度に依存します。ほとんど非常に科学的なテスト下では、上記の最初の方法が2番目の方法よりもわずかに2倍以上速く見えましたが、これはベンダーによって異なります。