9 文件读写 - M系列

本例程中介绍Lua系统函数中的文件API读写,目前可支持读txt、csv、bin文件等

本文的文件主要介绍以下 2 点:

  1. 读文件
  2. 写文件

本例程用到以下API函数,详细参考LUA 脚本API函数接口

  1. 遍历目录:list_dir(path)

  2. 打开文件:file_open(path,mode)

  3. 关闭文件:file_close()

  4. 文件大小:file_size()

  5. 文件偏移:file_seek(offset)

  6. 读文件:file_read(count)

  7. 写文件:file_write(data)

    适用范围:M系列

    例程下载链接:《M系列文件读写》(点击跳转)

9.1 读文件

本例程中的读文件,是读取 SD 根目录下的‘1.txt’文件,并用数据记录控件显示出来,用户可滑动数据记录控件,查看‘1.txt’里面的内容

屏内的盘符为"3:/xx.txt"

画面配置

画面中添加以下控件:

数据记录控件ID1:其中控件 ID1 用于显示‘1.txt’文件的内容

按钮控件 ID2:触发读取‘1.txt’文件的按钮。画面配置如下所示

Mrfile_attr

LUA 脚本

用户将1.txt放在SD卡根目录,点击按钮控件 ID2 后, 将读取 SD 目录下载的‘1.txt’文件,并添加到数据记录控件中显示;若文件不存在,则弹框提示。

基本读取流程:当按钮控件ID2按下的时候,触发触摸控件回调函数on_control_notify(),在调用自定义函数封装好的函数my_read_filedata()

  1. 打开文件:以只读的方式FA_READ(0x01)打开指定文件
  2. 获取文件大小:文件打开成功后,调用file_size() Api函数获取该文件的大小。
  3. 计算读取次数:根据文件大小,得出读取的次数。本例程采取一次读取2048个字节(注意:屏幕一次读取最大字节 <= 2048)
  4. 计算读取偏移量:每次读取需要调用file_seek(offset)定位读取位置,offset相当于已读取的字节数
  5. 读出数据:本文读取的数据,转换成字符拼接字符串显示出来,详细应用根据实际情况出来
  6. 关闭文件:文件读取完毕,将该文件关闭

代码如下所示

--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 写文件

常用的写文件操作主要有以下两种:

  1. 追加写:写在文件末尾。本例程是写在SD根目录下的‘1.txt’文件末尾,并用数据记录控件显示出来
  2. 覆盖写:清空再写入数据。本例程在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用于覆盖写功能。画面配置如下所示

Mwfile_attr

LUA脚本

当按钮控件 ID3 或按钮控件 ID5 按下的时候,触发触摸控件回调函数on_control_notify(), 在调用自定义函数 my_write_filedata()

基本写文件流程

  1. 打开文件:追加写:以读写的方式(FA_READ|FA_WRITE: 0x01|0x02) 打开 SD 卡目录下的1.txt 文件。覆盖写:以新建写入的方式(FA_CREATE_ALWAYS|FA_WRITE: 0x08|0x02) 打开SD 卡目录下的 NewFile.txt 文件,获取文件大小:文件打开成功后,若追加写模式,则执行 file_size() Api 代码段
  2. 计算写入次数:根据写入数据的大小, 得出写入的次数。 本例程采取一次写入2048 个字节(注意:屏幕一次读取最大字节 <= 2048)
  3. 计算写入的偏移量: 每次写入需要调用 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文件,并将数据写入该文件并显示出来

results matching ""

    No results matching ""