Emacs,使用字符串中的replace regexp匹配两个regexp
我试图使用Emacs,使用字符串中的replace regexp匹配两个regexp,regex,emacs,Regex,Emacs,我试图使用replace regexp in string替换字符串中的两个部分,但一次只能让一个部分工作。下面是一个示例,我想删除字符串开头的#和空格,以及字符串结尾的换行符。当我将两个调用组合成一个表达式时,我做错了什么 ;; Test string (setq inputStr "## Header Stuff ") ;; This doesnt trim the newline (setq header (replace-regexp-in-string "^[#\s]*\
replace regexp in string
替换字符串中的两个部分,但一次只能让一个部分工作。下面是一个示例,我想删除字符串开头的#
和空格,以及字符串结尾的换行符。当我将两个调用组合成一个表达式时,我做错了什么
;; Test string
(setq inputStr "## Header Stuff
")
;; This doesnt trim the newline
(setq header
(replace-regexp-in-string "^[#\s]*\\|\n$" "" inputStr) )
;; Each match done separately works though
(setq header
(replace-regexp-in-string "^[#\s]*" "" inputStr) )
(setq header
(replace-regexp-in-string "\n$" "" header) )
header
"Header Stuff"
更新:问题似乎出在第一个表达式上,例如,这将换行符和
“S”
替换为“X”
,(替换字符串“S\\\\\n$”“X”inputStr中的regexp)
看起来替换字符串中的regexp
与空字符串匹配的regexp有一些意外行为。下面的regexp实现了您所期望的功能(注意用+
量词代替*
):
原因在于replace regexp in string
的内部实现,您可以使用M-x find函数查找该函数。在伪代码中,它大致执行以下操作:
给定一个regexp
、一个replacement
和一个string
:
将l
设置为字符串长度,将start
设置为0
。创建一个名为matches
的空堆栈,以累积新字符串的片段
只要start
小于l
并且regexp
与string
中的某个地方匹配,请执行以下操作:
提取与regexp匹配的string
部分,并将其称为str
将regexp
替换为replacement
,在较短的字符串str
中(这很重要)
将新字符串的以下两个片段推送到匹配的堆栈上:
字符串
的不匹配初始部分,从开始
到匹配的开始
- 子字符串
str
,其中regexp
的匹配项现在已替换为replacement
将start
设置到匹配部分的末尾,然后重复
最后,将matches
堆栈上的字符串片段按相反顺序连接起来并返回结果
原始regexp的问题发生在循环的步骤(3)中。即使regexp与完整字符串“###Header stuff\n”
末尾的换行符正确匹配,但当它第二次与一个字符串“\n”
匹配时,替换项的第一个分支(与空字符串匹配)优先于第二个分支,它用空字符串替换空字符串,无法删除尾随的新行
这可以说是替换字符串中的regexp中的一个bug,但它也说明了regexp语义是多么棘手,尤其是当涉及空字符串时。对我来说,变通解决方案更容易阅读和理解:
(let ((input-string "## Header Stuff
"))
(setq input-string (replace-regexp-in-string "\\`[#\s]*" "" input-string))
(setq input-string (replace-regexp-in-string "\n*\\'" "" input-string))
input-string)
如果您有最新的Emacs(预测试24.4或更高版本),您还可以使用内置subr-x
软件包中的string trim right
功能:
(let ((input-string "## Header Stuff
"))
(string-trim-right (replace-regexp-in-string "\\`[#\s]*" "" input-string)))
顺便说一句,在研究这个问题时,我惊讶地发现Emacs字符串中的\s
只是一种不同的空格字符书写方式。如果您希望regexp行为类似于Perl的通配符\s
,您可能希望使用“\\s-”
(将任何字符与空格语法匹配),或“[:space:][]”
注意(将regexp替换为字符串“^[\s]*\\\\\\\n$“X”inputStr
返回结果。@Barmar我明白了,为什么“X”不是替换换行符?我不明白,这就是为什么我没有给出答案。@Barmar这一个在换行符后面放了“X”,但我不明白,(在字符串中替换regexp“^[\35;\ s]*\\\\\\n$\\'“X”inputStr)
$
匹配行尾,\\'
匹配行尾。根据代码注释,这个实现似乎是为了提高效率而设计的——特别是避免在每次替换时复制整个字符串,就像更简单的实现那样。我同意这似乎不是理想的行为。如果愿意,可以将其报告为bug(M-x report emacs bug
)。
(let ((input-string "## Header Stuff
"))
(string-trim-right (replace-regexp-in-string "\\`[#\s]*" "" input-string)))