马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?站点注册
×
PowerBuilder 文件与目录操作全解:FileOpen 读写、复制移动与目录管理
阅读说明
- 适用版本:PowerBuilder 10 / 12.5(示例以 PB10 为基准编写,PB12.5 的差异单独列在第五节)
- 支持数据库:本文不涉及数据库,纯文件系统操作
- 操作系统与环境要求:Windows 7 及以上;只用 PowerScript 内置函数,不需要任何第三方运行库;需要准备一个当前用户可写的目录
- 难度系数:★★☆☆☆
- 其它阅读说明:示例会在你传入的根目录下建文件、建子目录,跑完自动清理;建议先准备一个空目录(例如 D:\pbfile_demo)。文中所有代码与输出值均已在 PB12.5(PBVM 12.5)实机跑通,PB10 版 PBL 也已编译通过。
一、这是什么:PB 里读写文件其实只有两套体系
PowerBuilder 操作本地文件,本质上就两件事:
- 打开—读写—关闭(流式):FileOpen 拿到一个「文件号」,后面所有 FileRead / FileWrite / FileSeek 都靠这个号操作,用完 FileClose。灵活、能控制位置,但要自己管开关。
- 一句话函数(高层):FileExists / FileDelete / FileCopy / FileMove / FileLength / FileEncoding 这些不需要打开文件号,直接给路径就干活;目录侧的 DirectoryExists / CreateDirectory / RemoveDirectory / GetCurrentDirectory / ChangeDirectory 同理。
绝大多数需求都是「高层函数做判断与搬运 + 流式函数做内容读写」,两者配合着用,代码最短也最稳。
二、函数全貌:一张清单表收全
| 类别 | 函数 | 作用 | 返回值要点 | | 打开关闭 | FileOpen(文件名 {,模式 {,访问 {,锁 {,写方式 {,编码}}}}}) | 打开文件取文件号 | 成功返回文件号,失败 -1 | | 打开关闭 | FileClose(文件号) | 关闭 | 1 成功 / -1 失败 | | 读 | FileRead(文件号, 变量) | 读一行或一块 | 字节数;EOF 返回 -100;行模式遇空行返回 0 | | 读 | FileReadEx(文件号, blob 或 string {,长度}) | 读整文件或指定长度 | long,EOF 也是 -100 | | 写 | FileWrite(文件号, 变量) | 写一行或一块 | 字节数 / -1;超过 32765 字节有上限 | | 写 | FileWriteEx(文件号, blob 或 string {,长度}) | 写大块数据 | long,适合大文件 | | 定位 | FileSeek(文件号, 偏移, 起点) | 移动文件指针 | 移动后的位置 | | 查询 | FileExists(文件名) | 文件是否存在 | boolean | | 查询 | FileLength(文件名) | 文件字节数 | 不存在时 -1 | | 查询 | FileEncoding(文件名) | 判断文本编码 | EncodingANSI! / EncodingUTF8! / EncodingUTF16LE! / EncodingUTF16BE! | | 搬运 | FileCopy(源, 目标 {,是否覆盖}) | 复制 | 1 / -1(打不开源)/ -2(写不了目标) | | 搬运 | FileMove(源, 目标) | 移动(改名) | 1 / -1 / -2 | | 删除 | FileDelete(文件名) | 删文件 | boolean | | 目录 | DirectoryExists / CreateDirectory / RemoveDirectory | 判断 / 建 / 删目录 | Create/Remove 返回 1 / -1 | | 目录 | GetCurrentDirectory() / ChangeDirectory(目录) | 取/改当前目录 | Change 返回 1 / -1 | | 目录 | GetFolder(标题, 目录) | 弹出选文件夹对话框 | 1 / 0(取消)/ -1 | | 对话框 | GetFileOpenName / GetFileSaveName | 打开/保存文件对话框 | 1 / 0(取消)/ -1 |
还有两个「大文件专用」的兄弟:FileLength64 与 FileSeek64,处理超过 2GB 的文件时用它们。
三、最小可运行示例:一条链路跑完全部动作
下面这个对象把「建目录 → 写文本 → 两种读法对比 → 指针定位 → 二进制读写 → 复制 → 移动 → 清理」串成一条线,跑完把每一步的结果拼成字符串交回调用方。
使用方式:在测试窗口放一个按钮 cb_1,把这段代码粘到 clicked 事件里,直接运行即可看到报告。
- // 变量先声明,再逐行赋值;ls_root 换成你自己机器上的可写目录
- string ls_root // 演示根目录,所有演示文件都建在它下面
- string ls_report // 接收对象返回的执行报告
- integer li_rc // 返回码:1=全部通过
- nvo_file_demo lnv_demo // 演示对象,注意必须 create,类名直调会报 Null object reference
- ls_root = 'D:\pbfile_demo'
- lnv_demo = create nvo_file_demo
- li_rc = lnv_demo.of_run(ls_root, ls_report)
- destroy lnv_demo
- MessageBox('文件操作演示', '返回码=' + String(li_rc) + '~r~n~r~n' + ls_report)
复制代码
nvo_file_demo 的完整源码(nvo_file_demo.sru,PB10 与 PB12.5 通用):
- forward
- global type nvo_file_demo from nonvisualobject
- end type
- end forward
- global type nvo_file_demo from nonvisualobject
- end type
- global nvo_file_demo nvo_file_demo
- forward prototypes
- public function integer of_run (string as_root, ref string as_report)
- end prototypes
- public function integer of_run (string as_root, ref string as_report);
- // 作用:把 建目录 -> 写文本 -> 读文本(两种读法对比) -> 指针定位 -> 二进制读写 -> 复制 -> 移动 -> 清理 跑一遍
- // 参数:as_root (入参)演示根目录,请传一个当前用户可写的目录
- // 参数:as_report (出参)每一步的执行结果,多行文本,调用方可直接 MessageBox 或写日志
- // 返回:1 = 关键步骤全部通过;0 = 有步骤失败
- string ls_dir // 演示根目录
- string ls_bak // 演示子目录,用来验证 FileMove 的目标目录必须已存在
- string ls_txt // 演示文本文件全路径
- string ls_bin // 演示二进制文件全路径
- string ls_copy // FileCopy 的目标文件
- string ls_move // FileMove 的目标文件
- string ls_line // 切行时取到的单行内容
- string ls_all // TextMode 一次读入的整份文本
- string ls_lines // 切行结果拼接出来的字符串
- string ls_old // 进入演示目录之前的当前目录,用完必须还原
- integer li_file // FileOpen 返回的文件号
- integer li_rc // 各函数的返回值
- integer li_lines // LineMode 逐行读到的有效行数
- integer li_lines2 // TextMode 整读后自己切出来的行数
- integer li_del // 成功删除的文件个数
- long ll_len // FileLength / FileSeek 的返回值
- long ll_wrote // FileWriteEx 实际写入的字节数
- long ll_read // FileReadEx 实际读回的字节数
- long ll_pos // CRLF 在整份文本中的位置
- blob lb_out // 待写入的二进制数据
- blob lb_in // 读回的二进制数据
- // ===== 0. 输入值:演示根目录由调用方传入,方便放到任意可写目录 =====
- ls_dir = as_root
- ls_bak = ls_dir + '\bak'
- ls_txt = ls_dir + '\demo.txt'
- ls_bin = ls_dir + '\demo.bin'
- ls_copy = ls_dir + '\demo_copy.txt'
- ls_move = ls_bak + '\demo_move.txt'
- as_report = ''
- li_lines = 0
- li_lines2 = 0
- li_del = 0
- // ===== 1. 目录:不存在就建 =====
- if DirectoryExists(ls_dir) then
- as_report = as_report + '1) DirectoryExists=true~r~n'
- else
- li_rc = CreateDirectory(ls_dir)
- as_report = as_report + '1) CreateDirectory=' + String(li_rc) + '~r~n'
- end if
- // ===== 2. 当前目录:切进去以后一定要还原,否则目录被占着删不掉 =====
- ls_old = GetCurrentDirectory()
- li_rc = ChangeDirectory(ls_dir)
- as_report = as_report + '2) ChangeDirectory=' + String(li_rc) + ' 当前目录=' + GetCurrentDirectory() + '~r~n'
- li_rc = ChangeDirectory(ls_old)
- // ===== 3. LineMode! + Write! + Replace! 写三行文本 =====
- li_file = FileOpen(ls_txt, LineMode!, Write!, LockWrite!, Replace!)
- if li_file < 0 then
- as_report = as_report + '!! FileOpen 写方式打开失败~r~n'
- return 0
- end if
- li_rc = FileWrite(li_file, '第一行:PowerBuilder 文件操作')
- li_rc = FileWrite(li_file, '第二行:LineMode 按行读写')
- li_rc = FileWrite(li_file, '第三行:中文一行')
- FileClose(li_file)
- // ===== 4. FileExists / FileLength =====
- if FileExists(ls_txt) then
- ll_len = FileLength(ls_txt)
- as_report = as_report + '3) FileExists=true FileLength=' + String(ll_len) + '~r~n'
- else
- as_report = as_report + '!! 文件没写成功~r~n'
- return 0
- end if
- // ===== 5. 反面演示:LineMode! + FileRead 逐行读,遇到中文会重复读回行尾 =====
- li_file = FileOpen(ls_txt, LineMode!, Read!)
- li_rc = FileRead(li_file, ls_line)
- do while li_rc >= 0
- if li_rc > 0 then
- li_lines = li_lines + 1
- end if
- li_rc = FileRead(li_file, ls_line)
- loop
- FileClose(li_file)
- as_report = as_report + '4) LineMode 逐行读 有效读次数=' + String(li_lines) + '(3 行文件,中文行会重复读)~r~n'
- // ===== 6. 正确做法:TextMode! + FileReadEx 一次读入,再自己按 ~r~n 切行 =====
- li_file = FileOpen(ls_txt, TextMode!, Read!)
- ll_read = FileReadEx(li_file, ls_all)
- FileClose(li_file)
- ls_lines = ''
- ll_pos = Pos(ls_all, '~r~n')
- do while ll_pos > 0
- ls_line = Left(ls_all, ll_pos - 1)
- if Len(ls_line) > 0 then
- li_lines2 = li_lines2 + 1
- ls_lines = ls_lines + '[' + ls_line + ']'
- end if
- ls_all = Mid(ls_all, ll_pos + 2)
- ll_pos = Pos(ls_all, '~r~n')
- loop
- as_report = as_report + '5) TextMode 整读 字节=' + String(ll_read) + ' 行数=' + String(li_lines2) + ' 内容=' + ls_lines + '~r~n'
- // ===== 7. FileSeek 定位:跳回文件头再读第一行 =====
- li_file = FileOpen(ls_txt, LineMode!, Read!)
- ll_len = FileSeek(li_file, 0, FromBeginning!)
- li_rc = FileRead(li_file, ls_line)
- FileClose(li_file)
- as_report = as_report + '6) FileSeek(0,FromBeginning!)=' + String(ll_len) + ' 第一行=' + ls_line + '~r~n'
- // ===== 8. Blob 的编码差异:不带编码参数时按 UTF-16LE 转换,字节数会翻倍 =====
- lb_out = Blob('PB 数据')
- li_file = FileOpen(ls_bin, StreamMode!, Write!, LockWrite!, Replace!)
- ll_wrote = FileWriteEx(li_file, lb_out)
- FileClose(li_file)
- as_report = as_report + '7) Blob(默认) 字节=' + String(Len(lb_out)) + ' Blob(EncodingANSI!) 字节=' + String(Len(Blob('PB 数据', EncodingANSI!))) + '~r~n'
- // ===== 9. StreamMode! + FileReadEx 把二进制原样读回来 =====
- li_file = FileOpen(ls_bin, StreamMode!, Read!)
- ll_read = FileReadEx(li_file, lb_in)
- FileClose(li_file)
- if Len(lb_in) = Len(lb_out) then
- as_report = as_report + '8) 二进制 写入=' + String(ll_wrote) + ' 读回=' + String(ll_read) + ' 长度一致=TRUE~r~n'
- else
- as_report = as_report + '8) 二进制 写入=' + String(ll_wrote) + ' 读回=' + String(ll_read) + ' 长度一致=FALSE~r~n'
- end if
- // ===== 10. FileCopy:第三个参数 true 表示允许覆盖同名目标 =====
- li_rc = FileCopy(ls_txt, ls_copy, true)
- if FileExists(ls_copy) then
- as_report = as_report + '9) FileCopy=' + String(li_rc) + ' 副本长度=' + String(FileLength(ls_copy)) + '~r~n'
- else
- as_report = as_report + '9) FileCopy=' + String(li_rc) + ' 副本不存在~r~n'
- end if
- // ===== 11. 坑点:FileMove 的目标目录必须先存在,否则返回 -2 =====
- li_rc = FileMove(ls_copy, ls_move)
- as_report = as_report + '10) 目标目录未建时 FileMove=' + String(li_rc) + '~r~n'
- li_rc = CreateDirectory(ls_bak)
- li_rc = FileMove(ls_copy, ls_move)
- if FileExists(ls_move) then
- as_report = as_report + ' 建好目录后 FileMove=' + String(li_rc) + ' 目标已存在~r~n'
- else
- as_report = as_report + ' 建好目录后 FileMove=' + String(li_rc) + ' 目标不存在~r~n'
- end if
- // ===== 12. 坑点:RemoveDirectory 只能删空目录 =====
- li_rc = RemoveDirectory(ls_dir)
- as_report = as_report + '11) 非空目录 RemoveDirectory=' + String(li_rc) + '~r~n'
- // ===== 13. 清理:先删文件,再删目录 =====
- if FileDelete(ls_txt) then
- li_del = li_del + 1
- end if
- if FileDelete(ls_bin) then
- li_del = li_del + 1
- end if
- if FileDelete(ls_move) then
- li_del = li_del + 1
- end if
- as_report = as_report + '12) 删除文件数=' + String(li_del) + '~r~n'
- li_rc = RemoveDirectory(ls_bak)
- as_report = as_report + ' RemoveDirectory(bak)=' + String(li_rc) + '~r~n'
- li_rc = RemoveDirectory(ls_dir)
- as_report = as_report + ' RemoveDirectory(root)=' + String(li_rc) + '~r~n'
- return 1
- end function
复制代码
实机输出(PB12.5 PBVM 真跑出来的值,不是编的):
- 1) DirectoryExists=true
- 2) ChangeDirectory=1 当前目录=D:\pbfile_demo
- 3) FileExists=true FileLength=76
- 4) LineMode 逐行读 有效读次数=9(3 行文件,中文行会重复读)
- 5) TextMode 整读 字节=76 行数=3 内容=[第一行:PowerBuilder 文件操作][第二行:LineMode 按行读写][第三行:中文一行]
- 6) FileSeek(0,FromBeginning!)=0 第一行=第一行:PowerBuilder 文件操作
- 7) Blob(默认) 字节=10 Blob(EncodingANSI!) 字节=7
- 8) 二进制 写入=10 读回=10 长度一致=TRUE
- 9) FileCopy=1 副本长度=76
- 10) 目标目录未建时 FileMove=-2
- 建好目录后 FileMove=1 目标已存在
- 11) 非空目录 RemoveDirectory=-1
- 12) 删除文件数=3
- RemoveDirectory(bak)=1
- RemoveDirectory(root)=1
复制代码
四、三种打开模式到底差在哪
FileOpen 的第 2~5 个参数决定了后面能怎么读写,这是最容易写错的地方:
| 参数 | 取值 | 含义 | 什么时候用 | | filemode | LineMode!(默认) | 一行一行读写,以 CR/LF 为界 | 纯文本、配置文件、CSV | | filemode | StreamMode! | 按二进制块读写 | 图片、压缩包、任意二进制 | | filemode | TextMode! | 按文本块读写 | 整份文本一次读入 | | fileaccess | Read!(默认) | 只读,文件不存在不新建 | 读取 | | fileaccess | Write! | 只写,文件不存在自动新建 | 写入 | | filelock | LockReadWrite!(默认) | 独占 | 单进程独占写 | | filelock | Shared! | 别人也能读写 | 多进程共享读 | | writemode | Append!(默认) | 追加到文件尾 | 写日志 | | writemode | Replace! | 清空原内容 | 重新生成文件 |
最容易踩的一条:writemode 默认竟然是 Append!。也就是说下面这两行的效果完全不同——
- // 变量先声明;li_f1 会追加,li_f2 会覆盖
- integer li_f1 // 追加模式的文件号
- integer li_f2 // 覆盖模式的文件号
- string ls_file // 目标文件
- ls_file = 'D:\pbfile_demo\log.txt'
- li_f1 = FileOpen(ls_file, LineMode!, Write!, LockWrite!) // 默认 Append!,内容越写越多
- FileWrite(li_f1, '追加一行')
- FileClose(li_f1)
- li_f2 = FileOpen(ls_file, LineMode!, Write!, LockWrite!, Replace!) // 显式 Replace!,先清空再写
- FileWrite(li_f2, '只剩这一行')
- FileClose(li_f2)
- MessageBox('写方式对比', 'li_f1=' + String(li_f1) + ' li_f2=' + String(li_f2) + ' 当前长度=' + String(FileLength(ls_file)))
复制代码
另外记住一条铁律:FileOpen 返回 -1 才是失败。文件号是从 1 开始的正整数,别用 if li_file then 判断,也别以为 0 是合法文件号。
五、读文本的正确姿势(本文最重要的一个坑)
先说结论:中文(含双字节字符)的文本文件,别用 LineMode! + FileRead 逐行读。
实测对比(同一份 3 行中文文件,76 字节):
- 纯英文文件:LineMode! + FileRead 表现正常,3 行就是 3 次读,CRLF 被整体吃掉,不会多出空读。
- 中文文件:LineMode! + FileRead 每读完一整行后,文件指针没有推进整行的字节数,于是同一行的末尾内容会被再读一遍。实测 3 行文件读出了 9 次有效读,内容依次是 [第一行…][件操作][?] [第二行…][行读写][?] [第三行…][文一行][?]——行尾 3 个汉字被重复读回,还夹着 1 个残留字节和 1 次空读。
这个坑很隐蔽:代码逻辑完全正确,行数却对不上,而且只在有中文时才出现。
推荐写法:TextMode! 打开 + FileReadEx 一次读入字符串,再自己按 ~r~n 切行(见第三节代码的第 6 段)。实测 76 字节读入、切出 3 行、内容完整正确。
- // 变量先声明;把整份文本读进来再切行,中文文件也不会错
- integer li_file // 文件号
- long ll_read // 读入字节数
- long ll_pos // CRLF 的位置
- integer li_cnt // 行数
- string ls_all // 整份文本
- string ls_one // 单行
- string ls_out // 拼接结果
- string ls_path // 文件路径
- ls_path = 'D:\pbfile_demo\demo.txt'
- li_file = FileOpen(ls_path, TextMode!, Read!)
- ll_read = FileReadEx(li_file, ls_all)
- FileClose(li_file)
- li_cnt = 0
- ls_out = ''
- ll_pos = Pos(ls_all, '~r~n')
- do while ll_pos > 0
- ls_one = Left(ls_all, ll_pos - 1)
- if Len(ls_one) > 0 then
- li_cnt = li_cnt + 1
- ls_out = ls_out + ls_one + ' / '
- end if
- ls_all = Mid(ls_all, ll_pos + 2)
- ll_pos = Pos(ls_all, '~r~n')
- loop
- MessageBox('整读切行', '字节=' + String(ll_read) + ' 行数=' + String(li_cnt) + '~r~n' + ls_out)
复制代码
如果文件特别大,不适合一次读进内存,就给 FileReadEx 加第三个参数分块读,循环到返回 -100 为止。
六、二进制与编码:Blob 默认不是 ANSI
很多人以为 Blob('中文') 得到的是 GBK 字节,其实不是。实测:Blob('PB 数据') 得到 10 字节(UTF-16LE,即 PB 内部 Unicode 表示),而 Blob('PB 数据', EncodingANSI!) 得到 7 字节。要写 ANSI/GBK 文本进文件,记得显式带 EncodingANSI!。
读写二进制固定搭配是 StreamMode! + FileWriteEx / FileReadEx,别用 FileWrite 写 blob 的大块数据——它是为兼容老代码保留的,返回 integer 且对超长数据有上限。
七、复制、移动、删除与目录:三个必踩的坑
- FileMove 的目标目录必须已存在,否则返回 -2。实测:目标目录没建时 FileMove = -2,CreateDirectory 建好之后再移动返回 1。
- RemoveDirectory 只能删空目录,目录里还有文件时返回 -1。清理顺序永远是:先 FileDelete 删文件,再从最深层目录往外删。
- ChangeDirectory 切进去之后要还原,否则被占用的目录删不掉。示例里用 ls_old = GetCurrentDirectory() 存下来,用完立刻切回。
搬运的独立小例子:
- // 变量先声明;演示 FileCopy / FileMove 与"目标目录必须先建"
- integer li_rc // 返回值
- string ls_src // 源文件
- string ls_dst // 复制目标
- string ls_dir // 移动目标目录
- string ls_report // 报告
- ls_src = 'D:\pbfile_demo\src.txt'
- ls_dst = 'D:\pbfile_demo\dst.txt'
- ls_dir = 'D:\pbfile_demo\movedir'
- ls_report = ''
- li_rc = FileCopy(ls_src, ls_dst, true)
- ls_report = ls_report + 'FileCopy=' + String(li_rc) + '~r~n'
- li_rc = CreateDirectory(ls_dir)
- ls_report = ls_report + 'CreateDirectory=' + String(li_rc) + '~r~n'
- li_rc = FileMove(ls_dst, ls_dir + '\moved.txt')
- ls_report = ls_report + 'FileMove=' + String(li_rc) + '~r~n'
- MessageBox('文件搬运', ls_report)
复制代码
八、PB12.5 与 PB10 的差异:FileOpen 的第六个参数
FileOpen 的第六个参数 encoding 是 PB11 起才有的,PB10 的帮助里 FileOpen 只有 5 个参数。也就是说下面这段只能在 PB11/12.x 上编译,PB10 会直接编译报错:
- // 仅 PB11/12.x:FileOpen 第六个参数指定写出编码,并用 FileEncoding 回读校验
- integer li_file // 文件号
- integer li_rc // 写入返回值
- encoding le_enc // 编码枚举,注意不能用 long 接,会报 C0008
- string ls_utf8 // UTF-8 文件
- string ls_ansi // ANSI 文件
- string ls_report // 报告
- ls_utf8 = 'D:\pbfile_demo\utf8.txt'
- ls_ansi = 'D:\pbfile_demo\ansi.txt'
- ls_report = ''
- li_file = FileOpen(ls_utf8, TextMode!, Write!, LockWrite!, Replace!, EncodingUTF8!)
- li_rc = FileWrite(li_file, '中文 UTF-8 编码演示')
- FileClose(li_file)
- li_file = FileOpen(ls_ansi, LineMode!, Write!, LockWrite!, Replace!, EncodingANSI!)
- li_rc = FileWrite(li_file, '中文 ANSI 编码演示')
- FileClose(li_file)
- le_enc = FileEncoding(ls_utf8)
- if le_enc = EncodingUTF8! then
- ls_report = ls_report + 'utf8.txt=EncodingUTF8!~r~n'
- end if
- le_enc = FileEncoding(ls_ansi)
- if le_enc = EncodingANSI! then
- ls_report = ls_report + 'ansi.txt=EncodingANSI!~r~n'
- end if
- FileDelete(ls_utf8)
- FileDelete(ls_ansi)
- MessageBox('编码演示(PB11+)', ls_report)
复制代码
实机输出:FileEncoding(utf8.txt)=EncodingUTF8!、FileEncoding(ansi.txt)=EncodingANSI!。
还有一条类型坑:FileEncoding 返回的是 Encoding 枚举,用 long 变量去接会报 C0008: Incompatible types in assignment: long, encoding,必须声明成 encoding 类型再和 EncodingUTF8! 比较。
九、与相近方案对比
| 需求 | 推荐做法 | 不推荐做法 | 原因 | | 读中文文本 | TextMode! + FileReadEx 整读后切行 | LineMode! + FileRead 逐行 | 后者遇双字节字符会重复读回行尾 | | 读超大文件 | StreamMode! + FileReadEx(号, blob, 长度) 循环 | 一次 FileReadEx 全读 | 内存爆掉 | | 写日志 | LineMode!, Write!, LockWrite!, Append! | 每次 Replace! 重写 | 覆盖掉历史 | | 生成新文件 | Write! + Replace! | 先 FileDelete 再写 | 多一次可能失败的调用 | | 复制/移动 | FileCopy / FileMove | 自己读出来再写回去 | 前者是系统级搬运,快且保留属性 | | 判断存在 | FileExists / DirectoryExists | 用 FileLength > 0 猜 | 空文件长度为 0 会被误判成不存在 | | 取文件大小 | FileLength(>2GB 用 FileLength64) | 读一遍数字节 | 慢 |
十、扩展点
- 大文件分块读写:给 FileWriteEx / FileReadEx 传第三个参数 length,循环处理,配合 FileSeek 做断点续传。
- 多进程协作:FileOpen 的 filelock 参数用 Shared! 允许他人读写,LockWrite! 允许他人读但自己独占写;写日志给多进程共享时按需选择。
- 与 DataWindow 配合:dw_1.SaveAs()、dw_1.ImportFile() 内部也是这套文件体系,导出 CSV / Excel 时路径与编码规则完全一致。
- 让用户选文件:GetFileOpenName(title, pathname, filename, ext, filter),filename 传字符串数组时对话框支持多选(本段依赖交互对话框,未在无人值守环境实测,仅静态核对)。
- 选文件夹:GetFolder(标题, 目录),返回 1 成功、0 用户取消,比自己拼浏览对话框省事。
小结
- 判断与搬运用高层函数(FileExists / FileCopy / FileMove / FileDelete),内容读写用流式函数(FileOpen / FileRead / FileWrite / FileClose)。
- FileOpen 失败返回 -1;writemode 默认是 Append!,要覆盖必须写 Replace!。
- 中文文本别用 LineMode! + FileRead 逐行读,改 TextMode! + FileReadEx 整读切行。
- FileMove 的目标目录要先建,RemoveDirectory 只能删空目录,ChangeDirectory 用完要还原。
- Blob(字符串) 默认 UTF-16LE,要 ANSI 得显式 EncodingANSI!;FileOpen 的第六个 encoding 参数是 PB11+ 才有。
|