Lua 第7部分 輸入輸出

????????由于 Lua 語言強調可移植性和嵌入性 , 所以 Lua 語言本身并沒有提供太多與外部交互的機制 。 在真實的 Lua 程序中,從圖形、數據庫到網絡的訪問等大多數 I/O 操作,要么由宿主程序實現,要么通過不包括在發行版中的外部庫實現。 單就 Lua 語言而言,只提供了 ISO C語言標準支持的功能, 即基本的文件操作等。 在這一章中,我們將會學習標準庫如何支持這些功能。

7.1 ?簡單 I/O 模型

????????對于文件操作來說, I/O 庫提供了兩種不同的模型 。 簡單模型虛擬了一個當前輸入流和一個當前輸出流,其 I/O 操作是通過這些流實現的 。 I/O 庫把當前輸入流初始化為進程的標準輸入( C 語言中的 stdin ),將當前輸出流初始化為進程的標準輸出( C 語言中的 stdout ) 。 因此 ,當執行類似于 io.read()?這樣的語句時,就可以從標準輸入中讀取一行。

????????函數 io.input 和函數 io.output 可以用于改變當前的輸入輸出流。 調用 io.input(file-name )會以只讀模式打開指定文件,并將文件設置為當前輸入流。 之后,所有的輸入都將來自該文件,除非再次調用 io.input 。 對于輸出而言,函數 io.output 的邏輯與之類似。 如果出現錯誤,這兩個函數都會拋出異常。 如果想直接處理這些異常, 則必須使用完整 I/O 模型。

????????由于函數 write 比函數 read 簡單,我們首先來看函數 write 。 函數 io .write 可以讀取任意數量的字符串(或者數字)并將其寫人當前輸出流。 由于調用該函數時可以使用多個參數,因此應該避免使用 io.write(a..b..c )應該調用 io.write(a, b, ?c ) ,后者可以用更少的資源達到同樣的效果,并且可以避免更多的連接動作。

????????作為原則,應該只在“用后即棄”的代碼或調試代碼中使用函數 print ; 當需要完全控制輸出時,應該使用函數 io.write 。 與函數 print 不同,函數 io.write 不會在最終的輸出結果中添加諸如制表符或換行符這樣的額外內容。 此外,函數 io.write 允許對輸出進行重定向,而函數 print 只能使用標準輸出 。 最后 ,函數 print 可以自動為其參數調用 tostring ,這一點對于調試而言非常便利,但這也容易導致一些詭異的 Bug 。

????????函數 io.write 在將數值轉換為字符串時遵循一般的轉換規則;如果想要完全地控制這種轉換,則應該使用函數 string.format:

> io.write("sin(3) = ",math.sin(3), "\n")
sin(3) = 0.14112000805987
> io.write(string.format("sin(3) = %.4f\n", math.sin(3)))
sin(3) = 0.1411

????????函數 io.read 可以從當前輸入流中讀取字符串,其參數決定了要讀取的數據:

"a"讀取整個文件
"l"讀取下一行(丟棄換行符)
"L"讀取下一行(保留換行符)
"n"讀取一個數值
num以字符串讀取num個字符

????????調用 io.read("a") 可從當前位置開始讀取當前輸入文件的全部內容。如果當前位置處于文件的末尾或文件為空,那么該函數返回一個空字符串

????????因為 Lua 語言可以高效地處理長字符串,所以在 Lua 語言中編寫過濾器( filter )的一種簡單技巧就是將整個文件讀取到一個字符串中 , 然后對字符串進行處理,最后輸出結果為:

t = io.read("a")						-- 讀取整個文件
t = string.gsub(t, "bad", "good")		-- 進行處理
io.write(t)							    -- 輸出結果

????????舉一個更加具體的例子,以下是一段將某個文件的內容使用 MIME 可打印字符引用編碼?進行編碼的代碼。 這種編碼方式將所有非 ASCII 字符編碼為 = xx ,其中 xx 是這個字符的十六進制 。 為保證編碼的一致性,等號也會被編碼 :

