1

I'd like to perform a regexp search, capture part of a matched string, and save that part to a list. (Eventually the plan is to save this list to a file in a particular format, but for now a simple list will do.) I've tried the following, but it doesn't seem to do anything when I M-x eval-region over the whole document and M-x first-words-in-numbered-list.

(defun first-words-in-numbered-list ()
  "gets the first word in lines that start with numbered list and builds those
   words into a list, e.g. running the function on the following text:

   foo
   bar
   1. alpha zulu
   2. bravo tango
   quux schmucks
   3. charlie foxtrot
   10. kilo romeo

   should produce a list (\"kilo\" \"charlie\" \"bravo\" \"alpha\")"
  (interactive)
  (goto-char 1)
  (setq first-words '())
  (while (re-search-forward
          "^[[:space:]]*[[:digit:]]+[.)]?[[:space:]]+\([[:word:]]\)"
          nil t)
    ;; for debugging
    (message (match-string 1))
    (sleep-for 2)
    ;; for actually storing the list
    (setq first-words (cons (match-string 1) first-words))))

(defun print-list-elements (list)
  "Print each element of LIST on a line of its own."
  (while list
    (print (car list))
    (setq list (cdr list))))
(print-list-elements first-words)

foo
bar
1. alpha
2. bravo
quux
3. charlie
10. kilo

Edit: I've updated the file to look like this:

(defun first-words-in-numbered-list ()
  "gets the first word in lines that start with numbered list and builds those
   words into a list, e.g. running the function on the following text:

   foo
   bar
   1. alpha zulu
   2. bravo tango
   quux schmucks
   3. charlie foxtrot
   10. kilo romeo

   should produce a list (\"kilo\" \"charlie\" \"bravo\" \"alpha\")"
  (interactive)
  (goto-char 1)
  (setq first-words '())
  (while (re-search-forward
          "^[[:digit:]]+[.)]?[[:space:]]+\\([[:word:]]+\\)"
          nil t)
    (setq first-words (cons (match-string 1) first-words))))

foo
bar
1. alpha
2. bravo
quux
3. charlie
10. kilo

which is most of the way there! The problem is that when I C-h v first-words <RET>, I get the following:

first-words's value is (#("kilo" 0 4
   (fontified t))
 #("charlie" 0 7
   (fontified t))
 #("bravo" 0 5
   (fontified t))
 #("alpha" 0 5
   (fontified t)))


Documentation:
Not documented as a variable.

[back]

Why am I not getting plain strings like I wanted?

  • 3
    You have to escape backslashes in strings, hence your group should be \\([[:word:]]+\\). – mutbuerger Feb 18 '17 at 11:55
  • 2
    Use match-string-no-properties. – politza Feb 18 '17 at 16:38
2

Let me start with some general comments about the code below:

  • Since your code will move point around, it might be a good idea to put the body inside save-excursion so the point will be at the same place where you started when you're finished. But that depends on your application.
  • Use (goto-char (point-min)) instead of 1 (check documentation for reason).
  • You can use a local let-bound variable to pass the list between your functions. Setting and using first-words is not really necessary.
  • As @mutbuerger already mentioned, you have to quote \ in your regexp, hence the final part should look like \\([[:word:]]+\\). You can use the command M-x re-builder RET to test your regexps.
  • As @politza said, use match-string-no-properties to get the results of last search without properties.

Putting it together, I would suggest something like this:

(defun first-words-in-numbered-list ()
  "Gets the first word in lines that start with numbered list and
builds those words into a list, e.g. running the function on the
following text: ..."
  (interactive)
  (let (words)
    (save-excursion
      (goto-char (point-min))
      (while (re-search-forward
              "^[[:digit:]]+[.)]?[[:space:]]+\\([[:word:]]+\\)"
              nil t)
        (push (match-string-no-properties 1) words)))
    words))

(defun print-list-elements (list)
  "Print each element of LIST on a line of its own."
  (while list
    (print (car list))
    (setq list (cdr list))))

(print-list-elements (first-words-in-numbered-list))
  • Please integrate the "other comments above" into your answer (perhaps crediting their authors). Comments can be removed at any time, in which case that reference to them will dangle. – Drew Feb 18 '17 at 18:17
  • @Drew - Thnx, done! – Arash Esbati Feb 19 '17 at 10:07

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.