Fortranの標準ライブラリstdlibの紹介（文字・文字列操作）

# 概要
[Fortran-lang](https://fortran-lang.org)コミュニティが主導して開発しているFortraの標準ライブラリ[stdlib](https://github.com/fortran-lang/stdlib)について紹介します．紹介する内容は，stdlibの現行バージョン0.1.0に基づいています．

本記事では，stdlibに実装されている文字・文字列操作や定数について説明します．

- [stdlib_ascii](#stdlib_ascii)
- [stdlib_strings](#stdlib_strings)

# stdlib_ascii
`stdlib_ascii`モジュールでは，文字の定数，一文字に対する検査用の手続，文字列を変換する手続が実装されています．

## 定数
下記の定数が定義されています．

- ASCIIの各制御文字
    - `NUL`: Null
    - `SOH`: ヘッディング開始
    - `STX`: テキスト開始
    - `ETX`: テキスト終端
    - `EOT`: 伝送終了
    - `ENQ`: 問い合わせ
    - `ACK`: 肯定応答
    - `BEL`: ベル
    - `BS`: 後退
    - `TAB`: 水平タブ
    - `LF`: 改行
    - `VT`: 垂直タブ
    - `FF`: 書式送り
    - `CR`: 復帰
    - `SO`: シフトアウト
    - `SI`: シフトイン
    - `DLE`: 伝送制御拡張
    - `DC1`: 装置制御1
    - `DC2`: 装置制御2
    - `DC3`: 装置制御3
    - `DC4`: 装置制御4
    - `NAK`: 否定応答
    - `SYN`: 同期信号
    - `ETB`: 伝送ブロック終結
    - `CAN`: 取り消し
    - `EM`: 媒体終結
    - `SUB`: 置換
    - `ESC`: 拡張
    - `FS`: ファイル分離
    - `GS`: グループ分離
    - `RS`: レコード分離
    - `US`: ユニット分離
    - `DEL`: 抹消
- `digits`: 数字<br>`"0123456789"`
- `octal_digits`: 8進数に用いられる数字<br>`"01234567"`
- `fullhex_digit`, `hex_digits`, `lowerhex_digits`: 16進数に用いられる数字<br>`"0123456789ABCDEFabcdef"`, `"0123456789ABCDEF"`, `"0123456789abcdef"`
- `letters`: アルファベット<br>`"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"`
- `uppercase`, `lowercase`: アルファベットの大文字，小文字<br> `"ABCDEFGHIJKLMNOPQRSTUVWXYZ"`, `"abcdefghijklmnopqrstuvwxyz"`
- `whitespace`: ASCIIの空白<br>`" "`, `TAB`, `VT`, `CR`, `LF`, `FF`

## 文字の検査用手続
文字がアルファベットか等を検査する手続がいくつか定義されています．

- `is_alpha`<br>文字がアルファベットであれば`.true.`，そうでなければ`.false.`を返す．
- `is_alphanum`<br>文字がアルファベットか数字であれば`.true.`，そうでなければ`.false.`を返す．
- `is_digit`<br>文字が数字であれば`.true.`，そうでなければ`.false.`を返す．
- `is_hex_digit`<br>文字が16進数に使う数字・文字であれば`.true.`，そうでなければ`.false.`を返す．
- `is_octal_digit`<br>文字が8進数に使う数字であれば`.true.`，そうでなければ`.false.`を返す．
- `is_control`<br>文字がASCIIの制御文字であれば`.true.`，そうでなければ`.false.`を返す．
- `is_white`<br>文字がホワイトスペースに類する文字（スペース，水平タブ，垂直タブ， 書式送り，復帰，改行）であれば`.true.`，そうでなければ`.false.`を返す．
- `is_blank`<br>文字がブランク文字（スペース，水平タブ）であれば`.true.`，そうでなければ`.false.`を返す．
- `is_ascii`<br>文字がアスキー文字（文字コード0x00から0x7F）であれば`.true.`，そうでなければ`.false.`を返す．
- `is_punctuation`<br>文字が句読文字であれば`.true.`，そうでなければ`.false.`を返す．
- `is_graphical`<br>文字が空白以外の印字可能文字であれば`.true.`，そうでなければ`.false.`を返す．
- `is_printable`<br>文字が空白も含む印字可能文字であれば`.true.`，そうでなければ`.false.`を返す．
- `is_lower`<br>文字が小文字アルファベットであれば`.true.`，そうでなければ`.false.`を返す．
- `is_upper`<br>文字が大文字アルファベットであれば`.true.`，そうでなければ`.false.`を返す．

### 使用例

#### is_alphanum
```Fortran
! 文字がアルファベットもしくは数字かを判別する．
print *, "A is alphabet or number :", is_alphanum("A") ! T
print *, "a is alphabet or number :", is_alphanum("a") ! T
print *, "1 is alphabet or number :", is_alphanum("1") ! T
print *, "# is alphabet or number :", is_alphanum("#") ! F
print *, "á is alphabet or number :", is_alphanum("á") ! F
```
#### is_alpha
```Fortran
! 文字がアルファベットかを判別する．
print *, "A is alphabet :", is_alpha("A") ! T
print *, "A is alphabet :", is_alpha("a") ! T
print *, "1 is alphabet :", is_alpha("1") ! F
print *, "# is alphabet :", is_alpha("#") ! F
print *, "á is alphabet :", is_alpha("á") ! F
```

#### is_lower
```Fortran
! 文字が小文字アルファベットかを判別する．
print *, "A is lowercase alphabet :", is_lower("A") ! F
print *, "a is lowercase alphabet :", is_lower("a") ! T
print *, "1 is lowercase alphabet :", is_lower("1") ! F
print *, "# is lowercase alphabet :", is_lower("#") ! F
print *, "á is lowercase alphabet :", is_lower("á") ! F
```
#### is_upper
```Fortran
! 文字が大文字アルファベットかを判別する．
print *, "A is uppercase alphabet :", is_upper("A") ! T
print *, "a is uppercase alphabet :", is_upper("a") ! F
print *, "1 is uppercase alphabet :", is_upper("1") ! F
print *, "# is uppercase alphabet :", is_upper("#") ! F
print *, "á is uppercase alphabet :", is_upper("á") ! F
```
#### is_digit
```Fortran
! 文字が数字かを判別する．
print *, "A is digit :", is_digit("A") ! F
print *, "a is digit :", is_digit("a") ! F
print *, "1 is digit :", is_digit("1") ! T
```
#### is_octal_digit
```Fortran
! 文字が8進数で使われる数字かを判別する．
print *, "A is octal digit :", is_octal_digit("A") ! F
print *, "a is octal digit :", is_octal_digit("a") ! F
print *, "7 is octal digit :", is_octal_digit("7") ! T
print *, "8 is octal digit :", is_octal_digit("8") ! F
```
#### is_hex_digit
```Fortran
! 文字が16進数で使われる数字・文字かを判別する．
print *, "A is hex digit :", is_hex_digit("A") ! T
print *, "a is hex digit :", is_hex_digit("a") ! T
print *, "F is hex digit :", is_hex_digit("F") ! T
print *, "G is hex digit :", is_hex_digit("G") ! F
print *, "8 is hex digit :", is_hex_digit("8") ! T
```
#### is_white
```Fortran
! 文字がホワイトスペースかを判別する．
! スペース，水平タブ，垂直タブ， 書式送り，復帰，改行のとき真
print *, "' ' is white space :", is_white(" ") ! T
print *, "NUL is white space :", is_white(NUL) ! F
print *, "TAB is white space :", is_white(TAB) ! T
print *, "LF is white space :", is_white(LF) ! T
print *, "VT is white space :", is_white(VT) ! T
print *, "FF is white space :", is_white(FF) ! T
print *, "CR is white space :", is_white(CR) ! T
print *, "A is white space :", is_white("A") ! F
```
#### is_blank
```Fortran
! 文字がブランク文字かを判別する．
! スペース，水平タブとき真．
print *, "' ' is blank :", is_blank(" ") ! T
print *, "NUL is blank :", is_blank(NUL) ! F
print *, "TAB is blank :", is_blank(TAB) ! T
print *, "LF is blank :", is_blank(LF) ! F
print *, "VT is blank :", is_blank(VT) ! F
print *, "FF is blank :", is_blank(FF) ! F
print *, "CR is blank :", is_blank(CR) ! F
print *, "A is blank :", is_blank("A") ! F
```
#### is_control
```Fortran
! 文字が制御文字かを判別する．
print *, "' ' is control character :", is_control(" ") ! F
print *, "NUL is control character :", is_control(NUL) ! T
print *, "TAB is control character :", is_control(TAB) ! T
print *, "LF is control character :", is_control(LF) ! T
print *, "VT is control character :", is_control(VT) ! T
print *, "FF is control character :", is_control(FF) ! T
print *, "CR is control character :", is_control(CR) ! T
print *, "DEL is control character :", is_control(DEL) ! T
print *, "A is control character :", is_control("A") ! F
```
#### is_punctuation
```Fortran
! 文字が句読文字かを判別する．
print *, ", is punctuation character :", is_punctuation(",") ! T
print *, ". is punctuation character :", is_punctuation(".") ! T
print *, ": is punctuation character :", is_punctuation(":") ! T
print *, "; is punctuation character :", is_punctuation(";") ! T
print *, "! is punctuation character :", is_punctuation("!") ! T
print *, "# is punctuation character :", is_punctuation("#") ! T
print *, "~ is punctuation character :", is_punctuation("~") ! T
print *, "+ is punctuation character :", is_punctuation("+") ! T
print *, "_ is punctuation character :", is_punctuation("_") ! T
print *, "' ' is punctuation character :", is_punctuation(" ") ! F
print *, "A is punctuation character :", is_punctuation("A") ! F
print *, "1 is punctuation character :", is_punctuation("1") ! F
print *, "TAB is punctuation character :", is_punctuation(TAB) ! F
```

## 文字列の変換手続
文字列の大文字，小文字を変換する手続，反転する手続が定義されています．

- `to_lower`<br>大文字アルファベットを小文字に変換する．
- `to_upper`<br>小文字アルファベットを大文字に変換する．
- `to_title`<br>各英単語の最初の一文字を大文字にする．単語が引用符などで囲まれている場合でも，最初の単語を大文字にする．最初が数字の場合は何もしない．
- `to_sentence`<br>文字列の最初の一文字を大文字にする．
- `reverse`<br>文字列を反転する．

### 使用例
#### to_upper, to_lower
```Fortran
use :: stdlib_ascii

! 大文字アルファベットを小文字に変換する．
print *, uppercase
print *, to_lower(uppercase)
! ABCDEFGHIJKLMNOPQRSTUVWXYZ
! abcdefghijklmnopqrstuvwxyz

! 小文字アルファベットを大文字に変換する．
print *, lowercase
print *, to_upper(lowercase)
! abcdefghijklmnopqrstuvwxyz
! ABCDEFGHIJKLMNOPQRSTUVWXYZ
```

#### to_title, to_sentence, reverse
```Fortran
use :: stdlib_ascii

character(*), parameter :: str = "hello there. this is 'enquated string'. 19th century writer"

! 各英単語の最初の一文字を大文字にする．
print *, to_title(str)
! Hello There. This Is 'Enquated String'. 19th Century Writer

! 文字列の最初の一文字を大文字にする．
print *, to_sentence(str)
! Hello there. this is 'enquated string'. 19th century writer

! 文字列を反転する．
print *, reverse(str)
! retirw yrutnec ht91 .'gnirts detauqne' si siht .ereht olleh
```

# stdlib_strings
`stdlib_strings`モジュールでは，文字列を操作する手続が実装されています．

## 文字列の操作手続
- `strip`<br>文字列前後の空白を削除する．
- `chomp`<br>文字列の末尾から指定した文字を削除する．文字を指定しない場合は空白が削除される．
- `starts_with`<br>文字列が部分文字列から始まっているかを判別．
- `ends_with`<br>文字列が部分文字列で終わっているかを判別．
- `slice`<br>文字列を取り出す．
- `find`<br>文字列内に部分文字列パターンが含まれている場合にその開始位置を返す．
- `replace_all`<br>文字列から部分文字列を別の文字列に置き換える．
- `padl`<br>指定の文字数になるように左側に文字を詰める．
- `padr`<br>指定の文字数になるように右側に文字を詰める．
- `count`<br>文字列に含まれる部分文字列の数を数える．
- `to_string`<br>整数，実数，複素数，論理値を文字に変換する．

### 使用例
#### strip
```Fortran
print '(A)', "|"//strip("   hello   ")//"|"
! |hello|

! whitespaceは全て削除する．
print '(A)', "|"//strip(" "//TAB//LF//VT//FF//CR)//"|"
! ||

! 文字列の先頭が空白でない場合は，whitespaceを削除しない．
print '(A)', "|"//strip("'"//TAB//LF//VT//FF//CR//"'")//"|"
! |'
! '|
```

#### chomp
```Fortran
! 文字を指定しない場合は空白が削除される．
print '(A)', "|"//chomp("   hello   ")//"|"
! |   hello|

! 文字列からlとoを削除する．
print '(A)', "|"//chomp("hello", set=["l", "o"])//"|"
! |he|

! lとoが末尾にないので変化しない．
print '(A)', "|"//chomp("   hello   ", set=["l", "o"])//"|"
! |   hello   |

! l, o, " "が末尾にあるので削除される．
print '(A)', "|"//chomp("   hello   ", set=["l", "o", " "])//"|"
! |   he|

! loが末尾にあるので削除される．
print '(A)', "|"//chomp("hello", substring="lo")//"|"
! |hel|
```

#### starts_with, ends_with
```Fortran
print *, "hello starts with hel: ", starts_with("hello", "hel")
! hello starts with hel: T

print *, "hello starts with lo: ", starts_with("hello", "lo")
! hello starts with lo: F

print *, "hello ends with hel: ", ends_with("hello", "hel")
! hello ends with hel: F

print *, "hello ends with lo: ", ends_with("hello", "lo")
! hello ends with lo: T
```

#### slice
```Fortran
! 先頭から末尾まで間隔2で文字列を取り出す．
print '(A)', slice("1234567890", stride=2)
! 13579

! 2文字目から末尾まで間隔2で文字列を取り出す．
print '(A)', slice("1234567890", first=2, stride=2)
! 24680

! 先頭から4文字目まで間隔2で文字列を取り出す．
print '(A)', slice("1234567890", last=4, stride=2)
! 13

! 3文字目から9文字目まで間隔4で文字列を取り出す．
print '(A)', slice("1234567890", first=3, last=9, stride=4)
! 37
```

#### replace_all
```Fortran
! ingをedに置き換える．
print '(A)', replace_all("functions for string handling", pattern="ing", replacement="ed")
! functions for stred handled

! wwを12に置き換える．
print '(A)', replace_all("wwwwww", "ww", "12")
! 121212

! "  "を" "に置き換える．オーバーラップはしない．
print '(A)', "|"//replace_all("      ", "  ", " ")//"|"
! |   |
```

#### padl, padr
```Fortran
! 10文字になるように左側に空白を詰める．
print '(A)', "|"//padl("string", 10)//"|"
! |    string|

! 10文字になるように左側に指定の文字を詰める．
print '(A)', "|"//padl("string", 10, "-")//"|"
! |----string|

! 指定の文字数が元の文字数より小さい場合は何もしない．
print '(A)', "|"//padl("string", 4, "-")//"|"
! |string|

! 10文字になるように右側に空白を詰める．
print '(A)', "|"//padr("string", 10)//"|"
! |string    |

! 10文字になるように右側に指定の文字を詰める．
print '(A)', "|"//padr("string", 10, "-")//"|"
! |string----|

! 指定の文字数が元の文字数より小さい場合は何もしない．
print '(A)', "|"//padr("string", 4, "-")//"|"
! |string|
```

#### count
```Fortran
! iの数を数える．
print *, count("functions for string handling", "i")
! 3

! ingの数を数える．
print *, count("functions for string handling", "ing")
! 2

! i, n, oの数を数える．
print *, count("functions for string handling", ["i", "n", "o"])
! [3, 5, 2]

! "  "の数を数える．オーバーラップを許容し，1,2文字目，2,3文字目･･･の数を返す．
print *, count("      ", "  ")
! 5

! "  "の数を数える．引数でオーバーラップを許容することを明示する．
print *, count("      ", "  ", consider_overlapping=.true.)
! 5

! "  "の数を数える．オーバーラップを許容しない．
! 1,2文字目がカウントされたら，次は2,3文字目ではなく3,4文字目をカウントする．
print *, count("      ", "  ", consider_overlapping=.false.)
! 3
```

#### to_string

```Fortran
! 整数を文字に変換する．
print *, to_string( huge(0)) ! 2147483647
print *, to_string(-huge(0)) ! -2147483647

! 書式付きで整数を文字に変換する
! 4桁の数として文字列に変換する．
print *, "|"//to_string(1, '(I4)')   ! |   1

! 4桁でゼロ埋めして文字列に変換する．
print *, "|"//to_string(1, '(I4.4)') ! |0001

! 2桁の数として文字に変換する．桁数が足りない場合は*に置き換えられる．
print *, to_string(100, '(I2)') ! **

block
    integer(int32) :: i
    i = 16
    ! 2桁の10進数として文字列に変換．
    print *, to_string(i, '(I2)') ! 16

    ! 8桁の2進数として文字列に変換．
    print *, to_string(i, '(B8.8)') ! 00010000

    ! 2桁の16進数として文字列に変換．
    print *, to_string(i, '(Z2)') ! 10
end block
```

```Fortran
! 実数を文字列に変換する．
print *, to_string( huge(0.)) ! 0.340282347E+39
print *, to_string(-huge(0.)) ! -0.340282347E+39

print *, to_string(1.) ! 1.00000000

! 書式付きで実数を文字列に変換する．
! 全体で8文字，浮動小数点以下を3桁として文字列に変換する．
print '(A,F8.3)', to_string(100., '(F8.3)'), 100. ! 100.000 100.000

! 指数表記で文字列に変換する．
print '(A,E10.2e2)', to_string(1000., '(E10.2e2)'), 1000. ! 0.10E+04  0.10E+04

! 科学表記で文字列に変換する．

print '(A,ES10.2)', to_string(1000., '(ES10.2)'), 1000. ! 1.00E+03  1.00E+03
```

```Fortran
! 論理型を文字列に変換する．

! 偽を文字列に変換する．
print *, to_string(.false.) ! F

! 真を文字列に変換する．

print *, to_string(.true.) ! T

! 書式付きで文字列に変換する．
print *, to_string(.true., '(L3)') !   T

! 誤った書式で文字列に変換すると，[*]に置き換えられる．
print *, to_string(.true., '(I3)') ! [*]
```

```Fortran
! 複素数を文字列に変換する．
complex(real64) :: c
c = cmplx(0.75, 0.25)

! 書式付きで複素数を文字列に変換する．
print *, to_string(c, '(F6.2)')!  (  0.75,  0.25)

! 複素数リテラルを文字列に変換する．
print *, to_string((0.5, 0.5))! (0.500000000,0.500000000)

! 書式付きで複素数リテラルを文字列に変換する．
print *, to_string((0.5, 0.5), '(F8.3)')! (   0.500,   0.500)
end block
```