跳轉到內容

Fortran/io

來自華夏公益教科書

在 Fortran 和其他語言中,指定想要列印或讀取的內容的位置和方式通常很有用。Fortran 提供了許多命令和格式規範,可以滿足這些目的。在以下部分,我們將考慮 I/O 操作(opencloseinquirerewindbackspaceendfileflushprintreadwritenamelist)和 I/O 格式(format)。在 Fortran 2008 中,一個重要的補充是能夠使用使用者定義的例程擴充套件基本功能,以輸出派生型別,包括那些本身由其他派生型別組成的型別。

這些命令共同構成了一個非常強大的功能集,用於讀取和寫入格式化和非格式化檔案,這些檔案具有順序、直接或非同步訪問方式。實際上,這些選項乍一看可能令人困惑。但是,基本操作很簡單,但它們以強大的靈活性和讀取或寫入幾乎所有檔案的強大功能為後盾。

但是,值得一提的是,在語言定義的角度來看,Fortran 不能簡單直接地執行的操作:Fortran 不會處理由 URL 定義的檔案,檔案必須在本地或對映的網路驅動器或等效位置可用。類似地,Fortran 本身不支援 XML,除了 XML 是簡單的 ASCII 文字,因此可以輕鬆讀取,但解析它則由程式設計師負責!Fortran 語言不知道計算機圖形;你不會在語言定義的命令中找到 draw 命令。Fortran 很樂意開啟並讀取 jpg 檔案,但沒有語言定義的方法將檔案顯示為圖片;必須使用合適的外部庫。最後,Fortran 沒有任何語言定義的滑鼠操作或觸控式螢幕手勢。

I/O 操作

[編輯 | 編輯原始碼]

現代 Fortran 擁有豐富的 I/O 操作詞彙。這些操作通常可以在螢幕和鍵盤、外部檔案和內部檔案中使用。在最近的 Fortran 版本中,這些命令的語法已經變得更加合理,但為了向後相容,大部分原始語法都保留了下來。I/O 操作是出了名的容易出錯,Fortran 現在支援統一的機制來識別和處理錯誤。

簡單 I/O 操作

[編輯 | 編輯原始碼]

這是經典的“Hello World”操作,但在生產程式碼中很少使用。print 是兩種格式化輸出操作之一,它也比 write 語句簡單得多。print 語句的主要目的是列印到螢幕(標準輸出單元),它沒有檔案輸出選項。一般形式是

print fmt, list

fmtlist 都是可選的,fmt 可以是顯式或列表導向(如 * 所示),並且是可選的,可以採用 name=value 格式。列表是文字或內在型別變數的逗號分隔列表。以下是一些示例

program hello
    implicit none
    integer :: i

    ! List-directed fmt
    print *, "Hello World"
    do i = 1, 10
        ! An explicit fmt and a two element list
        print '(A,I0)', "Hello ", i
    end do
    ! Name=value for fmt
    print fmt='(A)', 'Goodbye'
end program hello

請注意,print 幾乎是唯一仍然不支援 iostatiomsg 子句的 I/O 操作,僅此原因就應該在新程式碼中不使用它,除非用於臨時輸出和除錯。print 沒有用於列印使用者定義型別的顯式機制,這也是在生產程式碼中不使用它的另一個原因。

I/O 通道和檔案

[編輯 | 編輯原始碼]

在上面顯示的 print 示例中,print 語句自動預連線到標準輸出裝置,也稱為計算機螢幕。但是,通常情況下,Fortran 需要一個兩階段過程才能將程式碼連線到外部檔案。首先,我們必須將檔案連線到 Fortran 通道(手動透過正整數標識或自動分配負值):open 命令,然後我們可以對現在開啟的通道進行 readwrite 操作。對未開啟的通道進行 readwrite 操作會導致錯誤;在相關通道開啟之前,沒有語言指定的緩衝區。完成 I/O 操作後,我們可以關閉檔案和 Fortran 通道之間的連線。如果 Fortran 程式在通道關閉到檔案之前終止,Fortran 通常會關閉通道,而不會丟失大量資料。

可以透過 inquire 命令的一種形式來確定 Fortran 通道的可用狀態。inquire 命令還可以在將檔案連線到 Fortran 通道之前用於確定檔案的存在和其他屬性。

