我有一个如下文件夹路径:
/h/apps/new/app/k1999
我想使用以下正则表达式删除/app/k1999
部分:
set folder "/h/apps/new/app/k1999"
regsub {\/app.+$} $folder "" new_folder
但结果是/h
:删除了太多元素。
我注意到我应该使用非贪婪匹配,所以我将代码更改为:
regsub {\/app.+?$} $folder "" new_folder
但结果仍是/h
。
上述代码有什么问题?
答案 0 :(得分:3)
非贪婪只是意味着它会尝试匹配最少量的字符,并在整个正则表达式不匹配时增加该数量。相反的 - 贪婪 - 意味着它会尝试尽可能多地匹配字符,并且如果整个正则表达式不匹配则减少该数量。
正则表达式中的 $
表示字符串的结尾。因此,something.+$
和something.+?$
将是等效的,只是在匹配之前会进行更多重试。
在您的情况下,/app.+
与/apps
匹配,这是您的字符串中第一次出现/app
。您可以通过更明确地添加/
后面的/app
来修复它:
regsub {/app/.+$} $folder "" new_folder
答案 1 :(得分:2)
如果您希望将app
与整个单词匹配,则可以使用Tcl中的\m
\M
和\m
:
\M
仅在单词的开头匹配\M
只在单词的末尾匹配
我们只需要/
,因为\m
是一个非单词字符,我们不需要set folder "/h/apps/new/app/k1999"
regsub {/app\M.+$} $folder "" newfolder
puts $newfolder
:
/h/apps/new
结果:app
(我们将整个字[^/]+
中的所有内容删除到最后。)
如果您只想删除路径中的部分字符串,可以使用否定的类regsub {/app/[^/]+} $folder "" newfolder
来确保只有IDEONE demo:
into
答案 2 :(得分:2)
你可以使用正则表达式替换操作从路径名中删除目录后缀,但这并不意味着应该。
file join {*}[lmap dir [file split $folder] {if {$dir ne {app}} {set dir} break}]
# -> /h/apps/new
路径名是一个字符串,但更恰当的是它是一个目录名列表:
file split $folder
# -> / h apps new app k1999
你想要的是目录名的子列表,但不包括名为" app"的目录。
lmap dir [file split $folder] {if {$dir ne {app}} {set dir} break}
# -> / h apps new
(目录名称可以根据需要进行测试; {$dir ni {foo app bar}}
可以跳过其他名称,或者{![string match app-* $dir]}
可以使用" app - &#34开头的任何名称;)
当您获得所需的目录名列表时,再次将其元素重新加入路径名,如上所述。
那么为什么要这样做而不是使用正则表达式替换操作呢?这个问题很好地说明了问题。除非一个人是RE专家或者非常注意阅读文档,否则很可能会根据预感制定正则表达式。在最坏的情况下,它第一次工作。如果没有,那么人们很想修补它直到它。任何充分未被理解的(是的, 一个词)RE似乎在大多数情况下会偶尔出现误报和否定以使事情变得有趣。
拆分它,截断它,加入它。不会出错。如果确实如此,那显然是错误的,迫使你解决它。
答案 3 :(得分:2)
正则表达式引擎总是尽快开始匹配;贪婪并不影响这一点。这意味着在这种情况下,它总是开始匹配太快;你想要最后一场比赛,而不是第一场比赛。
如果您使用regexp -all -indices -inline
,则可以找到最后一场比赛的开始位置。然后,您可以删除实际上不想要的部分(例如,将其替换为空字符串:
set folder "/h/apps/new/app/k1999"
set indices [regexp -all -indices -inline {/app} $folder]
# This gets this value: {2 5} {11 14}
# If we have indices — if we had a match — we can do the rest of our processing
if {[llength $indices] > 0} {
# Get the '11'; the first sub-element of the last element
set index [lindex $indices end 0]
# Replace '/app/k1999' with the empty string
set newfolder [string replace $folder $index end ""]
} else {
set newfolder $folder; # In case there's no match...
}