2021年7月31日 星期六

安裝 GFortran 使用 Fortran 程式讀取、寫入 csv 檔

作者:王一哲
日期:2021/7/31


前言


我之前幾乎沒有寫過 Fortran,這次為了幫某人擺脫需要花錢購買的 Visual Fortran,我只好從零開始學 Fortran,並且試試看是否能用 GFortran 編譯他們實驗室需要的程式碼,由 csv 檔讀取資料經過一些運算後再儲存成另一個 csv 檔,以下是我測試的筆記。


於 Ubuntu 安裝 GFortran


我使用的作業系統是 Ubuntu 20.04.2 LTS,只要在文字界面中執行以下指令即可安裝 GFortran。
sudo apt install gfortran-10

安裝完畢之後可以用以下指令查詢安裝的版本,我安裝的版本是 10.3.0。
gfortran-10 --version

建立一個測試用的資料夾,於資料夾中用文字編輯器將以下內容儲存成 hello.f95。
program hello
    write(*, *) 'Hello World!'
    write(*, *) '哈囉世界!'
stop
end program hello

用以下指令將 hello.f95 編譯成執行檔再執行檔案。
gfortran-10 -o hello hello.f95
./hello

如果螢幕輸出以下文字就成功了。
 Hello World!
 哈囉世界!



於 Windows 10 安裝 GFortran


我使用的作業系統是 Windows 10 家用版。安裝步驟主要是參考這篇文章 Installation of GFortran in Windows 10。先安裝 mingw-w64,目前最新的版本是 msys2-x86_64-20210725.exe,下載連結在此。安裝完成之後開啟 MSYS2 MinGW 64-bit。
MSYS2


輸入以下指令更新軟體列表、安裝 GFortran。
pacman -Syu
pacman -Su gcc-fortran

輸入以下指令查詢安裝好的 GFortran 版本。
gfortran.exe --version

我安裝的是 10.2.0 版。用 pwd 查詢路徑,看起來是在 /home/[UserName] 底下,實際上的路徑是
C:\msys64\home\[UserName]

接著用 Hello World! 測試一下。
於 Windows 10 測試 GFortran



使用 Fortran 程式讀取、寫入 csv 檔


這次的目標是要在執行程式時於後方加上輸入、輸出檔名,由輸入的 csv 檔讀取資料,再將資料寫到輸出的 csv 檔,主要是參考這兩個網頁的內容:Passing command line argumentsTEK-TIPS.COM: Help with .csv。假設輸入檔名為 input.csv,內容如下,第一行開頭的 ! 是為了在讀取資料時忽略此行而加上的記號。
!h(m),t(s),errt(s)
2,0.65,0.01
4,0.89,0.01
6,1.11,0.01
8,1.27,0.01
10,1.42,0.01
12,1.57,0.01
14,1.71,0.01
16,1.79,0.01
18,1.92,0.01
20,2.03,0.01

以下是我測試成功的程式碼。
program fileIO

    implicit none                     ! 不使用預設變數名稱及種類對應關係
    character(20) :: inputfile        ! 輸入檔名用的變數
    character(20) :: outputfile       ! 輸出檔名用的變數
    integer :: flag1, flag2, line_num ! 檔案狀態1、2, 行號 
    character(80) :: line             ! 讀取的整行資料
    real :: h, t, t2, errt            ! 儲存資料的變數

    ! 檢查輸入的參數是否為2個
    if(command_argument_count().NE.2) then
        write(*,*) 'Error! Please enter the input and output file names.'
        stop
    end if

    ! 由輸入的參數取得輸入、輸出檔名
    call get_command_argument(1, inputfile)
    call get_command_argument(2, outputfile)

    write(*,*) 'The input file name is: ', inputfile
    write(*,*) 'The output file name is: ', outputfile

    ! 由 inputfile 讀取資料, 準備寫入資料至 outputfile
    open(3, file=inputfile, status='old', action='read', iostat=flag1)
    open(4, file=outputfile, action='write', iostat=flag2)

    ! 檢查是否能開啟檔案
    if(flag1 .NE. 0) then
        write(*, *) 'Cannot open input file!'
        go to 99
    end if

    if(flag2 .NE. 0) then
        write(*, *) 'Cannot open output file!'
        go to 99
    end if

    ! 於 outputfile 寫入欄位標題
    write(4, *) '!h(m),t(s),t2(s2),errt(s)'

    line_num = 0
    do 
        ! 讀取整行資料並存入 line
        read(3, '(A)', end=99, iostat=flag1) line
        ! 若 flag1 不等於 0 則跳到 99 continue        
        if(flag1 .NE. 0) then
            write(*, *) 'Cannot read data!'
            go to 99
        end if
        ! 若 line 的開頭為 '!' 則不做任何事
        if(adjustl(trim(line(1:1))) .EQ. '!') then
            cycle
        end if
        ! 將 line 的資料分別存入 h, t, errt, 計算 t^2, 存入 outputfile
        read(line, *) h, t, errt
        t2 = t**2        
        write(*, *) 'line ', line_num, ': ', h, t, t2, errt
        line_num = line_num + 1
        write(4, *) h, ',', t, ',', t2, ',', errt
    end do

    99 continue
    close(3)
    close(4)

