我正在尝试编写一个函数来从字符串中删除后缀。后缀如下:
agent_pkg
agent
pkg
driver
abs_if
abs_if_pkg
if_pkg
if
测试字符串:
test_blah_agent_pkg
test_blah_agent
test_blah_pkg
test_blah_driver
test_blah_abs_if
test_blah_abs_if_pkg
test_blah_if_pkg
test_blah_if
从上面的测试字符串中,我希望得到test_blah
。
我写了一个这样的函数:
(defun get-base-name (name)
"Get the base name from string."
(setq s (substring-no-properties name))
(string-match "\\(.*\\)_\\(agent_pkg\\|agent\\|driver\\|abs_if\\|if\\|pkg\\)" s)
(match-string 1 s))
但它总是与短的候选人相匹配。我从test_blah_abs
(get-base-name "test_blah_abs")
答案 0 :(得分:4)
.*
是贪婪的,这意味着只要字符串与正则表达式匹配,它就会尽可能地覆盖。你想让它变得非贪婪,一旦找到匹配就停止。在?
或*
之后添加+
会使其变得非贪婪。比较:
(let ((s "abcabcabc"))
(string-match ".*c" s)
(match-string 0 s)) ; => "abcabcabc"
(let ((s "abcabcabc"))
(string-match ".*?c" s)
(match-string 0 s)) ; => "abc"
.*?
是.*
的非贪婪版本,因此只需添加?
即可:
(let ((s "test_blah_agent_pkg
test_blah_agent
test_blah_pkg
test_blah_driver
test_blah_abs_if
test_blah_abs_if_pkg
test_blah_if_pkg
test_blah_if"))
(string-match "\\(.*?\\)_\\(agent_pkg\\|agent\\|driver\\|abs_if\\|if\\|pkg\\)" s)
(match-string 1 s)) ; => "test_blah"
FYI,第三方字符串操作库s
有许多你认为有用的字符串函数,而不是一直依赖正则表达式。例如。 s-shared-start
可以找到2个字符串的公共前缀:
(s-shared-start "test_blah_agent" "test_blah_pkg") ; "test_blah_"
结合s-lines
,它将字符串按换行符分解为字符串列表,并使用来自令人惊叹的第三方列表操作库-reduce
的dash
函数,你可以找到一个每个字符串都常用的前缀:
(let ((s "test_blah_agent_pkg
test_blah_agent
test_blah_pkg
test_blah_driver
test_blah_abs_if
test_blah_abs_if_pkg
test_blah_if_pkg
test_blah_if"))
(-reduce 's-shared-start (s-lines s))) ; => "test_blah_"
¹请阅读under section Greediness以了解这一概念。