t = io.read("all")
t = string.gsub(t, "([\128-\255=])", function(c)return string.format("=%02X", string.byte(c))end)
io.write(t)

函數 string.gsub 會匹配所有的等號及非 ASCII 字符(從 128 到 255 ),并調用指定的函數完成替換。

????????調用 io.read (”l ”) 會返回當前輸入流的下一行,不包括換行符在內 ; 調用 io.read (” L ”)與之類似,但會保留換行符(如果文件中存在)。 當到達文件末尾時 ,由于已經沒有內容可以返回,該函數會返回?nil。 選項 ” l?” 是函數read 的默認參數。 我通常只在逐行處理數據的算法中使用該參數,其他情況則更傾向于使用選項 ” a ” 一次性地讀取整個文件,或者像后續介紹的按塊讀取。

????????作為面向行的輸入的一個簡單例子,以下的程序會在將當前輸入復制到當前輸出中的同時對每行進行編號

for count = 1, math.huge dolocal line = io.read("L")if line == nil then break endio.write(string.format("%6d ", count), line)
end

不過,如果要逐行迭代一個文件,那么使用 io.lines 迭代器會更簡單:

local count = 0
for line in io.lines() docount = count + 1 io.write(string.format("%6d ", count), line, "\n")
end

另一個面向行的輸入的例子參見示例 7.1 ,其中給出了一個對文件中的行進行排序的完整程序。

示例 7.1 對文件進行排序的程序