stop
end program fileIO

假設檔名是 fileIO.f95,在 Ubuntu 可以使用以下指令編譯程式。
gfortran-10 -o fileIO fileIO.f95

在 Windows 10 可以使用以下指令編譯程式。
gfortran.exe -o fileIO fileIO.f95

使用以下指令執行程式。
./fileIO input.csv output.csv

於文字界面輸出以下的內容。
 The input file name is: input.csv           
 The output file name is: output.csv          
 line            0 :    2.00000000      0.649999976      0.422499955       9.99999978E-03
 line            1 :    4.00000000      0.889999986      0.792099953       9.99999978E-03
 line            2 :    6.00000000       1.11000001       1.23210001       9.99999978E-03
 line            3 :    8.00000000       1.26999998       1.61289990       9.99999978E-03
 line            4 :    10.0000000       1.41999996       2.01639986       9.99999978E-03
 line            5 :    12.0000000       1.57000005       2.46490026       9.99999978E-03
 line            6 :    14.0000000       1.71000004       2.92410016       9.99999978E-03
 line            7 :    16.0000000       1.78999996       3.20409989       9.99999978E-03
 line            8 :    18.0000000       1.91999996       3.68639994       9.99999978E-03
 line            9 :    20.0000000       2.02999997       4.12089968       9.99999978E-03

於 Windows 10 測試 fileIO 程式碼


輸出的 csv 檔如下,看來應該有達到我要的效果。
 !h(m),t(s),t2(s2),errt(s)
   2.00000000     ,  0.649999976     ,  0.422499955     ,   9.99999978E-03
   4.00000000     ,  0.889999986     ,  0.792099953     ,   9.99999978E-03
   6.00000000     ,   1.11000001     ,   1.23210001     ,   9.99999978E-03
   8.00000000     ,   1.26999998     ,   1.61289990     ,   9.99999978E-03
   10.0000000     ,   1.41999996     ,   2.01639986     ,   9.99999978E-03
   12.0000000     ,   1.57000005     ,   2.46490026     ,   9.99999978E-03
   14.0000000     ,   1.71000004     ,   2.92410016     ,   9.99999978E-03
   16.0000000     ,   1.78999996     ,   3.20409989     ,   9.99999978E-03
   18.0000000     ,   1.91999996     ,   3.68639994     ,   9.99999978E-03
   20.0000000     ,   2.02999997     ,   4.12089968     ,   9.99999978E-03



結語


由於 Fortran 的語法與 Python、C、C++ 差異很大,寫起來感覺相當奇怪。接下來還要再修改他們實驗室以前的程式碼,其中一部分還是採用 Fortran 77 的語法撰寫,要修改成新版 GFotrtan 可以接受的語法才行,好像還有不少的麻煩要解決。


參考資料


  1. gfortran — the GNU Fortran compiler, part of GCC
  2. Installation of GFortran in Windows 10
  3. mingw-w64
  4. Passing command line arguments
  5. TEK-TIPS.COM: Help with .csv




HackMD 版本連結:https://hackmd.io/@yizhewang/Sk5vTFMyK

沒有留言:

張貼留言