views:

180

answers:

5

Given the following code sample:

uint8_t i, in, ni;
i = in = 2; ni = 1;
while (2 == i > ni) in++;

How can I replace i, in, and ni, respectively with either in, ni, and i or inni, inin, and nini using emacs, vi, *nix commands, or anything else?

+4  A: 
> cat foo
uint8_t i, in, ni;
i = in = 2; ni = 1;
while (2 == i > ni) in++;
> perl -p -i -e 's/\bi\b/inni/; s/\bin\b/inin/; s/\bni\b/i/;' foo
> cat foo
uint8_t inni, inin, i;
inni = inin = 2; i = 1;
while (2 == inni > i) inin++;
>

You're welcome to use any other tool supporting regular expressions aside from perl.

RarrRarrRarr
+1  A: 

Most easiest way would be to use the replace functionality of gedit.

In vi,

%s/old/new/g will replace all occurrences of "old" with "new" through out the file.

N 1.1
+2  A: 

In vim you can have multiple commands, separated by |, so you could do your replacement with %s command:

:%s/\<i\>/inni/ | %s/\<in\>/inin/ | %s/\<ni\>/nini/

Shortly what it means:

%s/search/replacement/

searches for given search pattern and replaces it with replacement;

symbols "\<" and "\>" are standing here as word boundaries.

Laimoncijus
+4  A: 

If I'm not mistaken, the solutions provided so far (using Perl and Vim) do not work correctly when any of the replacements is among the latter words to be replaced. In particular, none of the solutions works for the first example: "i" would be replaced with "in", which would then be incorrectly replaced with "ni" and then back to "i" by the subsequent rules, while it should stay as "in".

The substitutions cannot be assumed independent and applied in succession; they should be applied in parallel.

In Emacs, you can do this:

M-x parallel-replace,

and at the prompt, enter

i in in ni ni i.

The replacements will happen between the cursor and the end of buffer, or in a region if one is selected.

(Of course, provided you have this definition in your ~/.emacs.d/init.el:-)

(require 'cl)
(defun parallel-replace (plist &optional start end)
  (interactive
   `(,(loop with input = (read-from-minibuffer "Replace: ")
            with limit = (length input)
            for (item . index) = (read-from-string input 0)
                            then (read-from-string input index)
            collect (prin1-to-string item t) until (<= limit index))
     ,@(if (use-region-p) `(,(region-beginning) ,(region-end)))))
  (let* ((alist (loop for (key val . tail) on plist by #'cddr
                      collect (cons key val)))
         (matcher (regexp-opt (mapcar #'car alist) 'words)))
    (save-excursion
      (goto-char (or start (point)))
      (while (re-search-forward matcher (or end (point-max)) t)
        (replace-match (cdr (assoc-string (match-string 0) alist)))))))

Edit: Revised definition accepts unquoted strings.

huaiyuan
@huaiyuan in Vim it is not the case, once "i" was replaced by 1st exoression, it was not replaced by latter ones
Laimoncijus
huaiyuan
+4  A: 

Shorter Emacs solution:

C-M-% (query-replace-regexp)

To match: \<\(i\|in\|ni\)\> (I assume you want to match whole words only)

Replace with: \,(case (intern \1) (i "in") (in "ni") (ni "i"))

You'll need to require 'cl at some point before doing this to get the case macro from the CL package. You could achieve the same effect without that package, but it wouldn't be as terse.

Sean