9 文件读写 - M系列
本例程中介绍Lua系统函数中的文件API读写,目前可支持读txt、csv、bin文件等
本文的文件主要介绍以下 2 点:
- 读文件
- 写文件
本例程用到以下API函数,详细参考LUA 脚本API函数接口
遍历目录:list_dir(path)
打开文件:file_open(path,mode)
关闭文件:file_close()
文件大小:file_size()
文件偏移:file_seek(offset)
读文件:file_read(count)
写文件:file_write(data)
适用范围:M系列
例程下载链接:《M系列文件读写》(点击跳转)
例程下载链接:《M系列-bin文件读写V1.0.RAR)》(点击跳转)
9.1 读文件
本例程中的读文件,是读取 SD 根目录下的‘1.txt’文件,并用数据记录控件显示出来,用户可滑动数据记录控件,查看‘1.txt’里面的内容
屏内的盘符为"3:/xx.txt"
画面配置
画面中添加以下控件:
数据记录控件ID1:其中控件 ID1 用于显示‘1.txt’文件的内容
按钮控件 ID2:触发读取‘1.txt’文件的按钮。画面配置如下所示
LUA 脚本
用户将1.txt放在SD卡根目录,点击按钮控件 ID2 后, 将读取 SD 目录下载的‘1.txt’文件,并添加到数据记录控件中显示;若文件不存在,则弹框提示。
基本读取流程:当按钮控件ID2按下的时候,触发触摸控件回调函数on_control_notify(),在调用自定义函数封装好的函数my_read_filedata()
- 打开文件:以只读的方式FA_READ(0x01)打开指定文件
- 获取文件大小:文件打开成功后,调用file_size() Api函数获取该文件的大小。
- 计算读取次数:根据文件大小,得出读取的次数。本例程采取一次读取2048个字节(注意:屏幕一次读取最大字节 <= 2048)
- 计算读取偏移量:每次读取需要调用file_seek(offset)定位读取位置,offset相当于已读取的字节数
- 读出数据:本文读取的数据,转换成字符拼接字符串显示出来,详细应用根据实际情况出来
- 关闭文件:文件读取完毕,将该文件关闭
代码如下所示
--read file data
--@file:file path
function my_read_filedata(file)
local count = 0
local read_cnt = 0
local offset = 0
local all_byte = 0
local read_byte_Tb = {}
local read_char_Tb = {}
local read_str = ''
local open_state = file_open(file, FA_READ)
if open_state == true
then
--获取当前文件大小
all_byte = file_size()
if all_byte > 0
then
read_cnt = math.modf(all_byte/WriteOnceSize)
if all_byte % WriteOnceSize > 0
then
read_cnt = read_cnt + 1
end
for i = 1, read_cnt
do
--复位读字节数组
read_byte_Tb = {}
read_char_Tb = {}
--计算读取的偏移位置
offset = (i - 1) * WriteOnceSize
local offst_result = file_seek(offset)
--文件偏移失败
if offst_result == false
then
set_text(sc_prompt, 1, 'When reading the file, an offset error occurred. please try again! ! !')
set_text_roll(sc_prompt, 1, 50)
change_child_screen(sc_prompt)
break
end
--计算本次读的个数
count = WriteOnceSize
if i == read_cnt
then
if all_byte % WriteOnceSize > 0
then
count = all_byte % WriteOnceSize
end
end
--读取字节转换为字符并拼接成字符串
read_byte_Tb = file_read(count)
if #(read_byte_Tb) > 0
then
for j = 0, #(read_byte_Tb)
do
read_char_Tb[j + 1] = string.char(read_byte_Tb[j])
end
read_str = read_str..table.concat(read_char_Tb)
elseif read_byte_Tb ==nil
then
set_text(sc_prompt, 1, ' File read error. please try again! ! !')
set_text_roll(sc_prompt, 1, 50)
change_child_screen(sc_prompt)
break;
end
end
my_ShowRecord_FromFile(read_str)
else
set_text(sc_prompt, 1, 'The file don`t exist, please check the contents of the SD car! ! !')
set_text_roll(sc_prompt, 1, 50)
change_child_screen(sc_prompt)
end
else
--关闭文件
file_close()
end
end
--用户通过触摸修改控件后,执行此回调函数。
--点击按钮控件,修改文本控件、修改滑动条都会触发此事件。
function on_control_notify(screen,control,value)
if screen == sc_ReadFlile and control == 2 and value == 0
then
sc_ShowRecord = sc_ReadFlile
if IsinsertSD == 1
then
record_clear(sc_ReadFlile, 1)
my_read_filedata(sd_dir..'/'..'1.txt')
else
set_text(sc_prompt, 1, 'Please insert the SD card, or check if the SD is compatible! ! !')
set_text_roll(sc_prompt, 1, 50)
change_child_screen(sc_prompt)
end
......
elseif screen == sc_prompt and control == 3 and value == 0
then
change_child_screen(sc_ShowRecord)
end
end
运行预览
编译下载,下载到实体屏体验,用户手动添加1.txt文件到SD卡根目录,点击读取会将1.txt文件显示到数据记录控件上
6.2 写文件
常用的写文件操作主要有以下两种:
- 追加写:写在文件末尾。本例程是写在SD根目录下的‘1.txt’文件末尾,并用数据记录控件显示出来
- 覆盖写:清空再写入数据。本例程在SD卡目录下,每次写入创建一个新的“NewFile.txt”,并写入数据
追加写和覆盖写的功能实现区别:
打开方式:file_open(path,mode)
- 追加写:mode = FA_WRITE|FA_READ
- 覆盖写:mode = FA_CREATE_ALWAYS|FA_WRITE
写入位置:file_seek(offset)
- 追加写:offset = file_size() + (i - 1) * WriteOnceSize;其中WriteOnceSize为一次写入数据的大小,i为循环写入的次数。
- 覆盖写:offset = 0
画面配置
画面中添加以下控件:
数据记录控件ID1:数据记录控件仅做显示文件内容效果
文本控件ID2:用于键盘输入数值,追加写到SD根目录下的1.txt文件中
文本控件ID4:用于键值输入数值,覆盖写在NewFile.txt文件中
按钮控件ID3:用于体现追加写功能
控件控件ID5:按钮控件5用于覆盖写功能。画面配置如下所示
LUA脚本
当按钮控件 ID3 或按钮控件 ID5 按下的时候,触发触摸控件回调函数on_control_notify(), 在调用自定义函数 my_write_filedata()
基本写文件流程
- 打开文件:追加写:以读写的方式(FA_READ|FA_WRITE: 0x01|0x02) 打开 SD 卡目录下的1.txt 文件。覆盖写:以新建写入的方式(FA_CREATE_ALWAYS|FA_WRITE: 0x08|0x02) 打开SD 卡目录下的 NewFile.txt 文件,获取文件大小:文件打开成功后,若追加写模式,则执行 file_size() Api 代码段
- 计算写入次数:根据写入数据的大小, 得出写入的次数。 本例程采取一次写入2048 个字节(注意:屏幕一次读取最大字节 <= 2048)
- 计算写入的偏移量: 每次写入需要调用 file_seek(offset)定位读取位置写入数据,按钮控件 ID3 按下时候,将文本控件 ID2 的内容写在 SD 根目录下的‘1.txt’文件末尾(追加写); 按钮控件 ID5 按下时候,将文本控件 ID4 的内容写在 SD 根目录下的‘NewFile.txt’文件(覆盖写) 代码如下所示
--split string function
--@str: string to be split
--@pat: delimiter
function my_split(str, pat)
local i = 1
local t = {}
local last_end = 0
local s, e = string.find(str, pat, 1)
while s
do
table.insert(t, string.sub(str, last_end + 1, last_end + s - last_end - 1))
last_end = e
s, e = string.find(str, pat, last_end + 1)
i = i + 1
end
if last_end <= #str
then
cap = string.sub(str, last_end + 1)
table.insert(t, cap)
end
return t
end
--get the type and length of the variable
--@data: 'string' or 'table'
--@return: len and type of data
function my_getdataLen_Type(data)
local datalen = -1
--获取数据类型
local data_type = type(data)
--计算数据长度
if data_type == 'string'
then
datalen = string.len(data)
elseif data_type == 'table'
then
datalen = #(data)
end
return datalen,data_type
end
--Write data to the end of the file
--@file:file path
--@data:The type of '@data' only be [string] or [byte array]
--@open_mode:open file mode
function my_write_filedata(file, data, open_mode)
local count = 0
local write_cnt = 0
local seek_ops = 0
local all_byte = 0
--获取待写入数据的数据类型和长度
local wrire_len, data_type = my_getdataLen_Type(data)
local write_byte_Tb = {}
local open_state = file_open(file, open_mode)
if open_state == true
then
--获取当前文件大小,仅在追加文件末尾写入执行
if open_mode == add_write
then
all_byte = file_size()
end
if wrire_len > 0
then
--计算'@data'要写多少次
write_cnt = math.modf(wrire_len / WriteOnceSize)
if wrire_len % WriteOnceSize > 0
then
write_cnt = write_cnt + 1
end
my_debug_print('Lua_debug: need write allcnt -> '..write_cnt)
for i = 1, write_cnt
do
--复位写字节数组
write_byte_Tb = {}
--计算写的位置
seek_ops = (i - 1) * WriteOnceSize +all_byte
file_seek(seek_ops)
--文件偏移失败
if offst_result == false
then
set_text(sc_prompt, 1, 'When writing the file, an offset error occurred. please try again! ! !')
set_text_roll(sc_prompt, 1, 50)
change_child_screen(sc_prompt)
break
end
my_debug_print('Lua_debug: cur seek_ops -> '..seek_ops)
--计算本次写的个数
count = WriteOnceSize
if i == write_cnt
then
if wrire_len % WriteOnceSize > 0
then
count = wrire_len % WriteOnceSize
end
end
my_debug_print('Lua_debug: cur write -> '..write_cnt..'th / wrire count '..count)
--填充写入flash的字节数组
for j = 1, count
do
if data_type == 'string'
then
--字符串类型,将每个字符转换为字节数组
write_byte_Tb[j - 1] = tonumber(string.byte(data, ((i - 1) * WriteOnceSize + j), ((i - 1) * WriteOnceSize + j)))
elseif data_type == 'table'
then
--数组类型,字节赋值
write_byte_Tb[j - 1] = data[((i - 1) * WriteOnceSize + j)]
end
end
local IswriteOK = file_write(write_byte_Tb)
if IswriteOK == false
then
i = i - 1
end
end
end
else
set_text(sc_prompt, 1, 'The file don`t exist, please check the contents of the SD car! ! !')
set_text_roll(sc_prompt, 1, 50)
change_child_screen(sc_prompt)
end
--关闭文件
file_close()
end
--用户通过触摸修改控件后,执行此回调函数。
--点击按钮控件,修改文本控件、修改滑动条都会触发此事件。
function on_control_notify(screen,control,value)
......
elseif screen == sc_WriteFile and control == 3 and value == 0
then
sc_ShowRecord = sc_WriteFile
if IsinsertSD == 1
then
record_clear(sc_WriteFile, 1)
local str = '\n'..get_text(sc_WriteFile, 2)
my_write_filedata(sd_dir..'/'..'1.txt', str, add_write)
my_read_filedata(sd_dir..'/'..'1.txt')
local allrecordcnt = record_get_count(sc_WriteFile, 1)
record_setoffset(sc_WriteFile, 1, allrecordcnt - 1)
record_select(sc_WriteFile , 1, allrecordcnt - 1)
else
set_text(sc_prompt, 1, 'Please insert the SD card, or check if the SD is compatible! ! !')
set_text_roll(sc_prompt, 1, 50)
change_child_screen(sc_prompt)
end
elseif screen == sc_WriteFile and control == 5 and value == 0
then
sc_ShowRecord = sc_WriteFile
if IsinsertSD == 1
then
record_clear(sc_WriteFile, 1)
cnt = cnt + 1
local str = (cnt + 1)..'th write -> '..get_text(sc_WriteFile, 4)
my_write_filedata(sd_dir..'/'..'NewTxtFile.txt', str, over_write)
my_read_filedata(sd_dir..'/'..'NewTxtFile.txt')
else
set_text(sc_prompt, 1, 'Please insert the SD card, or check if the SD is compatible! ! !')
set_text_roll(sc_prompt, 1, 50)
change_child_screen(sc_prompt)
end
elseif screen == sc_prompt and control == 3 and value == 0
then
change_child_screen(sc_ShowRecord)
end
end
运行预览
编译下载,下载到实体屏体验,用户手动添加1.txt文件到SD卡根目录,体检追加写功能,会将字符串写到1.txt文件的尾部,并显示到数据记录控件上。同理,覆盖写功能,每次会覆盖创建NewFile.txt文件,并将数据写入该文件并显示出来