在 lisp 中使用 FSO 对象

FileSystemObject 文件系统对象,简称 FSO,它是微软提供的在 windows 中操作本地文件和文件夹的 ActiveX 技术支持,在 win32/64 系统中通用。
FSO 对象模型简单易用。可以实现文件(夹)的创建、改变、移动和删除等常见操作,也可以获取文件(夹)的名称、大小、属性、创建日期或最近修改日期等信息。
通过 FSO 对象模型还可以获取当前系统驱动器信息,如驱动器的种类、序列号、磁盘剩余空间等。
无论是 AutoLisp 还是 VisualLisp 对文件(夹)和驱动器的操作都是弱项,引入 FSO 对象模型,可以让你的程序更加强大。

创建 FSO 对象

FSO 对象被封装在 scrrun.dll 里,类型库名称为 scripting.FileSystemObject。在 Lisp 里一般通过后期绑定的方式,创建 FSO 对象。

;;示例1
(setq FSO (vlax-create-object "scripting.FileSystemObject"))

可以通过 vlax-dump-object 函数来查询 FSO 对象的属性和方法。使用完毕,建议释放 FSO 对象:

;;示例2
(vlax-dump-object FSO t) ;;查询对象的属性和方法
(vlax-release-object FSO) ;;释放对象(结束进程)

FSO 的属性

FSO 只有一个 Drives 属性,该属性是 Drive 对象的集合。

