ljxkm 发表于 2019-9-7 16:22:37

两个简单的读取excel表格函数

在CAD二次开发中,常需要读取excel表格数据,根据lisp语言的特点,有时一个一个单元格读取反而方便,以下是在实际中常用的两个读取excel表格数据小程序,个人觉得还是比较实用啊:
;;;;;读取excel据函数
(defun Ljx-read-excel-data (vv hor sh / hor ru) ;;;vv为列数直接输入字母、hor为行数,直接输入数字,sh为工作表对象sheet
(setq hor (itoa hor))
(setq ru (vlax-variant-value (msxl-get-value (msxl-get-range sh (strcat vv hor)))))
ru
)

;;;;;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
;;;;;读取excel数据函数输出text格式
(defun Ljx-read-excel-text (vv hor sh / hor ru) ;;;vv为列数直接输入字母、hor为行数,直接输入数字,sh为工作表对象sheet
(setq hor (itoa hor))
(setq ru (vlax-variant-value (msxl-get-text (msxl-get-range sh (strcat vv hor)))))
ru
)

crtrccrt 发表于 2019-9-11 15:54:47

;;;ljx-vlxls-app-Init;子程序局部优化;

      (cond
      ( (wcmatch excelname "*.xls");;;为2003-xls文件时;
            (cond
            ( (setq out2003 (vl-member-if (function (lambda (x) (findfile x))) hm-2003))    (setq out (car out2003))    )
            ( (setq out2007 (vl-member-if (function (lambda (x) (findfile x))) hm-2007))    (setq out (car out2007))    )
            ( (setq out2010 (vl-member-if (function (lambda (x) (findfile x))) hm-2010))    (setq out (car out2010))    )
            ( (setq out2013 (vl-member-if (function (lambda (x) (findfile x))) hm-2013))    (setq out (car out2013))    )
            ( (setq out2016 (vl-member-if (function (lambda (x) (findfile x))) hm-2016))    (setq out (car out2016))    )
            ( t    (princ "\\n计算机上未安装excel2003、excel2007、excel2010、excel2013、excel2016,不能打开xls文件!"))
            );;;cond;
      )
      ( (wcmatch excelname "*.xlsx");;;为2007-xlsx文件时;
            (cond
            ( (setq out2007 (vl-member-if (function (lambda (x) (findfile x))) hm-2007))    (setq out (car out2007))    )
            ( (setq out2010 (vl-member-if (function (lambda (x) (findfile x))) hm-2010))    (setq out (car out2010))    )
            ( (setq out2013 (vl-member-if (function (lambda (x) (findfile x))) hm-2013))    (setq out (car out2013))    )
            ( (setq out2016 (vl-member-if (function (lambda (x) (findfile x))) hm-2016))    (setq out (car out2016))    )
            ( t    (princ "\\n计算机上未安装excel2007、excel2010、excel2013、excel2016,不能打开xls文件!"))
            );;;cond;
      )
      ( T(setq out nil))
      );;;cond;
;;;先加载低版本,低版本不存在再加载高版本;;

ljxkm 发表于 2024-6-11 20:07:20

dalin1985 发表于 2023-5-27 16:49
请问如何实现列数用数字呢?用字母列数太多了不能迭代呀,用着不方便。