local lines = {}-- 將所有行讀取到表"lines"中
for line in io.lines() dolines[#lines + 1] = line 
end-- 排序
table.sort(lines)-- 輸出所有的行
for _, l in ipairs(lines) doio.write(l, "\n")
end

????????調用 io.read ("n") 會從當前輸入流中讀取一個數值,這也是函數 read 返回值為數值(整型或者浮點型,與 Lua 語法掃描器的規則一致) 而非字符串的唯一情況。 如果在跳過了空格后,函數 io.read 仍然不能從當前位置讀取到數值(由于錯誤的格式問題或到了文件末尾),則返回 nil。

????????除了上述這些基本的讀取模式外,在調用函數 read 時還可以用一個數字 n 作為其參數 :在這種情況下,函數 read 會從輸入流中讀取 n 個字符。 如果無法讀取到任何字符(處于文件末尾)則返回 nil ;否則,則返回一個由流中最多 n 個字符組成的字符串 。 作為這種讀取模式的示例,以下的代碼展示了將文件從 stdin 復制到 stdout 的高效方法:

while true do local block = io.read(2^13)				-- 塊大小是8KBif not block then break endio.write(block)
end

????????io.read(0) 是一個特例,它常用于測試是否到達了文件末尾 。 如果仍然有數據可供讀取,它會返回一個空字符串;否則,則返回 nil 。

????????調用函數 read 時可以指定多個選項,函數會根據每個參數返回相應的結果。 假設有一個每行由 3 個數字組成的文件:

如果想打印每一行的最大值,那么可以通過調用函數 read 來一次性地同時讀取每行中的3個數字:

6.1 	-3.23		15e12
4.3 	234			1000001
... while true dolocal n1, n2, n3 = io.read("n", "n", "n")if not n1 then break endprint(math.max(n1,n2,n3))
end

7.2 ?完整 I/O 模型

????????簡單 I/O 模型對簡單的需求而言還算適用,但對于諸如同時讀寫多個文件等更高級的文件操作來說就不夠了 。 對于這些文件操作,我們需要用到完整 I/O 模型 。

????????可以使用函數 io.open 來打開一個文件,該函數仿造了 C 語言中的函數 fopen 。 這個函數有兩個參數一個參數是待打開文件的文件名,另一個參數是一個模式 ( mode )字符串 。模式字符串包括表示只讀的 r、 表示只寫的 w (也可以用來刪除文件中原有的內容)、表示追加的 a,?以及另外一個可選的表示打開二進制文件的 b 。 函數 io.open 返回對應文件的流。 當發生錯誤時,該函數會在返回 nil 的同時返回一條錯誤信息及一個系統相關的錯誤碼 :

> print(io.open("non-existent-file", "r"))
nil     non-existent-file: No such file or directory    2
>
> print(io.open("/etcpasswd", "w"))
nil     /etcpasswd: Permission denied   13

????????檢查錯誤的一種典型方法是使用函數 assert:

local f = assert(io.open(filename, mode))

如果函數 io.open 執行失敗,錯誤信息會作為函數 assert 的第二個參數被傳人,之后函數assert 會將錯誤信息展示出來。

????????在打開文件后,可以使用方法 read 和 write 從流中讀取和向流中寫人。 它們與函數 read和 write 類似,但需要使用冒號運算符將它們當作流對象的方法來調用 。 例如,可以使用如下的代碼打開一個文件并讀取其中所有內容:

local f = assert(io.open(filename, "r"))
local t = f:read("a")
f:close()

????????I/O 庫提供了三個預定義的 C 語言流的句柄 : io.stdin、?io.stdout 和 io.stderr 。 例如:可以使用如下的代碼將信息直接寫到標準錯誤流中:

io.stderr:write(message)

????????函數 io.input 和 io.output 允許混用完整 I/O 模型和簡單 I/O 模型 。 調用無參數的 io.input()可以獲得當前輸入流,調用 io.input ( handle )可以設置當前輸入流(類似的調用同樣適用于函數 io.output ) 。 例如,如果想要臨時改變當前輸入流,可以像這樣:

local temp = io.input() 		-- 保存當前輸入流
io.input("newinput")			-- 打開一個新的當前輸入流
-- 對新的輸入流進行某些操作
io.input():close()				-- 關閉當前流
io.input(temp)					-- 恢復此前的當前輸入流

注意, io.read(args) 實際上是 io.input (): read(args)的簡寫,即函數 read 是用在當前輸入流上的 。同樣, io.write(args)是 io.output():write(args)的簡寫。

????????除了函數 io.read 外,還可以用函數 io.lines 從流中讀取內容。 正如之前的示例中展示的那樣,函數 io.lines 返回一個可以從流中不斷讀取內容的迭代器。 給函數 io.lines 提供一個文件名,它就會以只讀方式打開對應該文件的輸入流,并在到達文件末尾后關閉該輸入流。 若調用時不帶參數,函數 io.lines 就從當前輸入流讀取。 我們也可以把函數 lines當作句柄的一個方法。 此外,從 Lua 5.2 開始 函數 io.lines 可以接收和函數 io.read 一樣的參數。 例如,下面的代碼會以在 8KB為塊迭代,將當前輸入流中的內容復制到當前輸出流中:

for block in io.input():lines(2^13) doio.write(block)
end

7.3 ?其他文件操作

????????函數 io.tmpfile 返回一個操作臨時文件的句柄,該句柄是以讀/寫模式打開的 。 當程序運行結束后,該臨時文件會被自動移除(刪除)
????????函數 flush 所有緩沖數據寫入文件。 與函數 write 一樣,我們也可以把它當作 io.flush()使用 ,以刷新當前輸出流;或者把它當作方法 f:flush() 使用,以刷新流 f 。
????????函數 setvbuf 用于設置流的緩沖模式。該函數的第一個參數是一個字符串:"no" 表示無緩沖,” full ” 表示在緩沖區滿時或者顯式地刷新文件時才寫入數據, "line"表示輸出一直被緩沖直到遇到換行符或從一些特定文件(例如終端設備)中讀取到了數據。 對于后兩個選項,函數 setvbuf 支持可選的第二個參數,用于指定緩沖區大小。在大多數系統中,標準錯誤流( io.stderr)是不被緩沖的, 而標準輸出流(io.stdout )?按行緩沖。 因此,當向標準輸出中寫人了不完整的行(例如進度條)時,可能需要刷新這個輸出流才能看到輸出結果。
????????函數 seek 用來獲取和設置文件的當前位置,常常使用 f:seek(whence, offset )的形式來調用,其中參數 whence 是一個指定如何使用偏移的字符串 。 當參數 whence 取值為 ” set ”時, 表示相對于文件開頭的偏移 ;取值為"cur"時,表示相對于文件當前位置的偏移;取值為 ” end ” 時,表示相對于文件尾部的偏移。 不管 whence 的取值是什么,該函數都會以字節為單位返回當前新位置在流中相對于文件開頭的偏移
????????whence 的默認值是"cur", offset 的默認值是 0 。 因此,調用函數 file:seek()?會返回當前的位置且不改變當前位置 ; 調用函數 file:seek("set")?會將位置重置到文件開頭并返回 0 ;調用函數 file:seek("end ”) 會將當前位置重置到文件結尾并返回文件的大小。 下面的函數演示了如何在不修改當前位置的情況下獲取文件大小:

function fsize(file)local current = file:seek()			-- 保存當前位置local size = file:seek("end")		-- 獲取文件大小file:seek("set", current)			-- 恢復當前位置return size
end

????????此外,函數 os.rename 用于文件重命名,函數 os.remove 用于移除(刪除)文件。 需要注意的是,由于這兩個函數處理的是真實文件而非流,所以它們位于 os 庫而非 io 庫中 。
????????上述所有的函數在遇到錯誤時,均會返回 nil 外加一條錯誤信息和一個錯誤碼。

7.4 ?其他系統調用

????????函數 os.exit 用于終止程序的執行。 該函數的第一個參數是可選的,表示該程序的返回狀態,其值可以為一個數值( 0 表示執行成功)或者一個布爾值( true 表示執行成功);該函數的第二個參數也是可選的,當值為 true 時會關閉 Lua 狀態,并調用所有析構器釋放所占用的所有內存(這種終止方式通常是非必要的,因為大多數操作系統會在進程退出時釋放其占用的所有資源)。
????????函數 os.getenv 用于獲取某個環境變量,該函數的輸入參數是環境變量的名稱,返回值為保存了該環境變量對應值的字符串:

print(os.getenv("HOME"))		-- /home/lua

? ? ? ? 對于未定義的環境變量,該函數返回 nil 。

7.4.1 ?運行系統命令

????????函數 os.execute 用于運行系統命令,它等價于 C 語言中的函數 system 。 該函數的參數為表示待執行命令的字符串返回值為命令運行結束后的狀態。 其中,第一個返回值是一個布爾類型,當為 true 時表示程序成功運行完成;第二個返回值是一個字符串,當為 ” exit ”時表示程序正常運行結束,當為 “signal ”時表示因信號而中斷 ; 第三個返回值是返回狀態(若該程序正常終結)或者終結該程序的信號代碼。 例如,在 POSIX 和 Windows 中都可以使用如下的函數創建新目錄 :

function createDir(dirname)os.execute("mkdir " .. dirname)
end

????????另一個非常有用的函數是 io.popen 。 同函數 os.execute 一樣,該函數運行一條系統命令,但該函數還可以重定向命令的輸入/輸出,從而使得程序可以向命令中寫入或從命令的輸出中讀取。 例如,下列代碼使用當前目錄中的所有內容構建了一個表:

-- 對于POSIX系統而言,使用'ls'而非'dir'
local f = io.popen("dir /B", "r")
local dir = {}
for entry in f:lines() dodir[#dir + 1] = entry
end

其中 ,函數 io.popen 的第二個參數”r” 表示從命令的執行結果中讀取。 由于該函數的默認行為就是這樣,所以在上例中這個參數實際是可選的 。

????????下面的示例用于發送一封郵件:

local subject = "some news"
local address = "someone@somewhere.org"local cmd = string.format("mail -s '%s' '%s'", subject, address)
local f = io.popen(cmd, "w")
f:write([[ Nothing important to say. -- me ]])
f:close()

注意 , 該腳本只能在安裝了相應工具包的 POSIX 系統中運行。 上例中函數 io.popen 的第二個參數是” w ”, 表示向該命令中寫入。
????????正如我們在上面的兩個例子中看到的一樣,函數 os.execute 和 io.popen 都是功能非常強大的函數,但它們也同樣是非常依賴于操作系統的 。
????????如果要使用操作系統的其他擴展功能,最好的選擇是使用第三方庫, 比如用于基本目錄操作和文件屬性操作的 LuaFileSystem ,或者提供了 POSIX.1標準支持的 luaposix 庫。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/902918.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/902918.shtml
英文地址,請注明出處:http://en.pswp.cn/news/902918.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【開源】STM32HAL庫移植Arduino OneWire庫驅動DS18B20和MAX31850

項目開源鏈接 github主頁https://github.com/snqx-lqh本項目github地址https://github.com/snqx-lqh/STM32F103C8T6HalDemo作者 VXQinghua-Li7 📖 歡迎交流 如果開源的代碼對你有幫助,希望可以幫我點個贊👍和收藏 項目說明 最近在做一個項目…

【合新通信】浸沒式液冷光模塊與冷媒兼容性測試技術報告

一、測試背景與核心挑戰 行業需求驅動 隨著800G/1.6T光模塊功耗突破30W/端口,傳統風冷已無法滿足散熱需求,浸沒式液冷成為超算/AI數據中心的主流方案。冷媒兼容性是系統可靠性的關鍵指標,涉及材料腐蝕、光學性能、長期穩定性等維度。 核心…

Pandas中的日期時間date處理

Pandas提供了強大的日期和時間處理功能,這對于時間序列分析至關重要。本教程將介紹Pandas中處理日期時間的主要方法。包括: 日期時間數據的創建和轉換日期時間屬性的提取時間差計算和日期運算重采樣和頻率轉換時區處理基于日期時間的索引操作 Pandas中…

Vue3文件上傳組件實戰:打造高效的Element Plus上傳解決方案,可以對文件進行刪除,查看,下載功能。

在現代Web開發中,文件上傳功能是許多應用的核心需求之一。無論是企業管理系統、內容管理系統還是醫療信息系統,上傳附件的功能都至關重要。本文將分享一個基于 Vue3 和 Element Plus 實現的文件上傳組件,結合父子組件的協作,展示如何構建一個功能強大、用戶體驗友好的文件上…

AI 工程師崛起:科技浪潮下的新興力量

在當今科技迅猛發展的時代,人工智能(AI)無疑是最熱門的領域之一。隨著基礎模型的涌現和開源 / API 的普及,一種新興的職業 ——AI 工程師,正逐漸嶄露頭角。他們在 AI 技術的應用和開發中扮演著關鍵角色,其崛…

人工智能與機器學習:Python從零實現邏輯回歸模型

🧠 向所有學習者致敬! “學習不是裝滿一桶水,而是點燃一把火。” —— 葉芝 我的博客主頁: https://lizheng.blog.csdn.net 🌐 歡迎點擊加入AI人工智能社區! 🚀 讓我們一起努力,共創…

濟南國網數字化培訓班學習筆記-第二組-5節-輸電線路設計

輸電線路設計 工程設計階段劃分 35kv及以上輸變電工程勘測設計全過程 可行性研究(包括規劃、工程選站)(包括電力系統一次二次,站址選擇及工程設想,線路工程選擇及工程設想,節能降耗分析,環境…

【Linux網絡】TCP服務中IOService應用與實現

📢博客主頁:https://blog.csdn.net/2301_779549673 📢博客倉庫:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢歡迎點贊 👍 收藏 ?留言 📝 如有錯誤敬請指正! &…

Linux 怎么找Java程序的監聽的端口

Linux 怎么找Java程序的監聽的端口 1、假設你知道啟動該Java應用的進程ID (PID),可以通過以下命令查找其監聽的端口: 首先找到該Java應用的PID: ps -ef | grep xxxx-1.0-RELEASE.jar或者,如果你知道啟動命令的一部分&#xff0…

解讀《數據資產質量評估實施規則》:企業數據資產認證落地的關鍵指南

隨著“數據要素市場”建設加速,數據資產逐步成為企業核心資產之一。2024年4月,由中國質量認證中心(CQC)發布的《數據資產質量評估實施規則》(編號:CQC96-831160-2024)正式實施,為企業…

[吾愛出品] 【鍵鼠自動化工具】支持識別窗口、識圖、發送文本、按鍵組合等

鍵鼠自動化工具 鏈接:https://pan.xunlei.com/s/VOOhDZkj-E0mdDZCvo3jp6s4A1?pwdfufb# 1、增加的找圖點擊功能(不算增加,只能算縫補),各種的不完善,但是能運行。 2、因為受限于原程序的界面,…

【計算機視覺】CV實戰項目 - 基于YOLOv5的人臉檢測與關鍵點定位系統深度解析

基于YOLOv5的人臉檢測與關鍵點定位系統深度解析 1. 技術背景與項目意義傳統方案的局限性YOLOv5多任務方案的優勢 2. 核心算法原理網絡架構改進關鍵點回歸分支損失函數設計 3. 實戰指南:從環境搭建到模型應用環境配置數據準備數據格式要求數據目錄結構 模型訓練配置文…

IntelliJ IDEA 中配置 Spring MVC 環境的詳細步驟

以下是在 IntelliJ IDEA 中配置 Spring MVC 環境的詳細步驟: 步驟 1:創建 Maven Web 項目 新建項目 File -> New -> Project → 選擇 Maven → 勾選 Create from archetype → 選擇 maven-archetype-webapp。輸入 GroupId(如 com.examp…

基于javaweb的SpringBoot+MyBatis通訊錄管理系統設計與實現(源碼+文檔+部署講解)

技術范圍:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬蟲、數據可視化、小程序、安卓app、大數據、物聯網、機器學習等設計與開發。 主要內容:免費功能設計、開題報告、任務書、中期檢查PPT、系統功能實現、代碼編寫、論文編寫和輔導、論文…

EFISH-SBC-RK3588無人機地面基準站項目

一、核心技術組件解析 1. EFISH-SBC-RK3588 性能優勢:作為瑞芯微推出的高性能處理器,采用8nm 工藝,搭載ARM Cortex-A76/A55 八核架構,集成強大的 NPU(神經網絡處理單元)和 GPU(圖形處理器&…

藍橋杯 4. 卡片換位

卡片換位 原題目鏈接 題目描述 你玩過華容道的游戲嗎? 這是一個類似的,但更簡單的游戲。 看下面的 3 2 格子: --------- | A | * | * | --------- | B | | * | ---------在其中放置了 5 張牌,其中: A 表示關…

7.Excel:單元格格式

一 案例 1.案例1 2.案例2 3.案例3 二 三種基本數據類型 補充:在沒有任何格式的情況下是這樣對齊的。 1.文本 默認左對齊,文本不可參與計算。 2.數值 默認右對齊,數值計算精度是15位。 若超出15位,超出部分顯示為0。 3.邏輯值 …

高效DCDC電源芯片在運動控制器中的應用:設計考量、性能評估與可靠性分析

摘要 隨著工業自動化的深入推進,運動控制器作為自動化系統的核心組件,對電源芯片的性能提出了極高要求。高效率DCDC電源芯片憑借其卓越的電能轉換效率、優異的負載響應特性和高功率密度,在運動控制器領域得以廣泛應用。本文以國科安芯的ASP3…

Action:Update your application‘s configuration

在使用Maven項目時,有一個報錯信息是:Update your applications configuration 這類問題,就是我們的application.yml文件 或者 application.properties文件 內容哪里寫錯了 最有可能就是對齊方式有問題

Pycharm(六):可變與不可變類型

一、引用 在java中既有值傳遞,也有引用傳遞,我們思考一下在python中值的傳遞方式是哪種類型呢? 答案是引用傳遞。 概述: Python中存儲變量是需要占用內存空間的,為了更好地管理這些空間,每塊空間都是有自己的地址值的。 格式: id(變量名/值) 可以查看變量/…