Как я могу поменять или заменить несколько строк в коде одновременно? - PullRequest
7 голосов
/ 07 апреля 2010

Учитывая следующий пример кода:

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

Как я могу заменить i, in, and ni, соответственно, либо in, ni, and i, либо inni, inin, and nini, используя команды emacs, vi, * nix или что-то еще?

Ответы [ 5 ]

13 голосов
/ 07 апреля 2010

Сокращенное решение Emacs:

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

Для соответствия: \ <\ (i \ | in \ | ni \) \> (я предполагаю, что вы хотите соответствовать только целым словам)

Заменить на: \, (case (intern \ 1) (i "in") (in "ni") (ni "i"))

Вам нужно будет require 'cl в какой-то момент, прежде чем делать это, чтобы получить макрос case из пакета CL. Без этого пакета вы могли бы добиться того же эффекта, но это было бы не так кратко.

РЕДАКТИРОВАНИЕ ДЛЯ ДОБАВЛЕНИЯ: На самом деле, это может быть так кратко, как я понял, когда недавно отвечал на аналогичный вопрос о Reddit.

Для соответствия: \<\(?:\(i\)\|\(in\)\|ni\)\>

Заменить на: \,(if \1 "in" (if \2 "ni" "i"))

11 голосов
/ 07 апреля 2010

Если я не ошибаюсь, представленные решения (с использованием Perl и Vim) не работают должным образом, если среди последних слов, подлежащих замене, есть какие-либо замены. В частности, ни одно из решений не работает для первого примера: «i» будет заменено на «in», которое затем будет некорректно заменено на «ni», а затем последующими правилами вернется к «i», в то время как оно должно остаться как в".

Замены не могут считаться независимыми и применяться последовательно; они должны применяться параллельно.

В Emacs вы можете сделать это:

М-х параллельная замена ,

и в командной строке введите

i in in ni ni i .

Замена будет происходить между курсором и концом буфера или в области, если она выбрана.

(При условии, что у вас есть это определение в вашем ~/.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 (2013-08-20):

Несколько улучшений:

  1. Для особого случая, когда даны только два элемента, вместо этого выполнить своп (т. Е. Заменить друг друга);
  2. Запросите подтверждение для каждой замены таким же образом, как и query-replace.
(require 'cl)
(defun parallel-query-replace (plist &optional delimited start end)
  "Replace every occurrence of the (2n)th token of PLIST in
buffer with the (2n+1)th token; if only two tokens are provided,
replace them with each other (ie, swap them).

If optional second argument DELIMITED is nil, match words
according to syntax-table; otherwise match symbols.

When called interactively, PLIST is input as space separated
tokens, and DELIMITED as prefix arg."
  (interactive
   `(,(loop with input = (read-from-minibuffer "Replace: ")
            with limit = (length input)
            for  j = 0 then i
            for (item . i) = (read-from-string input j)
            collect (prin1-to-string item t) until (<= limit i))
     ,current-prefix-arg
     ,@(if (use-region-p) `(,(region-beginning) ,(region-end)))))
  (let* ((alist (cond ((= (length plist) 2) (list plist (reverse plist)))
                      ((loop for (key val . tail) on plist by #'cddr
                             collect (list (prin1-to-string key t) val)))))
         (matcher (regexp-opt (mapcar #'car alist)
                              (if delimited 'words 'symbols)))
         (to-spec `(replace-eval-replacement replace-quote
                    (cadr (assoc-string (match-string 0) ',alist
                                        case-fold-search)))))
    (query-replace-regexp matcher to-spec nil start end)))
4 голосов
/ 07 апреля 2010
> 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++;
>

Вы можете использовать любой другой инструмент, поддерживающий регулярные выражения, кроме perl.

2 голосов
/ 07 апреля 2010

В vim вы можете иметь несколько команд, разделенных |, поэтому вы можете выполнить замену командой %s:

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

Вкратце, что это значит:

%s/search/replacement/

ищет данный шаблон search и заменяет его на replacement;

символы "\<" и "\>" стоят здесь как границы слов.

1 голос
/ 07 апреля 2010

Самый простой способ - использовать функциональность замены gedit.

В vi,

%s/old/new/g заменит все вхождения "old" на "new" из файла.

...