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.mzph.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(变量名/值) 可以查看变量/…