2

It is clear to me how to programmatically replace all instances of regex in a buffer with some text, something like (pardon my naive code):

(defun my/regex-global-replace (regex subst-text)
 "Globally replace `regex' with `subst-text' in current buffer"
 (goto-char (point-min))
 (while (re-search-forward regex nil t)
 (replace-match subst-text)))

But I can't figure it out how to do a similar global replace when using capture groups and sub-matches.

I am aware of this answer and the Cookbook entry, but it does not help me much, since it is not clear how I could use the sub-matches to replace text globally.

How could one write a broader version of the code above, so that it deals with sub-matches, something like:

(defun my/regex-global-replace (regex1 regex2)
 "Replace `regex1' with `regex2' in a buffer."
 (goto-char (point-min))
 (while (re-search-forward regex1 nil t)
 (replace-match regex2)))

Where regex1 may contain a certain number of capture groups, like,

(setq regex1 "some text \\([[:digit:]]+\\)")

and regex2 a related sub-match, the equivalent of PCRE's $1 or \1?


Example

As an example, say I have a buffer with content,

Some text 123
Some more text 456

I would like to programmatically run a global search-and-replace that would, as example, change text but keep the numbers,

Changed text, keep digits 123
Changed text, keep digits 456

In some other languages (Perl comes to mind), I would create a search regex with a capture group, and a replace expression using the sub-matches, something like

s/(\w+\s+)+(\d+)\n/Changed text, keep digits \2\n/gim
6
  • What does it mean to replace a regex with a regex? That's not a flippant question; I genuinely don't understand what you're trying to accomplish. Apr 8, 2016 at 19:06
  • I have added an example, would that make it more clear?
    – gsl
    Apr 8, 2016 at 19:25
  • 1
    Just use \\2 in the replacement text. It it as simple as that. Note the escaping that is needed within strings to get a single character \ in the resulting string. \2 would just insert the character with octal code 2.
    – Tobias
    Apr 8, 2016 at 19:26
  • Could you give me a practical example, perhaps using the drafted function above? I am sorry I just can't wrap my mind around it and I would not know even the basic next step.
    – gsl
    Apr 8, 2016 at 19:30
  • 1
    Your perl example can be implemented as (while (re-search-forward "\\([[:alpha:][:space:]]\\)+\\([0-9]+\\)" nil t) (replace-match "Changed text, keep digits \\2\n")). Note, that you have to be careful with \\sw since this also matches digits. Therefore the above example is written with the character class [:alpha:]. The space could also be written as \\s-.
    – Tobias
    Apr 8, 2016 at 19:42

1 Answer 1

2

I think you are asking for a fairly standard use of replace-match. Capture both groups, and use the one you want to keep in the replacement text:

(setq regex1 "\\([^0-9]+ \\)\\([[:digit:]]+\\)$")
(setq regex2 "Changed text, keep digits \\2\n")

(defun my/regex-global-replace (regex1 regex2)
 "Replace `regex1' with `regex2' in a buffer."
 (goto-char (point-min))
 (while (re-search-forward regex1 nil t)
 (replace-match regex2)))
4
  • Thank you for the code. I have added an example, does it make it more clear? Would you set me in the right direction by using your code to improve my function?
    – gsl
    Apr 8, 2016 at 19:27
  • Thank you for the answer, it is what I was looking for. Nicely creative also how you improved on the (\w+\s+)+ part, by defining it as non-digit. Thanks.
    – gsl
    Apr 9, 2016 at 9:12
  • @gsl Negative matchers are a bit dangerous in connection with + and *. They also match newlines. Sometimes this can lead to very long matches that slow emacs down. One must use them with care. This is especially important for stuff that runs often such as font-lock-stuff.
    – Tobias
    Apr 9, 2016 at 19:53
  • Good to know, thank you for the advice. I am actually in the process of moving many scripts that use lots of regex search and replace from perl to elisp (because of the rx syntax, which makes them easier to maintain). I will be careful to check on those cases. Imho, for similar cases, rx syntax by itself makes elisp a well-worthy substitute for perl python, ruby, etc.
    – gsl
    Apr 10, 2016 at 7:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.