5

Often I need to use regular expressions to match strings across multiple lines. In python and other languages, it's possible to ask that dots match newlines (as opposed to the default behavior). Is it possible to do this using the default regexp utilities without hacking the source?

I know I can use a negated character class, which will consume newlines if the newline is not in the class, but this is too limited since what I really want is to just match any character at all.

  • 1
    FWIW: In Icicles, you can use C-M-. anytime in the minibuffer to toggle whether when you type . in your minibuffer input it matches any char (including newlines) or any char except newlines. This is handy for apropos completion, which uses regexp matching. – Drew Aug 9 '15 at 1:03
5

While erjoalgo's answer is correct, according to the Emacs Wiki Mutliline Regex page, it is not the most efficient answer:

To match any number of characters, use this: .* – the problem is that . matches any character except newline. What many people propose next works, but is inefficient if you assume that newlines are not that common in your text: "\\(.\\|\n\\)". Better match multiple lines instead: "\\(.*\n?\\)*". The newline is optional so that the expression can end in the middle of a line. Better yet: "[\0-\377[:nonascii:]]*" avoids “Stack overflow in regexp matcher” for huge texts, e.g., > 34k.

  • 2
    Can anyone suggest why that last pattern from the Wiki was written "[\0-\377[:nonascii:]]*" rather than "[\0-\177[:nonascii:]]*" (or, moreover, "[[:ascii:][:nonascii:]]*") ? – phils Aug 9 '15 at 10:16
  • "\\(.*\n?\\)*" will lead to pathologically slow behavior because it can match in too many different ways. Better use ".*\\(\n.*\\)?" – Stefan Aug 9 '15 at 19:56
4

Actually, I just noticed I can do this with \(.\|[\n]\)*. For example,

[code] $ sudo wpa_supplicant -B -i wlan0 -Dwext -c universitywpa 
Successfully initialized wpa_supplicant
ioctl[SIOCSIWENCODEEXT]: Invalid argument
ioctl[SIOCSIWENCODEEXT]: Invalid argument
[/code]

-

(progn 
  (re-search-forward "[[]code]\\(\\(.\\|[\n]\\)*?\\)[[]/code]" )
  (match-string 1))

gives

 " $ sudo wpa_supplicant -B -i wlan0 -Dwext -c universitywpa 

Successfully initialized wpa_supplicant
ioctl[SIOCSIWENCODEEXT]: Invalid argument
ioctl[SIOCSIWENCODEEXT]: Invalid argument
"

A shorthand for this would be nicer, though, something like

(let ((re-dot-match-all t))
      (re-search-forward "[[]code]\\(.*?\\)[[]/code]" )

(match-string 1))

Similar to the case-fold-search use. Or a named character class:

(re-search-forward "[[]code]\\([[:any:]]*?\\)[[]/code]" )
  • I suppose it should be noted that \s- and [:space:] also match newlines, and \ca and [:ascii:] match any ASCII character. But see my answer for a potentially better solution. – Trebor Rude Aug 8 '15 at 20:45
  • someone care to explain the downvote? – erjoalgo Aug 8 '15 at 22:27
  • 1
    I wondered the same thing myself. I upvoted your answer since it does work. I've never cast any downvotes on emacs SE (as can be seen by my lack of the Critic badge). – Trebor Rude Aug 8 '15 at 23:08
  • Since you're writing elisp anyway, I suggest something along the lines of: (search-forward "[code]") (buffer-substring (point) (progn (search-forward "[/code]") (match-beginning 0))) – YoungFrog Aug 8 '15 at 23:47
  • yeah, that's exactly what I want to avoid, having to write more code, especially imperative and tedious code. the intent can be expressed more readably and concisely by making dot match newlines, which is already standard practice in other languages. – erjoalgo Aug 9 '15 at 1:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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