赞
踩
(split "I Love You" " ")
用空格切割字符串,返回
("I" "Love" "You")
以下两个函数分别是明经和小东论坛里,大师们用 VLisp 函数实现的 split 功能,其中参数为: str — 待处理的长字符串; p — 为分割关键词
;;来自明经,该函数使用频率较高,互相引用频繁,原作者不详
(defun vl-Split1 (str p / pa sl xn)
(setq xn (1+ (strlen p)))
(while (setq pa (vl-string-search p str))
(setq sl (cons (substr str 1 pa) sl)
str (substr str (+ pa xn)))
)(reverse (cons str sl))
)
;;来自小东, Gu_xl 的 vlstring->list
(Defun vl-Split2 (str p / lst e)
(setq str (strcat str p))
(while (vl-string-search p str)
(setq lst (append lst (list (substr str 1 (vl-string-search p str)))))
(setq str (substr str (+ (1+ (strlen p)) (vl-string-search p str))))
)
(if lst (mapcar '(lambda (e) (vl-string-trim " " e)) lst))
)
以上函数算法基本相同:即用 vl-string-search 函数搜索 p 在 str 中出现的位置,再用 substr 按位置切割 str 字符串,并将返回值重新组合为一个 list 表。
测试:
命令: (split1 “I Love You” " ") 返回 (“I” “Love” “You”)
命令: (split2 “I Love You” " ") 返回 (“I” “Love” “You”)
如果到这里结束的话,就属于灌水的博客,显然本文的目的并不在此。再看下面出现汉字时的调用:
命令: (Split1 “粃糠abczyx” “z”) 返回 ("? “糠abc” “yx”)
命令: (Split2 “笨賊a\shit” “\”) 返回 ("笨? “a” “shit”)
返回值出现 bug,说明函数对字符的处理有漏洞。
经测试,上面函数 bug 出现原因是 vl-string-search 函数造成的。
lisp 函数在处理英文字符时,按一个字节 ascii 码搜索,处理 gb2312 编码汉字时,是按两个字节的 ascii 码分别处理,而不是将汉字视为一个整体。
命令: (vl-string->list “z”) 返回 (122)
命令: (vl-string->list “粃”) 返回 (187 122)
当函数的参数 str 里的汉字出现了参数 p 中英文字符的 ascii 码时,字符分割就会出现问题。
根据这个现象,增加对汉字的判断,如果首字节 ASCII 大于 127 时,即为汉字,则跳过两个字节。修正后的 split 函数如下:
(defun vl-Split3 (str p / pa sl xn f)
(setq xn (1+ (strlen p)) f 0)
(while (setq pa (vl-string-search p str f))
(if (< (vl-string-elt str (1- pa)) 128)
(setq sl (cons (substr str 1 pa) sl)
str (substr str (+ pa xn)) f 0)
(setq f (1+ pa))
)
)(reverse (cons str sl))
)
关于 AutoCAD 以及自带的 LISP 语言对中文字符处理的先天性缺陷,在其他语言中很少存在,现用正则表达式来设计一个 split 函数:
(defun rg-Split (s p / L r)
(setq r (vlax-create-object "vbscript.regexp"))
(vlax-put-property r 'Global 1)
(vlax-put-property r 'Pattern p)
(read (strcat "(\"" (vlax-invoke r 'Replace s "\" \"") "\")"))
)
用正则很简洁,只需要告诉正则对象的匹配语法即可。而且这个函数还有一个实用的功能,可以支持多个关键字分割,只需要用 “|”将关键字分开即可。
测试:
命令: (rg-Split “粃糠abczyx” “z”) 返回 (“粃糠abc” “yx”)
命令: (rg-split “abcfarecadefge” “c|f”) 返回 (“ab” “are” “ade” “ge”)
创建一个长度为十万的字符串,对比测试一下 vlisp 和 正则 函数的分割效率。
(defun c:test( / i AA)
(setq i 0 AA "")
(while (< i 100000) (setq AA (strcat AA (itoa (setq i (1+ i))) ",")))
(setq time0 (getvar "date"))
(vl-Split3 AA ",") ;;调用 VL 函数
(setq time1 (getvar "date"))
(rg-Split AA ",") ;;调用正则函数
(setq time2 (getvar "date"))
(princ (strcat "\nvl-split 函数耗时: " (rtos (* 86400 (- time1 time0)) 2 4) " 秒"))
(princ (strcat "\nrg-split 函数耗时: " (rtos (* 86400 (- time2 time1)) 2 4) " 秒"))
(princ)
)
运行结果:
vl-split 函数耗时: 28.615 秒
rg-split 函数耗时: 0.047 秒
可见 VL 函数和正则表达式对字符串的操作效率完全没有可比性。实际上,即使在 vb 或 c 中,用自带的 split 函数也比不上用正则来实现 split 功能的效率,这就是 ActiveX/COM 的优越性。
唯一需要注意的是,在 AutoLisp 和 正则表达式中,不约而同的定义 “\” 为转义字符,所以如果要以单字符“\” 为切割字符,应该用四个 “\\” 来表示,第一次转义为 CAD 字符,第二次转义为正则字符。
例如:
命令: (Split "笨賊a\\shit" "\")
分割字符为单个斜杠是不行的,需要用四个斜杠表示:
命令: (Split "笨賊a\\shit" "\\\\")
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。