为程序设置配置文件:TOML

TOML官网🎯:https://toml.io/cn/
TOML-F仓库(许可证:Apache License v2或者MIT License):https://github.com/toml-f/toml-f

TOML(Tom的(语义)明显、(配置)最小化的语言)主要被设计为程序的配置文件,简单易用,典型地被fpm、cargo、julia作为代码包的配置文件。
TOML也可被用作有限制性的、数据序列化的数据存储文件。

toml-f是Fortran语言的TOML解析实现,我们可以使用它作为Fortran程序的配置文件。

TOML-Fortran

toml-f读取toml的API

在演示toml-f示例之前,我们来熟悉一(亿)下toml-f的API😎。

toml_parse:解析toml

从字符串(character(:), allocatable)或者文件单元号(unit)读取toml信息,这是解析toml内容的第一步。

call toml_parse(table, in [, error])
变量类型意义
tabletoml_table类型,allocatableintent(out)toml表格实例
ininteger或者character(:), allocatable类型,intent(in)控制从文件单元号,或字符串中读取toml信息
errortoml_error类型,intent(out)optional错误提示

get_value:读取变量的值

从toml表格中读取确定键(key)的值(value)。

call get_value(table [, pos], ptr [, requested/default, stat])
变量类型意义
tabletoml_table/toml_array/toml_keyval类型,intent(inout)toml表格实例/数组指针/键值指针
postoml_array/toml_key/integer类型,intent(in)toml表格中的位置
ptrinteger或者character(:), allocatable类型,intent(in)变量的值,或者子表格的指针
requestedlogical类型,intent(in)optional是否请求在toml表格中添加默认值
defaultreal/integer/logical/character(*)类型,intent(in)读取toml信息时,信息不存在,预设的默认值
statinteger类型,intent(out)optional返回0为成功

destory:析构toml缓存(可忽略)

在读取完toml信息,我们可选地析构toml_table内的内容。

call table%destory()
变量类型意义
tabletoml_table类型toml表格实例

🔰 除非你在某个module内声明了一个module内的全局toml_table,要节约内存,否则,这个析构例程请大胆地忽略它。 因为在单元例程中的toml_table类型变量在例程结束后,就会结束其生命周期。

演示toml-f库的读取功能

cd workspace  # 切换到你常用的工作区间
fpm new --app toml-demo  # 创建fpm项目
cd toml-demo && code .   # 切换到`toml-demo`文件夹,并使用vs code打开它

我们创建了一个toml-demo工程,使用vs code打开了它,我们可以在fpm工程的fpm.toml文件中添加以下语句,以使用toml-f

[dependencies]
toml-f = { git="https://github.com/toml-f/toml-f.git" }

toml-f仓库的README.md中🕹提供了一个简单的示例。我们自己写个示例进行演示:

toml-demo

我们读取了所有toml内的数据,读取结果全部符合预期,程序正常退出了🚀。

示例代码

program main

    use tomlf, only: toml_table, get_value, toml_parse, toml_array, len
    implicit none
    type(toml_table), allocatable :: intable
    type(toml_table), pointer :: subtable
    type(toml_array), pointer :: array
    integer :: unit, i, z

    !> 目标读取变量
    character(:), allocatable :: name
    integer, allocatable :: x(:)
    real :: y

    open (newunit=unit, file="data.toml", status="old")

    !> 解析toml文件
    call toml_parse(intable, unit)

    !> 读取主表根目录的各个值
    
    call get_value(intable, "name", name)
    call check(name == "toml-demo", "`name=='toml-demo'` failed")

    call get_value(intable, "x", array)
    if (len(array) == 3) then
        allocate (x(3))

        do i = 1, 3

            call get_value(array, i, x(i))

        end do

        call check(all(x == [1, 2, 3]), "`x == [1, 2, 3]` failed")

    else
        error stop "`x`数组长度不等于3"
    end if

    !> 读取子表的各个值
    call get_value(intable, "foo", subtable)
    call get_value(subtable, "y", y)
    call check(abs(y - 10.0) < 1.0E-6, "`y == 10.0` failed")

    !> 读取一个不存在的值
    call get_value(intable, "z", z, stat=i)
    !> *注意*:z值不存在,但i返回成功值`0`
    call check(i == 0, "`i == 0` failed")

    !> 读取一个不存在的值,但提供默认值
    call get_value(intable, "z", z, 1, stat=i)
    call check(i == 0, "`i == 0` failed")
    !> 值不存在,但提供默认值,则z返回默认值
    call check(z == 1, "`z == 1` failed")

    print *, "读取结束,程序正常退出 ^_^"
contains

    !> 断言与测试
    subroutine check(condition, msg)
        logical, intent(in) :: condition     !! 测试条件
        character(len=*), intent(in) :: msg  !! 测试失败时的消息
        if (condition) return
        error stop msg
    end subroutine check

end program main
# data.toml
name = "toml-demo"
x = [1, 2, 3]

[foo]
y = 10.0

说明

module tomlf
    public :: get_value, set_value  !! 读取、写入值
    public :: toml_parse            !! 解析toml
    public :: toml_error, toml_stat !! toml-f辅助类型
    public :: toml_serializer       !! toml-f序列化例程
    public :: toml_table, toml_array, toml_key, is_array_of_tables, new_table, add_table, add_array, len
                                    !! toml-f辅助例程
    public :: sort                  !! toml-f排序
    public :: tomlf_version_string, tomlf_version_compact, get_tomlf_version  !! toml-f版本管理例程
end module tomlf

toml-f的API可以在https://toml-f.github.io/toml-f/找到,也可以直接阅读源码,非常推荐阅读它的单元测试源码,里面有着很全的API使用。

你可以模仿fpm.toml中toml的写法,或者去TOML官网查阅toml语法。

我们先学习如何使用toml-f解析读取toml信息,在此过程中,我们逐渐熟悉其API的使用,读者如果有输出toml的需求,很容易理解使用toml-f写入toml。

需要注意的有两点:

  • tomljson的转换在toml-f的单元测试中提供了代码。
    json-fortran(许可证:类似BSD):https://github.com/jacobwilliams/json-fortran
  • 从toml文件中尝试解析不存在的键值对,toml-f不认为是错误,所以请读取变量的设置默认值。
  • 在手动设置.toml内容时,注意整型和浮点型的区别,x = 1x始终被认为是整型,x = 1.0x才是浮点型,这点往往会被忽视。