;;示例3
(setq DRs (vlax-get-property FSO 'Drives))

一般来说,一个对象集合均具备 count 属性和 Item 属性,通过指定 Item 属性的值可以调用该集合的对象,但是 FSO 对象很奇怪,只能用 vlax-for 来遍历对象所有条目,也就是下面的语句将返回一个 Automation 错误。

;;示例4
(vlax-get-property DRs 'Item 1)  ;;这是一个错误的示例

上面的代码得不到 Drives 集合的第二个 Drive 对象。解决的办法也很简单,用 vlax-for 将对象集合转为一个 list 表,这样可用 nth 来提取指定的 Item 项。下面定义一个 Lisp 函数来代替 Item 功能:

;;示例5
(defun GetDrive(n / d)
(vlax-for x (vlax-get FSO 'Drives)(setq d (cons x d)))
(nth n (reverse d))
)

例如,返回第一个驱动器的 Drive 对象(一般是 C: 盘),可以用:(GetDrive 0) 来表示

若需得到一台计算机的驱动器 list 列表,可提取 Drive 对象的 path 属性,代码如下:

;;示例6
(setq dr '())
(vlax-for x (vlax-get FSO 'Drives)(setq dr (cons(vlax-get x 'Path) dr)))

用 AutoLisp 的 findfile 函数也可以判断驱动是否存在:

;;示例7
(findfile "f:") 

当 findfile 函数返回 nil 时表示 f: 盘不存在,但是在 win8/10 系统里,如果没用管理员模式打开 AutoCAD,则 C: 即使存在,也会返回 nil;使用 FSO 对象则绕过了管理员账户,对查询 C: 盘并不影响:

;;示例8
(vlax-invoke FSO 'DriveExists "c:") ;; 返回 -1 代表存在

在程序注册加密时,我们通常获取驱动器的序列号作为机器码,下面代码将返回第2个驱动器的序列号:

;;示例9
(vlax-get (GetNs 1) 'SerialNumber)

也可以调用 FSO 的方法来获取指定驱动器的序列号,代码如下:

;;示例10
(vlax-get (vlax-invoke FSO 'GetDrive "d:") 'SerialNumber)

所以说,FSO 提供的方法是冗余的,通过不同的途径可以获取相同的结果。采用 FSO 的方法来获取序列号时,应判断 d: 是否存在:

;;示例11
(vlax-invoke FSO 'DriveExists "d:")

FSO 的方法

FSO 对象提供了 26 个方法,主要方法说明和参数如下:

方法与参数功能说明
BuildPath(path,name)连接一个名字到一个路径,与 strcat 函数类似
CreateTextFile(strfile,blnoverwrite)创建一个空文本文件
CreateFolder(strfolder)创建一个空的文件夹
CopyFile(source,destination[,overwrite])将一个或多个文件从某位置复制到另一位置
CopyFolder(source,destination[,overwrite])将文件夹从某位置复制到另一位置
DeleteFile(strfile,force)删除一个文件
DeleteFolder(strfolder,force)删除一个文件夹
DriveExists(drivespec)判断一个驱动器是否存在,返回 0 不存在,-1 存在
FileExists(strfile)判断文件是否存在
FolderExists(strfolder)判断文件夹是否存在
GetAbsolutePathName(pathspec)从路径中返回一个完整、明确的路径(类似Dos命令)
GetDriveName(path)返回包含指定路径的驱动器名字的字符串
GetDrive(drivespec)返回与指定路径中的驱动器相对应的 Drive 对象
GetBaseName(path)返回包含路径中最后部件的基本名字(去掉任何文件扩展名)的字符串
GetExtensionName(path)获取文件后缀名
GetFileName(pathspec)指定路径中的最后部件,该路径不是驱动器说明的一部分
GetFile(filespec)返回和指定路径中文件相对应的 File 对象
GetFolder(folderspec)返回和指定路径中文件夹相对应的 Folder 对象
GetParentFolderName(path)返回包含指定路径最后部件父文件夹名字的字符串
GetSpecialFolder(folderspec)特殊文件夹,folderspec 常数 0:windows 文件夹, 1:System 文件夹, 2:临时文件夹
GetTempName随机产生的临时文件或文件夹名字,该名字在执行某些操作时有用
OpenTextFile(filename)打开一指定的文本文件,并返回一个 TextStream 对象
MoveFile(source,destination)将一个或多个文件从某位置移动到另一位置
MoveFolder(source,destination)将一个或多个文件夹从某位置移动到另一位置

BuildPath 方法

参数: (path,name)
功能:在已有的路径 path 上增添名字为 name 的文件或文件夹,如果需要,则增添路径分隔符 :

;;示例12
(vlax-invoke FSO 'BuildPath "d:\\aa" "abc")

以上示例将返回一个字符串: "d:\\aa\\abc" ,程序自动在 aa 后面添加了一个 "\\";这个方法并未在磁盘里建立真正文件或文件夹,仅仅是返回了一个路径,运行效果相当于 strcat 函数:

;;示例13
(strcat "d:\\aa" "\\" "abc")

尚不明白这个方法有什么其他特别作用,它并不判断路径是否存在。

CopyFolder 方法:

参数: (source,destination [,overwrite])
功能:从指定的源文件夹 source(可以包含通配符)中复制一个或多个文件夹到指定的目标文件夹 destination,复制包含了源文件夹中的所有文件及子文件夹,overwrite 默认为 true,覆盖模式。

;;示例14
(vlax-invoke FSO 'CopyFolder "d:\\fff" "d:\\aa\\")
;;参数 "d:\\aa\\" 和 "d:\\aa" 结果是不同的

注意,destination 参数后面是否加 "\\" 是有区别的,有斜杠是 fff 文件夹复制,包括 fff 自身;没有斜杠时复制 fff 文件夹的所有内容,但不含 fff 名称。

FSO 子对象的属性和方法

通过操作 FSO 对象属性或方法可以获取一些子对象,包括: drive 驱动器对象、folder 文件夹对象、file 文件对象和 TextStream 文本流对象。

drive 对象

FSO 对象唯一的 Drives 属性将返回一个 drive 对象的集合。drive 对象属性表示意义如下:

属性功能说明
AvailableSpace驱动器的可用空间,考虑了某些限制,字节单位
DriveLetter哪个符号被赋给了该驱动器
DriveType驱动器的类型,如不可识别的、可移动的、固定的、网络的、cd-rom 或 ram 磁盘
FileSystem驱动器使用的文件系统类型,如 fat、fat32、ntfs 等
FreeSpace驱动器剩余的可用空间,字节单位
IsReady驱动器是否可以使用,-1 代表可用, 0 代表不可用
Path驱动器的路径
RootFolder驱动器的根目录对象
SerialNumber驱动器的序列号
ShareName如果是一个网络驱动器,则返回驱动器的网络共享名
TotalSize驱动器的总容量,字节单位
VolumeName驱动器卷标名

drive 对象没有支持的方法。

folder 对象

调用 FSO 对象的 GetFolder 方法将返回一个 folder 对象,下面代码在 CAD 中查看 folder 对象的属性和方法:

;;示例15
(vlax-dump-object (vlax-invoke FSO 'GetFolder "d:\\dc") t)

folder 对象的属性:

属性功能说明
Attributes文件属性,包括 0-正常、1-只读、2-隐藏、4-系统、9-名称、16-文件夹
DateCreated返回该文件夹的创建日期和时间
DateLastAccessed返回最后一次访问该文件夹的日期和时间
DateLastModified返回最后一次修改该文件夹的日期和时间
Drive返回该文件夹所在的驱动器 Drive 对象
Files返回该文件夹下的文件对象集合
IsRootFolder是否根目录
Name返回文件夹的名字
ParentFolder返回该文件的父文件夹的 Folder 对象
Path返回文件的绝对路径,可使用长文件名
ShortName返回DOS风格的8.3形式的文件名
ShortPath返回DOS风格的8.3形式的文件绝对路径
Size返回该文件的大小(字节)
SubFolders子文件夹对象
type返回一个文件类型的说明字符串

folder 对象支持的方法:

方法与参数功能说明
Copy(destination,overwrite)将这个文件复制到 destination 指定的文件夹。如果 destination 的末尾是路径分隔符,那么认为 destination 是放置拷贝文件的文件夹。否则认为 destination 是要创建的新文件的路径和名字。如果目标文件已经存在且 overwrite 参数设置为 False,将产生错误,缺省的 overwrite 参数是 True
Delete(force)删除这个文件。如果可选的 force 参数设置为 True,即使文件具有只读属性也会被删除。缺省的 force是 False
Move(destination)将文件移动到destination指定的文件夹
CreateTextFile (filename,overwrite,unicode)用指定的文件名创建一个新的文本文件,并且返回一个相应的 TextStream 对象。如果可选的 overwrite 参数设置为 True,将覆盖任何已有的同名文件。缺省的 overwrite 参数是 False。如果可选的 unicode 参数设置为 True,文件的内容将存储为 unicode 文本。缺省的 unicode 是False
OpenAsTextStream(iomode,format)打开指定文件并且返回一个TextStream对象,用于文件的读、写或追加。iomode 参数指定了要求的访问类型,允许值是 ForReading(1) (缺省值)、ForWrite(2)、ForAppending(8)。format 参数说明了读、写文件的数据格式。允许值是 0(缺省): ascii 数据格式;-1: Unicode 数据格式;-2: 系统缺省格式

一个文件夹对象的属性,有子文件夹对象集合-SubFolders、有父文件夹对象-ParentFolder、还有驱动器对象-Drive。通过这三个属性可以在整个文件系统中导航,获取磁盘驱动器的所有文件。

file 对象

调用 FSO 对象的 GetFile 方法将返回一个 file 对象,下面代码查看 file 对象的属性和方法:

;;示例16
(vlax-dump-object (vlax-invoke FSO 'GetFile "d:\\d.pdf") t)
;特性值:
;   Attributes = 32
;   DateCreated (RO) = 42864.5
;   DateLastAccessed (RO) = 42864.5
;   DateLastModified (RO) = 42864.5
;   Drive (RO) = #<VLA-OBJECT IDrive 0000000008a48d08>
;   Name = "d.pdf"
;   ParentFolder (RO) = #<VLA-OBJECT IFolder 0000000008a02178>
;   Path (RO) = "D:\\d.pdf"
;   ShortName (RO) = "d.pdf"
;   ShortPath (RO) = "D:\\d.pdf"
;   Size (RO) = 2104235
;   Type (RO) = "Adobe Acrobat 文档"
;支持的方法:
;   Copy (2)
;   Delete (1)
;   Move (1)
;   OpenAsTextStream (2)

file 对象的属性:
Attributes 返回文件的属性。可以是下列值中的一个或其组合:
Normal(0)、ReadOnly(1)、Hidden(2)、System(4)、Volume(9)、Directory(16)、Archive(32)、Alias(64)和Compressed(128)
其余属性及方法与 folder 对象相同,不再赘述。

应用示例

以下函数用递归法求某文件夹里文件和子文件夹数量(在制作文件处理进度条时可能有用)。

;;示例17
;;调用 (fnSum "d:\\dc")
;;返回 (11446 2266)
(defun fnSum(p / F Filesum Foldsum)
    (setq F (vlax-create-object "scripting.FileSystemObject")
        Filesum 0 Foldsum 0)
    (defun getsum(FD / FDs)
        (setq FDs (vlax-get FD 'SubFolders)
            Filesum (+ Filesum (vlax-get (vlax-get FD 'Files) 'count))
            Foldsum (+ Foldsum (vlax-get FDs 'count)))
        (vlax-for x FDs (getsum x))
    )
(if (= -1 (vlax-invoke F 'FolderExists p))(getsum (vlax-invoke F 'GetFolder p)))
(vlax-release-object F)
(list Filesum Foldsum)
)

FSO 并不是必须的,轻量级的文件夹操作,使用 Vlisp 也可以满足要求,例如遍历文件夹,返回某路径下的文件夹及子文件夹的列表。

;;示例18
;;返回某路径下的文件夹及子文件夹
;;参数: p 为路径,调用 (Get_Folds "d:\\fff")
(defun fnSum2(p / d)
(defun Fold(s)
 (setq d (cons s d))
 (foreach x (cddr(vl-directory-files s nil -1))(Fold(strcat s "\\" x)))
)
(if (findfile p)(Fold p))
(reverse d)
)

用 Vlisp 函数求文件夹所有子文件及文件夹数量,实现示例17 的效果。

;;示例19
(defun fnSum2(p / Filesum Foldsum)
    (setq Filesum 0 Foldsum 0)
    (defun Fold(s / d)
        (setq d (cddr(vl-directory-files s nil -1))
            Foldsum (+ Foldsum (length d))
            Filesum (+ Filesum (length (vl-directory-files s nil 1))))
        (foreach x d (Fold(strcat s "\\" x)))
    )
(if (findfile p)(Fold p))
(list Filesum Foldsum)
)

两种方法的效率对比测试:

;;示例20
(defun c:test( / i AA)
(vl-load-com)
(setq time0 (getvar "date"))(fnSum "d:\\dc") ;;调用 FSO 对象函数
(setq time1 (getvar "date"))(fnSum2 "d:\\dc") ;;调用 VLisp 函数
(princ (strcat "\n调用 FSO 对象函数耗时: " (rtos (* 86400 (- time1 time0)) 2 4) " 秒"))
(princ (strcat "\n调用 VLisp 函数耗时: " (rtos (* 86400 (- (getvar "date") time1)) 2 4) " 秒"))
(princ)
)

测试文件夹数据为:文件 1.2 万个,文件夹 2300 个
调用 FSO 对象函数耗时: 0.574 秒
调用 VLisp 函数耗时: 0.732 秒
可以看出效率相差不大,可能是由于 lisp 的递归结构降低了 FSO 对象的使用效率。

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yxp_xa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值