Fortran 對內部檔案的 I/O 不需要預連線過程。Fortran 從鍵盤輸入和輸出到螢幕的 I/O 在特殊通道 (*) 上自動預連線。編譯器供應商可以自由地將通道號分配給這些標準 I/O 裝置,使用者可以透過內在模組 iso_fortran_env 確定已使用哪些通道號。

這是建立外部檔案和 Fortran 通道之間連線所需的命令。open 命令可用於建立新檔案或連線到現有檔案。開啟後,對檔案的後續 I/O 是透過此通道號進行的。open 命令具有選項來確保檔案已存在或不存在,以確保它僅用於輸入,或僅用於輸出,或用於兩者。可以在 open 命令中指定檔案的預期格式,並可以捕獲錯誤。命令的完整語法可能看起來很複雜,但通常任何一次對 open 的呼叫只使用所有可用選項中的一個小子集。

open 命令最初用於外部檔案通常是卡片影像的時候。open 現在可以指定檔案連線為固定格式、非同步、二進位制流以及這些格式的多種組合。值得記住的是,I/O 是潛在編碼錯誤的主要來源,在讀取關鍵資料時,應將其重新寫入以確認處理正確。

Fortran 通道號的值在任何一個 Fortran 程式的影像中都有全域性作用域。即使使用整型變數開啟通道,並且該變數的作用域非常有限,但實際的通道號實際上無處不在。在設計時需要考慮這一點,因為一個模組可以開啟,比如通道 10,另一個模組可以關閉通道 10,而它們之間沒有任何使用關聯。出於這個原因,在大型程式中,通常使用單個模組來控制所有檔案 I/O 操作,以便可以維護清晰明顯的“開啟 - 讀取/寫入 - 關閉”鏈。

最後,和往常一樣,Fortran 有些選項和子句是為了遺留目的而保留的,不應該在新程式碼中使用。

開啟命令語法

[edit | edit source]
open ([unit=]u[, olist])

其中 [] 表示可選部分,olist 是一個用逗號分隔的選項列表。在上面,u 是一個標量整型表示式或等效的,並且是必需的,除非指定了 newunit 選項。(不幸的是,我們不能在一個 open 語句中開啟多個檔案)。從技術上講,u 被稱為外部檔案單元號,簡稱通道號。

常用選項

[edit | edit source]

newunit=nu,其中 nu 是一個預設的整型變數。這允許處理器選擇通道號,並且,為了避免與遺留程式碼衝突,將選擇一個負值(不是 -1),它不會與任何當前正在使用的單元號衝突。這應該是所有新程式碼中使用的形式。

iostat=ios,其中 ios 是一個預設的整型變數,如果 open 語句沒有檢測到錯誤,則將被設定為零,但如果發生錯誤,則將被設定為正值,並且確切的值取決於供應商。雖然從技術上講是一個選項,但這是所有 open 命令的強烈推薦選項。如果沒有此選項(並且沒有 err= 選項,請參見下文),如果發生錯誤,程式將停止。此選項的存在使程式設計師有責任檢查返回值,並讓程式相應地採取行動。

iomsg=iom,其中 iom 是一個預設型別的標量字元變數。同樣,雖然從技術上講是一個選項,但這對於新程式碼中的所有 open 命令來說都是一個強烈推薦的選項。訊息的長度是錯誤和供應商特定的,可能需要一些試錯。

