[net.emacs] regular expressions and operations other than replace

mike@peregrine.UUCP (Mike Wexler) (10/18/86)

Since I have switched from vi to EMACS, there is one thing that I missed
more than anything else.  The ability to perform an operation on all
the lines that met a particular criteria(specified by a regular expression).
For instance in vi, I could type in "/[A-Z][a-z]*/d" to delete all lines
that met the specified criteria or I could type in 
"/\([A-Za-z][A-Za-z]*(\).*\()\)/s//\1\2".  How would I do similar operations
in EMACS?



-- 
Mike Wexler
(trwrb|scgvaxd)!felix!peregrine!mike
(714)855-3923

wohler@sri-spam.istc.sri.com (Bill Wohler) (10/19/86)

mike,

i had the same problem you did when i switch to gnuemacs,
but, since gnuemacs is fully extensible, i wrote my first
lisp function (no laughter from the peanut gallery, please).
in addition, emacs offers a richer regular expression set
than vi does.  check it out!

anyway, here's the function that will get you started.  load
it in and bind it to a key if you wish.  currently you have to
set the region around the section of text that you want to
massage and then set your point at the beginning of the
region.  therefore, if you wanted to do the whole file, you
could use C-x h to select the whole buffer.

it works fine, but could be more elegant.  for instance, it
should work on the region even if you aren't at the
beginning of it. if any of you can improve on it, please be
sure to share your findings with us. thanks!

    					--bw
    ----- 8< re-relace-region follows -----
(defun re-replace-region () nil
  (interactive)
  (setq old (read-input "Replace string: " nil))
  (setq new (read-input "with: " nil))
  (while (< (dot) (mark))
    (re-search-forward old (+ (mark) 1) nil)
    (setq end (dot))
    (re-search-backward old)
    (delete-region (dot) end)
    (insert new)
  )
)

evp@lewey.UUCP (10/19/86)

> Since I have switched from vi to EMACS, there is one thing that I missed
> more than anything else.  The ability to perform an operation on all
> the lines that met a particular criteria(specified by a regular expression).
> For instance in vi, I could type in "/[A-Z][a-z]*/d" to delete all lines
> that met the specified criteria or I could type in 
> "/\([A-Za-z][A-Za-z]*(\).*\()\)/s//\1\2".  How would I do similar operations
> in EMACS?

There are several answers to this question.  The easiest way to do
exactly what you want is to pipe the file through sed: set the mark at
the top, move to the bottom of the file, then use (in GNUmacs) the
"shell-command-on-region" command -- ^U ESC-|, passing the appropriate
agruments to sed.  The buffer will be replaced by the results of the sed.

However, this isn't really in the "spirit of EMACS".  When I have to do some
global operation, I usually use a keyboard macro.  Start the macro with ^X-(,
do a single instance of the operation, then ^X-) to terminate the macro
definition.  Repeat it a few times with ^X-e to make sure it works right,
then run it a bunch of times with ^U 999 ^X-e.  If you have a search at the
beginning of the macro, it will stop executing as soon as the search fails.

For more interesting problems, there's the "grep" command.  This allows you
do a search through many files for anything that grep can find. After
execution, calls to the "next-error" command (usually ^X-`) puts the cursor
on the next line containing the grep expression, in whatever file.  If
that line is one that needs to be changed, you can easily generate a
keyboard macro that does the operation then moves to the next instance.

Your last example can be performed almost verbatim with the GNUmacs 
"query-replace-regexp" command, which I bind to ESC-Q.

-- 
Ed Post   {hplabs,voder,pyramid}!lewey!evp
American Information Technology
10201 Torre Ave. Cupertino CA 95014
(408)252-8713

jr@CC5.BBN.COM (John Robinson) (10/20/86)

Then again, you could use this built-in function in the loop:

replace-regexp:
Replace things after point matching REGEXP with TO-STRING.
Preserve case in each match if case-replace and case-fold-search
are non-nil and REGEXP has no uppercase letters.
Third arg DELIMITED (prefix arg if interactive) non-nil means replace
only matches surrounded by word boundaries.
In TO-STRING, \& means insert what matched REGEXP,
and \<n> means insert what matched <n>th \(...\) in REGEXP.

/jr

phr@ernie.Berkeley.EDU (Paul Rubin) (10/20/86)

In article <403@lewey.UUCP> evp@lewey.UUCP (Ed Post) writes:
>> Since I have switched from vi to EMACS, there is one thing that I missed
>> more than anything else.  The ability to perform an operation on all
>> the lines that met a particular criteria(specified by a regular expression).
>> For instance in vi, I could type in "/[A-Z][a-z]*/d" to delete all lines
>> that met the specified criteria or I could type in 
>> "/\([A-Za-z][A-Za-z]*(\).*\()\)/s//\1\2".  How would I do similar operations
>> in EMACS?
>
>There are several answers to this question.  The easiest way to do
>exactly what you want is to pipe the file through sed...

Ugh!!  The first thing to try when figuring out things like this is the
Apropos command.  You can also get more detailed documentation from the
Emacs Info file: type C-h I, then look through the Command Index and
Concept Index nodes til you find what you want.  In this case, you get
the following descriptions.  You can also bring in the Lisp source
for these commands to see how they work or to modify them to do anything
you want on matching (non-matching, etc.) lines.

  File: emacs  Node: Other Repeating Search, Prev: Replace, Up: Search

  Other Search-and-Loop Commands
  ==============================

    Here are some other commands that find matches for a regular expression.
  They all operate from point to the end of the buffer.

  `M-x list-matching-lines'     
       Print each line that follows point and contains a match for the
       specified regexp.  A numeric argument specifies the number of context
       lines to print before and after each matching line; the default is
       none.

  `M-x count-matches'     
       Print the number of matches following point for the specified regexp.

  `M-x delete-non-matching-lines'     
       Delete each line that follows point and does not contain a match for
       the specified regexp.

  `M-x delete-matching-lines'     
       Delete each line that follows point and contains a match for the
       specified regexp.

wunder@hpcea.HP.COM (Walter Underwood) (10/21/86)

> Since I have switched from vi to EMACS, there is one thing that I missed
> more than anything else.  The ability to perform an operation on all
> the lines that met a particular criteria(specified by a regular expression).
> 
> Mike Wexler

Just use keyboard macros.  Start the macro, do a regex search, do the action,
and close the macro.  Give it a very large arg (type ^U ten times) and
then execute the macro.  Voila!  Your buffer has been munged!

I had the same problem when converting from vi, but I find that keyboard
macros are actually more powerful than regular expressions, and easier to
specify.  They are also much easier to teach to a novice.

wunderwood
wunder@hplabs