# 安裝 GFortran 使用 Fortran 程式讀取、寫入 csv 檔
> 作者:王一哲
> 日期:2021/7/31
<br />
## 前言
我之前幾乎沒有寫過 Fortran,這次為了幫某人擺脫需要花錢購買的 [Visual Fortran](https://software.intel.com/content/www/us/en/develop/articles/intel-visual-fortran-compiler-190-for-windows-release-notes-for-intel-parallel-studio-xe-2019.html),我只好從零開始學 Fortran,並且試試看是否能用 [GFortran](https://gcc.gnu.org/wiki/GFortran) 編譯他們實驗室需要的程式碼,由 csv 檔讀取資料經過一些運算後再儲存成另一個 csv 檔,以下是我測試的筆記。
<br />
## 於 Ubuntu 安裝 GFortran
我使用的作業系統是 Ubuntu 20.04.2 LTS,只要在文字界面中執行以下指令即可安裝 GFortran。
```shell
sudo apt install gfortran-10
```
<br />
安裝完畢之後可以用以下指令查詢安裝的版本,我安裝的版本是 10.3.0。
```shell
gfortran-10 --version
```
<br />
建立一個測試用的資料夾,於資料夾中用文字編輯器將以下內容儲存成 hello.f95。
```fortran
program hello
    write(*, *) 'Hello World!'
    write(*, *) '哈囉世界!'
stop
end program hello
```
<br />
用以下指令將 hello.f95 編譯成執行檔再執行檔案。
```shell
gfortran-10 -o hello hello.f95
./hello
```
<br />
如果螢幕輸出以下文字就成功了。
```shell
 Hello World!
 哈囉世界!
```
<br />
## 於 Windows 10 安裝 GFortran
我使用的作業系統是 Windows 10 家用版。安裝步驟主要是參考這篇文章 [Installation of GFortran in Windows 10](https://masuday.github.io/fortran_tutorial/install_gfortran_windows.html)。先安裝 [mingw-w64](http://mingw-w64.org/doku.php),目前最新的版本是 msys2-x86_64-20210725.exe,[下載連結在此](https://github.com/msys2/msys2-installer/releases/download/2021-07-25/msys2-x86_64-20210725.exe)。安裝完成之後開啟 MSYS2 MinGW 64-bit。
<img height="40%" width="40%" src="https://i.imgur.com/xIlG034.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">MSYS2</div>
<br /><br />
輸入以下指令更新軟體列表、安裝 GFortran。
```shell
pacman -Syu
pacman -Su gcc-fortran
```
<br />
輸入以下指令查詢安裝好的 GFortran 版本。
```shell
gfortran.exe --version
```
<br />
我安裝的是 10.2.0 版。用 pwd 查詢路徑,看起來是在 /home/[UserName] 底下,實際上的路徑是
```shell
C:\msys64\home\[UserName]
```
<br />
接著用 Hello World! 測試一下。
<img height="60%" width="60%" src="https://i.imgur.com/oMB3I1f.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">於 Windows 10 測試 GFortran</div>
<br /><br />
## 使用 Fortran 程式讀取、寫入 csv 檔
這次的目標是要在執行程式時於後方加上輸入、輸出檔名,由輸入的 csv 檔讀取資料,再將資料寫到輸出的 csv 檔,主要是參考這兩個網頁的內容:[Passing command line arguments](https://riptutorial.com/fortran/example/26615/passing-command-line-arguments)、[TEK-TIPS.COM: Help with .csv](https://www.tek-tips.com/viewthread.cfm?qid=1710427)。假設輸入檔名為 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
```
<br />
以下是我測試成功的程式碼。
```fortran=
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
```
<br />
假設檔名是 fileIO.f95,在 Ubuntu 可以使用以下指令編譯程式。
```fortran
gfortran-10 -o fileIO fileIO.f95
```
<br />
在 Windows 10 可以使用以下指令編譯程式。
```fortran
gfortran.exe -o fileIO fileIO.f95
```
<br />
使用以下指令執行程式。
```shell
./fileIO input.csv output.csv
```
<br />
於文字界面輸出以下的內容。
```shell
 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
```
<br />
<img height="100%" width="100%" src="https://i.imgur.com/CBjWRKC.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">於 Windows 10 測試 fileIO 程式碼</div>
<br /><br />
輸出的 csv 檔如下,看來應該有達到我要的效果。
```shell
 !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
```
<br />
## 結語
由於 Fortran 的語法與 Python、C、C++ 差異很大,寫起來感覺相當奇怪。接下來還要再修改他們實驗室以前的程式碼,其中一部分還是採用 Fortran 77 的語法撰寫,要修改成新版 GFotrtan 可以接受的語法才行,好像還有不少的麻煩要解決。
<br />
## 參考資料
1. [gfortran — the GNU Fortran compiler, part of GCC](https://gcc.gnu.org/wiki/GFortran)
2. [Installation of GFortran in Windows 10](https://masuday.github.io/fortran_tutorial/install_gfortran_windows.html)
3. [mingw-w64](http://mingw-w64.org/doku.php)
4. [Passing command line arguments](https://riptutorial.com/fortran/example/26615/passing-command-line-arguments)
5. [TEK-TIPS.COM: Help with .csv](https://www.tek-tips.com/viewthread.cfm?qid=1710427)
<br />
---
###### tags:`Computer`、`Fortran`