file=fln,其中 fln 是一個預設的字元變數、文字或表示式,用於指定外部檔案的名稱。檔名可以是完全限定的路徑或本地檔名。如果路徑指向網路驅動器上的檔案,則驅動器必須預先連線,並且沒有語言定義的方法來建立這種連線。(除了我們可以始終訴諸於 execute_command_line

status=stn,其中 stn 也是一個預設的字元變數、文字或表示式,它必須評估(不區分大小寫)為“old”、“new”、“replace”、“scratch”或“unknown”之一。“old”要求檔案存在,通常在 open 語句的目的是允許讀取檔案時使用。“new”和“replace”要求存在上面描述的 file= 選項,“new”要求檔案不存在,“replace”允許檔案已經存在,但如果存在,則將被覆蓋。“scratch”很特殊,因為它不能使用 file= 選項,並且在隨後執行 close 命令時不能保留建立的檔案。“scratch”通常用於將大型資料結構臨時儲存到硬碟或類似的大容量儲存裝置中。如果指定了“unknown”,這也是 status= 選項未給出的預設值,並且檔案狀態變得依賴於供應商和系統,即需要查閱手冊。

action=act,其中 act 是一個預設型別的字元表示式、變數或值,它評估為“read”、“write”或“readwrite”。令人驚訝的是,預設值是處理器相關的,因此需要查閱手冊。如果指定了“read”,則該檔案將被視為只讀,並且嘗試在此通道上執行 writeprintend file 語句會導致錯誤。類似地,如果指定了“write”,則該檔案將被視為只寫,並且嘗試執行 read 語句會導致錯誤。當指定“write”時,某些其他語句可能會以處理器相關的方式導致錯誤(例如 backspace)。

開啟的簡單示例

[edit | edit source]
program opena
    implicit none
    integer :: nout !channel number
    integer :: my_iostat !integer scalar to catch error status
    character (len=256) :: my_iomsg !Default-kind character variable to catch error msg

    open (newunit=nout, file="local.dat", iostat=my_iostat, iomsg=my_iomsg)
    if (my_iostat /= 0) then
        write (*,*) 'Failed to open local.dat, iomsg='//trim(my_iomsg)
        stop
    end if
    write (nout,*) 
end program

不太常用的選項

[edit | edit source]

access=acl,其中 acl 是一個字元表示式、變數或文字,它評估為“sequential”、“direct”或“stream”之一。當開啟已經存在的檔案時,此值必須與允許的值對應,該值通常是在建立檔案時給出的值。新檔案的預設值為“sequential”。“stream”訪問是 Fortran 2008 中的新功能,它提供了一些與 C 二進位制流檔案相容性。“stream”訪問的另一個真正重要的特徵是,可以將檔案定位為寫入,並且可以覆蓋檔案的一部分,而不會改變檔案的其餘部分。對於格式化的流檔案,new_line(nl) 函式將在字元變數 nl 中返回相關的新行字元。

recl=rcl,其中 rcl 是一個整型表示式、變數或文字,它必須評估為正值。對於要以直接訪問方式開啟的檔案,此“選項”是必需的,並且必須指定每個記錄的長度。對於順序檔案,它是可選的,可以用來指定記錄的最大長度。對於已經存在的檔案,rcl 的值必須與用於建立檔案的那個值相對應。在任何情況下,rcl 的值也必須被底層作業系統允許。

form=frm,其中 frm 是一個字元表示式、變數或文字,它評估為“formatted”或“unformatted”之一。此選項通常可以省略,因為順序訪問的預設值為“formatted”,直接訪問的預設值為“unformatted”。

blank=blk,其中 blk 是一個字元表示式、變數或文字,它為格式化 I/O 提供“null”或“zero”的值。請參見下面的 bn 和 bz 格式。

position=psn,其中 psn 是一個字元表示式、變數或文字,它評估為“asis”、“rewind”或“append”,並且僅在訪問方法為順序時適用。預設值為“asis”。當開啟時,新檔案始終定位在其初始點,但對於現有檔案,使用者可以選擇當前位置的定位位置。

delim=

pad=

要避免的選項

[edit | edit source]

那裡有很多遺留程式碼,本節描述了仍然合法的功能,但應該考慮替換它們,當然不能在新程式碼中使用。

err=eno,其中 eno 是一個文字整型標籤號。如果在處理 open 語句時發生錯誤,程式將控制權轉移到標籤號為 eno 的語句。err= 選項的存在導致程式在發生錯誤時繼續。此選項現在應該用 iostat=iomsg= 替換。(在 err=iostat= 中指定兩者都是合法的,但如果沒有兩者,如果在處理 open 語句時發生錯誤,程式將停止。)

unit=nu,其中 nu 是一個預設的整型表示式、變數或文字值,它必須為正,並且不能與任何已使用的單元重合。如果此選項放在選項列表中的第一個位置,“unit=”可以省略。這曾經被廣泛使用,現在應該用 newunit= 替換。在非常舊的程式碼中,nu 是一個固定值,程式設計師必須確保它不會與同時使用的其他通道發生衝突。最近,可以使用 inquire 語句選擇尚未使用的單元號,但這不能防止後續的 open 語句嘗試使用已使用的固定值。這就是為什麼 newunit 選項(並且只有 newunit 選項)允許為通道號指定負值。

讀取

[edit | edit source]

read 語句是一個從指定輸入以指定格式讀取資料的語句,一個變數。例如,

program reada
    implicit none
    integer :: a

    read (*,*) a
end program

將建立一個用於 a 的整型記憶體單元,然後它將使用預設格式從預設輸入讀取一個值並將其儲存在 a 中。(*,*) 中的第一個 * 表示應該從哪裡讀取值。第二個 * 指定使用者希望使用的讀取數字的格式。讓我們首先討論可用的格式字串。在 Fortran 中,我們可以使用許多格式字串來指定我們希望數字或字元字串在螢幕上顯示的方式。對於定點實數:Fw.d;w 是分配給數字的總空格數,d 是小數位數。小數點始終佔一個位置。例如,

program reada
    implicit none
    real :: a

    read (*,'(F5.2)') a
end program

I/O 格式的詳細資訊(例如,'(F5.2)')將在下面描述

寫入

[edit | edit source]

查詢

[edit | edit source]

inquire 語句有兩種基本形式:“按單元inquire”和“按檔案inquire”,這兩種形式都非常有用,值得學習。還有一種不太常用的形式,稱為“按長度inquire”,它用於檢查潛在輸出的非格式化記錄長度,以便確定需要什麼記錄長度,或者已經定義了記錄長度的檔案是否可以處理給定的輸出。

按單元查詢

[編輯 | 編輯原始碼]

按檔案查詢

[編輯 | 編輯原始碼]

按長度查詢

[編輯 | 編輯原始碼]

這種不太常用的 inquire 命令版本用於獲取包含給定形式輸出所需的非格式化記錄長度,從而允許使用者檢查或指定所需的記錄長度。

查詢錯誤

[編輯 | 編輯原始碼]

close 命令釋放檔案和 Fortran 通道之間的連線。在此過程中,根據檔案的開啟方式,可以儲存或丟棄檔案。close 命令的一般形式如下

close ([unit=]u, [, olist])

其中 u 是一個預設的整型表示式、變數或文字值,它計算為要關閉的通道的編號,unit= 是可選的。選項列表中可用的選項如下

iostat=ios,其中 ios 是一個預設的整型變數,如果 close 命令執行正確,它將返回 0。如果發生錯誤,返回值將為正數,並將透過下面描述的 iomsg 選項提供描述錯誤的訊息。

err=eno,其中 eno 是一個文字整型標籤號。如果在處理 close 語句時發生錯誤,程式將控制權轉移到標籤號為 eno 的語句。err= 選項的存在導致程式在發生錯誤時繼續執行。現在,此選項應替換為 iostat=iomsg=。(在技術上允許指定 err=iostat=,但如果沒有指定任何一個,如果在處理 open 語句時發生錯誤,程式將停止。)

iomsg=iom,其中 iom 是一個預設型別的標量字元變數。同樣,儘管從技術上講是一個選項,但對於新程式碼中的所有 close 命令來說,這是一個強烈推薦的選項。訊息的長度是錯誤和供應商特定的,可能需要一些試錯。

status=st

關閉錯誤

[編輯 | 編輯原始碼]

可能有點違反直覺,但 Fortran 不認為嘗試關閉已經關閉的通道是錯誤。與 inquireopen 一樣,close 將報告的錯誤實際上是作業系統錯誤。例如,如果通道已關閉,則使用 status="delete" 關閉通道是一個錯誤,尤其是在檔案不再存在的情況下。同樣,如果以 'scratch' 建立的檔案使用 status="keep" 關閉,Fortran 將報告錯誤,因為暫存檔案的唯一選項是 status='delete'。這些限制基於使用 iostat(和 iomsg)子句允許使用者在必要時對程式進行優雅的終止,而不是獲得有關 close 命令執行情況的完整報告。

I/O 格式化

[編輯 | 編輯原始碼]

列表定向格式化

[編輯 | 編輯原始碼]

我們在下面描述了顯式格式化,但很明顯,存在一種“語言中的語言”,因此 Fortran 提供了一種快捷方式,或者語言定義的格式猜測。事實證明,這種預設格式非常接近用於輸入的逗號分隔值 (CSV) 處理器。列表定向 I/O 由 fmt=* 子句指定,但 fmt 子句是可選的,可以替換為 *。

顯式格式化

[編輯 | 編輯原始碼]

Fortran 擁有一種豐富但非常簡潔的語言,用於控制 I/O 操作的格式。格式命令可以放置在顯式 format 語句中,也可以放置在相關 READ 或 WRITE 語句的子句中,無論是直接放置還是儲存在 character 變數中。

華夏公益教科書