这个网上有:
;;;cell id 轉換
(Defun vlxls-cellid (id / xx id1 id2 Rtn)
(if (= (type id) 'list)
    (setq id (vlxls-rangeid id))
)
(setq id (strcase id))
(if (null (setq xx (vl-string-search ":" id)))
    (setq Rtn (list id ""))
    (setq id1 (substr id 1 xx)
   id2 (substr id (+ xx 2))
   id1 (vlxls-rangeid id1)
   id2 (vlxls-rangeid id2)
   Rtn (list (vlxls-rangeid (list (min (car id1) (car id2)) (min (cadr id1) (cadr id2))))
      (vlxls-rangeid (list (max (car id1) (car id2)) (max (cadr id1) (cadr id2))))
       )
    )
)
Rtn
)


;;;Examples:
(vlxls-cellid ‘(3 14)) return: ("C14" "")
(vlxls-cellid “D23”) return: ("D23" "")
(vlxls-cellid “C12:F3”) return: ("C3" "F12")
(vlxls-cellid “F15:G22”) return: ("F15" "G22")

ljxkm 发表于 2019-9-14 10:05:50

ljx-read-excel-data、ljx-read-excel-data、Ljx-vlxls-get-range-value三个函数在收集的大量的函数的支持下运行成功,但这确实显得过于复杂繁琐,多年来自己收集了大量的函数,但换一个人来用就非常不方便,从自己使用的体会来看,越不要外挂,使用系统自带的东西就能运行,越是方便适用,来了点兴趣啊,按以上思路,不依靠那些类型库声明代码、不依靠大量mslx函数和自定义函数就能运行,现基本搞成,在自己的机子上运行良好,以下是经过修改后的函数和运行实例,excel表还是用原来的"ABC.xls"文件啊:
;;;;;读取excel数据函数
(defun Ljx-read-excel-data1 (vv hor sh) ;;;vv为列数直接输入字母、hor为 第几行,直接输入数字,sh为工作表对象sheet
(vlax-variant-value (vlax-get-property (vlax-get-property sh 'Range (strcat vv (itoa hor))) 'Value2))
)

;;;;;读取excel数据函数输出text格式
(defun Ljx-read-excel-text1 (vv hor sh ) ;;;vv为列数直接输入字母、hor为 第几行,直接输入数字,sh为工作表对象sheet
(vl-princ-to-string (vlax-variant-value (vlax-get-property (vlax-get-property sh 'Range (strcat vv (itoa hor))) 'Value2)))
)
;;;;;;取得单元格数据,支持区域输入
(defun ljx-vlxls-get-range-value1 ( sheet rangeid / range value valuelist )
(setq range (vlax-get-property sheet 'Range rangeid))
(setq value (vlax-get-property range 'Value2))
(cond
    ((= (vlax-variant-type value)8204);;;为数组时,即为区域;
      (progn
          (setq value (vlax-safearray->list(vlax-variant-value value)))
          (setq valuelist (mapcar (function (lambda (x)(mapcar 'vlax-variant-value x))) value))
      )
    )
    ( T;;;;为单个单元格;
      (setq valuelist (vlax-variant-value value))
    )
);;;;cond
valuelist
)

;;;;运行函数
(defun test1 ()
(vl-load-com)
(setq exname "d:\\ABC.xls")
(setq *excel* (vlax-create-object "excel.application"))
(setq *xlapp* (vlax-invoke-method (vlax-get-property *excel* 'Workbooks) 'Open exname))
(vla-put-visible *excel* 1)
(setq *sheet* (vlax-get-property (vlax-get-property *xlapp* 'Worksheets) 'item "DEF"))
(setq dat0 (Ljx-vlxls-get-range-value1 *sheet* "E5")
      dat1 (Ljx-vlxls-get-range-value1 *sheet* "E5:F5");;;;输出:((-25.10 "张三"))
      dat2 (Ljx-vlxls-get-range-value1 *sheet* "E5:E6");;;;输出:((-25.10)(26.85))
      dat3 (Ljx-vlxls-get-range-value1 *sheet* "E5:F6");;;;输出:((-25.10 "张三")(26.85 "李四"))
      dat4 (Ljx-read-excel-data1"E" 5 *sheet*);;;;-->-25.10
      dat5 (Ljx-read-excel-text1"E"5*sheet* );;;;-->"-25.10"
      dat6 (Ljx-read-excel-data1"E"6 *sheet* );;;;-->26.85
      dat7 (Ljx-read-excel-text1 "E"6 *sheet*);;;;-->"26.85"
      dat8 (Ljx-read-excel-data1 "F"5 *sheet*);;;;-->“张三”
      dat9 (Ljx-read-excel-text1 "F"5 *sheet*);;;;-->“张三”
      dat10 (Ljx-read-excel-data1"F"6 *sheet* );;;;-->"李四"
      dat11 (Ljx-read-excel-text1"F"6 *sheet* );;;;-->"李四"
)
(vlax-invoke-method (vlax-get-property *excel* "ActiveWorkbook") 'Close 0)
(vlax-invoke-method *excel* 'QUIT)
(vlax-release-object *sheet*)
(vlax-release-object *xlapp*)
)
;;;;具体使用时先将ABC.xls(解压ABC.zip)拷贝至d盘根目录下,加载test1.lsp文件,在CAD命令行输入 (test1)即可运行

ljxkm 发表于 2019-9-7 19:47:47

再贴一个我用的函数,一般读取excel数据基本够用了啊:
取得exce表指定单元格数据:
;;;;;;Ljx-vlxls-get-range-value
;;;;;;取得单元格数据,支持区域输入
;;;;;;参数:sheet当前工作表,rangeid:区域id,如"A1","A1:F10", "A1:A100","A1:EE1"
;;;;;;示例:(Ljx-vlxls-get-range-value *sheet* "E5")输出:-25.10,
;;;;;;      (Ljx-vlxls-get-range-value *sheet* "E5:F5"")输出:((-25.10 "张三"))
;;;;;;      (Ljx-vlxls-get-range-value *sheet* "E5:E6"")输出:((-25.10)(26.85))
;;;;;      (Ljx-vlxls-get-range-value *sheet* "E5:F6"")输出:((-25.10 "张三")(26.85 "李四")
(defun Ljx-vlxls-get-range-value (sheet rangeid / range value value1 valuelist index_i)
(setq range (msxl-get-range sheet rangeid))
(setq value (MSXL-GET-VALUE2 range ))
(cond
    ((=(vlax-variant-type value)8204);;;为数组时,即为区域
   (progn
       (setq value (vlax-safearray->list(vlax-variant-value value)))
       (setq valuelist '()
             index_i 0
       )
       (repeat (length value)
(setq value1 (nth index_i value))
(setq valuelist (cons (mapcar
   '(lambda (x)(vlax-variant-value x))
   value1
       )
       valuelist
)
)
(setq index_i (1+ index_i))
       );;;;repeat
       (setq valuelist (reverse valuelist))
   )
    )
    (T;;;;为单个单元格
   (setq valuelist (vlax-variant-value value))
    )
);;;;cond
valuelist
);;;;defun

注册 发表于 2019-9-8 09:13:41

留个脚印,先

crtrccrt 发表于 2019-9-8 15:13:59

(Ljx-vlxls-get-range-value *sheet* "E5:F5"")
*sheet*具体些,或具体实例,好让新手明白

(defun Ljx-read-excel-data (vv hor sh / hor ru)
(defun Ljx-read-excel-text (vv hor sh / hor ru)
;;;sh;具体些,或具体实例,好让新手明白

ljxkm 发表于 2019-9-8 18:21:01

crtrccrt说的很对,*SHeet*、sh均为工作表对象,就是你要读取数据的工作表,VV和hor分开主要便于循环读取,Ljx-vlxls-get-range-value函数功能强一些,当为单个单元格时如(Ljx-vlxls-get-range-value *sheet* "E5")直接读得该单元格的数据,输出:-25.10,为区域时按((第一行) (第二行)...(第n行))的格式读取输出,
;;;;;;      (Ljx-vlxls-get-range-value *sheet* "E5:F5")输出:((-25.10 "张三")),第5行:“E5”数据为-25.10、“F5”数据为“张三”
;;;;;;      (Ljx-vlxls-get-range-value *sheet* "E5:E6")输出:((-25.10)(26.85)) ,第5行“E5”数据为-25.10、第六行“E6”数据为26.85
;;;;;      (Ljx-vlxls-get-range-value *sheet* "E5:F6")输出:((-25.10 "张三")(26.85 "李四")),第五行:“E5”数据为-25.10、“F5”数据为“张三”;第六行:“E6”数据为26.85、 “F6”数据为"李四"。
简单点,几个单元格数据分别为:
"E5"=-25.10
"F5"=“张三”
"E6"=26.85
"F6"=“李四”。
(Ljx-read-excel-data"E"5 *sheet*)-->-25.10
(Ljx-read-excel-text"E"5*sheet* )-->"-25.10"
(Ljx-read-excel-data"E"6 *sheet* )-->26.85
(Ljx-read-excel-text "E"6 *sheet*)-->"26.85"

(Ljx-read-excel-data "F"5 *sheet*)-->“张三”
(Ljx-read-excel-text "F"5 *sheet*)-->“张三”
(Ljx-read-excel-data"F"6 *sheet* )-->"李四"
(Ljx-read-excel-text"F"6 *sheet* )-->"李四"

;;;;两个函数再精简一下:
;;;;;读取excel数据函数
(defun Ljx-read-excel-data (vv hor sh /ru) ;;;vv为列数直接输入字母、hor为 第几行,直接输入数字,sh为工作表对象sheet
(setq ru (vlax-variant-value (msxl-get-value (msxl-get-range sh (strcat vv (itoa hor))))))
)

;;;;;读取excel数据函数输出text格式
(defun Ljx-read-excel-text (vv hor sh /ru) ;;;vv为列数直接输入字母、hor为 第几行,直接输入数字,sh为工作表对象sheet
(setq ru (vlax-variant-value (msxl-get-text (msxl-get-range sh (strcat vv (itoa hor))))))
)

;;;;这样好像差不多了,这几个函数都是在初始化excel应用程序,打开excel表格,取得要读取的sheet后才能用啊,有关的步骤论坛上都有,大家可以去搜索一下




crtrccrt 发表于 2019-9-9 21:04:54

举例:
d:/ABC.xls中有DEF工作簿
如何形成Ljx-vlxls-get-range-value子程序需要的*sheet*参数.
新手一时半会也搞不明白.
直接给新手实例,举手之劳,新手会节省很多时间.

ljxkm 发表于 2019-9-10 10:28:22

本帖最后由 ljxkm 于 2019-9-10 20:37 编辑

crtrccrt 发表于 2019-9-9 21:04
举例:
d:/ABC.xls中有DEF工作簿
如何形成Ljx-vlxls-get-range-value子程序需要的*sheet*参数.

这有点超出贴两个函数源码的初衷了啊,真要函数动起来,还需要不少步骤,用到不少函数哈,不过既然提出来,就啰嗦一下,用到的函数都是网路搜索的集体成果啊:
定义以下函数:
(defun test ()
(vl-load-com)
(setq exname "d:\\ABC.xls")
(vlxls-app-init1 exname)
(setq *xlapp* (vlxls-app-open exname T))
(vlxls-sheet-put-active *xlapp* "DEF");;;;设为活动工作表
(setq *sheet* (msxl-get-ActiveSheet *Xlapp*))
(setq dat0 (Ljx-vlxls-get-range-value *sheet* "E5")
      dat1 (Ljx-vlxls-get-range-value *sheet* "E5:F5");;;;输出:((-25.10 "张三"))
      dat2 (Ljx-vlxls-get-range-value *sheet* "E5:E6");;;;输出:((-25.10)(26.85))
      dat3 (Ljx-vlxls-get-range-value *sheet* "E5:F6");;;;输出:((-25.10 "张三")(26.85 "李四"))
      dat4 (Ljx-read-excel-data"E" 5 *sheet*);;;;-->-25.10
      dat5 (Ljx-read-excel-text"E"5*sheet* );;;;-->"-25.10"
      dat6 (Ljx-read-excel-data"E"6 *sheet* );;;;-->26.85
      dat7 (Ljx-read-excel-text "E"6 *sheet*);;;;-->"26.85"
      dat8 (Ljx-read-excel-data "F"5 *sheet*);;;;-->“张三”
      dat9 (Ljx-read-excel-text "F"5 *sheet*);;;;-->“张三”
      dat10 (Ljx-read-excel-data"F"6 *sheet* );;;;-->"李四"
      dat11 (Ljx-read-excel-text"F"6 *sheet* );;;;-->"李四"
)
(vlxls-app-quit *xlapp* nil);;;;退出excel应用程序
(vlax-Release-Object *sheet*)
)
;;;;具体使用时先将ABC.xls(解压ABC.zip)拷贝至d盘根目录下,加载test.lsp文件,在CAD命令行输入 (test)即可运行,好了,两个函数扯出这么多来,lisp操控excel有点复杂,是集体智慧,实际工作中,要收集前辈编的较为全面的函数拷在一个lsp文件下成为模块,这样就方便了,excel版本不断更新,以前在网络上找的vlxls-app-init函数不能用了,那个vlxls-app-init1是我自己改来用的,一并贴出了,有点啰嗦,不是很好,但确实是可用的,祝大家进步,提高





crtrccrt 发表于 2019-9-11 11:31:13

;=========楼主细心,认真,值得表扬。;
;=========优化后Ljx-vlxls-get-range-value;
;;;;;;Ljx-vlxls-get-range-value
;;;;;;取得单元格数据,支持区域输入
;;;;;;参数:sheet当前工作表,rangeid:区域id,如"A1","A1:F10", "A1:A100","A1:EE1"
;;;;;;示例:(Ljx-vlxls-get-range-value *sheet* "E5");;;;;输出:-25.10,
;;;;;;      (Ljx-vlxls-get-range-value *sheet* "E5:F5");输出:((-25.10 "张三"))
;;;;;;      (Ljx-vlxls-get-range-value *sheet* "E5:E6");输出:((-25.10)(26.85))
;;;;;       (Ljx-vlxls-get-range-value *sheet* "E5:F6");输出:((-25.10 "张三")(26.85 "李四")
(defun ljx-vlxls-get-range-value ( sheet rangeid / range value valuelist )
(setq range (msxl-get-range sheet rangeid))
(setq value (MSXL-GET-VALUE2 range ))
(cond
    ( (= (vlax-variant-type value)8204);;;为数组时,即为区域;
      (progn
          (setq value (vlax-safearray->list(vlax-variant-value value)))
          (setq valuelist (mapcar (function (lambda (x)(mapcar 'vlax-variant-value x))) value))
      )
    )
    ( T;;;;为单个单元格;
      (setq valuelist (vlax-variant-value value))
    )
);;;;cond
valuelist
)

ljxkm 发表于 2019-9-11 13:02:26

crtrccrt 发表于 2019-9-11 11:31
;=========楼主细心,认真,值得表扬。;
;=========优化后Ljx-vlxls-get-range-value;
;;;;;;Ljx-vlxls ...

这样确实简洁,mapcar这个函数确实好,
页: [1] 2 3 4 5 6
查看完整版本: 两个简单的读取excel表格函数