6

Emacs または Vim で、次の例のように文字列を結合するスムーズな方法は何ですか:

変換元:
    (アルファ、ベータ、ガンマ) 何とか (123, 456, 789)
に:
    (アルファ=123、ベータ=456、ガンマ=789)

次のようにスケーリングする必要があります。

  • これらの多くの行
  • 括弧内の多くの要素

私は最近、この種の変革を頻繁に必要としていることに気づきました。

私はEmacsでEvilを使用しているため、Vimの回答も役立つ可能性があります。

アップデート:

解決策は、私が望んでいたほど一般的ではありませんでした。たとえば、文字列のリストがあり、それらを大きな XML ドキュメントに配布したい場合にも、このソリューションが機能することを望みます。例えば:

<item foo="" bar="barval1"/>
<item foo="" bar="barval2"/>
<item foo="" bar="barval3"/>
<item foo="" bar="barval4"/>

fooval1
fooval2
fooval3
fooval4

解決策を策定し、回答として追加しました。

4

6 に答える 6

2
%s/(\(\S\{-}\), \(\S\{-}\), \(\S\{-}\)).\{-}(\(\S\{-}\), \(\S\{-}\), \(\S\{-}\))/(\1=\4, \2=\5, \3=\6)

%s: グローバル検索と置換

\(\S{-}\),: 後方参照のために "(" で囲まれた、次のコンマまでの非空白文字の貪欲でない検索

\1=\4: 最初の一致、「=」記号、次に 4 番目の一致を出力します

于 2012-12-19T23:55:57.533 に答える
2

このようなテキスト変換には、awk を使用します。

このワンライナーが役立つ場合があります:

awk -F'\\(|\\)' '{split($2,t,",");split($4,v,",");printf "( "; for(x in t)s=s""sprintf("%s=%s, ", t[x],v[x]);sub(", $","",s);printf s")\n";s=""}' file

ちょっとしたテスト:

kent$  cat test
(alpha, beta, gamma) blah (123, 456, 789)
(a, b, c) foo (1, 2, 3)
(x, y, z, m, n) bar (100, 200, 300, 400, 500)

kent$  awk -F'\\(|\\)' '{split($2,t,",");split($4,v,",");printf "( "; for(x in t)s=s""sprintf("%s=%s, ", t[x],v[x]);sub(", $","",s);printf s")\n";s=""}' test

( alpha=123,  beta= 456,  gamma= 789)
( a=1,  b= 2,  c= 3)
(  m= 400,  n= 500, x=100,  y= 200,  z= 300)
于 2012-12-20T01:31:07.593 に答える
1

グーラッシュ王子の答えの Emacs Lisp バージョン

(require 'cl)

(defun split-and-trim (str separator)
  (let ((strs (split-string str separator)))
    (mapcar (lambda (s)
              (replace-regexp-in-string "^\\s-+" "" s))
            (mapcar (lambda (s)
                      (replace-regexp-in-string "\\s-$" "" s)) strs))))

(defun my/merge-list (beg end)
  (interactive "r")
  (goto-char beg)
  (let ((endmark (set-mark end))
        (regexp "(\\([^)]+\\))[^(]+(\\([^)]+\\))"))
    (while (re-search-forward regexp end t)
      (let ((replace-start (match-beginning 0))
            (replace-end   (match-end 0))
            (keys-str (match-string-no-properties 1))
            (values-str (match-string-no-properties 2)))
        (let* ((keys (split-and-trim keys-str ","))
               (values (split-and-trim values-str ",")))
          (while (> (length keys) (length values))
            (setq values (append values '(""))))
          (let* ((pairs (mapcar* (lambda (k v)
                                   (format "%s=%s" k v)) keys values))
                 (transformed (format "(%s)" (mapconcat #'identity pairs ", "))))
            (goto-char replace-start)
            (delete-region replace-start replace-end)
            (insert transformed)))))
    (goto-char (marker-position endmark))))

たとえば、次のようにリージョンを選択します

(alpha, beta, gamma)  blah (123, 456, 789)
(alpha, beta, gamma, delta)  blah (123, 456, 789, aaa)

Mx my/merge-list の後

(alpha=123, beta=456, gamma=789)
(alpha=123, beta=456, gamma=789, delta=aaa)
于 2012-12-20T01:50:34.430 に答える
1

これが Vimscript ソリューションです。ashの回答ほどエレガントではありませんが、任意の長さのリストで機能します。

function! ListMerge()
    " Get line, remove text between lists, split lists at parentheses:
    let curline = getline('.')
    let curline = substitute(curline,')\zs.*\ze(','','g')
    let curline = substitute(curline,'(','','g')
    let lists = map(split(curline,')'),'split(v:val,",")')
    " Return if we don't have two lists of equal length:
    if len(lists) != 2 || len(lists[0]) != len(lists[1])
        return
    endif
    " Loop over the lists, remove whitespace, build the replacement string:
    let i=0
    let string = '('
    while i<len(lists[0])
        let string .= substitute(lists[0][i],'^ *','','')
        let string .= '='
        let string .= substitute(lists[1][i],'^ *','','')
        let string .= ', '
        let i+=1
    endwhile
    " Add the concluding bracket:
    let string = substitute(string,', $',')','')
    " Replace the current line with the string:
    execute "normal! S" . string
endfunction

次に、次のようにすべての行でこの関数を呼び出すことができます。

:%call ListMerge()
于 2012-12-20T00:05:26.597 に答える
1

これから説明するこの方法は少し風変わりですが、管理できる最小限の Elisp コードが必要です。コンマを削除すると、結合されるリストが Lisp リストとして解釈できる場合にのみ適用されます。あなたの例のように、アルファベット文字の数字とシーケンスは問題ありません。

まず、Common Lisp ライブラリがロードされていることを確認します: M-:(require 'cl)RET.

ここで、最初のリストの先頭にあるカーソルから始めます。

M-C-k ; kill-forward-sexp

C-e ; 行末移動

M-C-b ; 逆性指数

M-C-k ; kill-forward-sexp

C-a ; 行頭の移動

C-k ; キルライン

現在blah(または何でも) はキル リングの最初のエントリであり、2 番目のリストは 2 番目のエントリであり、最初のリストは 3 番目のエントリです。

(を入力してからM-:( eval-expression) を入力し、深呼吸をして、次のように入力します。

(loop with (a b) = (mapcar (lambda (x) (car (read-from-string (remove ?, x))))
                     (subseq kill-ring 1 3))
   for x in a for y in b do (insert (format "%s=%s, " y x)))

(プレゼンテーションのために分割していますが、1 行ですべて入力できます。)

そして最後DELDEL)に、あなたは完了です!必要に応じて、マクロに変換できます。

于 2012-12-20T04:44:22.600 に答える
0

私のアプローチは、マッチリストを設定する1つのコマンドを作成し、replace-regexpの既存の\、機能を利用して、マッチリストを配布する2番目のコマンドとしてreplace-regexpを使用することです。

.emacsファイルなどでElispを評価します。

(defvar match-list nil
  "A list of matches, as set through the set-match-list and consumed by the cycle-match-list function. ")
(defvar match-list-iter nil
  "Iterator through the global match-list variable. ")
(defun reset-match-list-iter ()
  "Set match-list-iter to the beginning of match-list and return it. "
  (interactive)
  (setq match-list-iter match-list))
(defun make-match-list (match-regexp use-regexp beg end)
  "Set the match-list variable as described in the documentation for set-match-list. "
  ;; Starts at the beginning of region, searches forward and builds match-list.
  ;; For efficiency, matches are appended to the front of match-list and then reversed
  ;; at the end.
  ;;
  ;; Note that the behavior of re-search-backward is such that the same match-list
  ;; is not created by starting at the end of the region and searching backward.
  (let ((match-list nil))
    (save-excursion
      (goto-char beg)
      (while
          (let ((old-pos (point)) (new-pos (re-search-forward match-regexp end t)))
            (when (equal old-pos new-pos)
              (error "re-search-forward makes no progress.  old-pos=%s new-pos=%s end=%s match-regexp=%s"
                     old-pos new-pos end match-regexp))
            new-pos)
        (setq match-list
              (cons (replace-regexp-in-string match-regexp
                                              use-regexp
                                              (match-string 0)
                                              t)
                    match-list)))
      (setq match-list (nreverse match-list)))))
(defun set-match-list (match-regexp use-regexp beg end)
  "Set the match-list global variable to a list of regexp matches.  MATCH-REGEXP
is used to find matches in the region from BEG to END, and USE-REGEXP is the
regexp to place in the match-list variable.

For example, if the region contains the text: {alpha,beta,gamma}
and MATCH-REGEXP is: \\([a-z]+\\),
and USE-REGEXP is: \\1
then match-list will become the list of strings: (\"alpha\" \"beta\")"
  (interactive "sMatch regexp: \nsPlace in match-list: \nr")
  (setq match-list (make-match-list match-regexp use-regexp beg end))
  (reset-match-list-iter))
(defun cycle-match-list (&optional after-end-string)
  "Return the next element of match-list.

If AFTER-END-STRING is nil, cycle back to the beginning of match-list.
Else return AFTER-END-STRING once the end of match-list is reached."
  (let ((ret-elm (car match-list-iter)))
    (unless ret-elm
      (if after-end-string
          (setq ret-elm after-end-string)
        (reset-match-list-iter)
        (setq ret-elm (car match-list-iter))))
    (setq match-list-iter (cdr match-list-iter))
    ret-elm))
(defadvice replace-regexp (before my-advice-replace-regexp activate)
  "Advise replace-regexp to support match-list functionality. "
  (reset-match-list-iter))

次に、元の問題を解決するには:

M-x set-match-list
Match regexp: \([0-9]+\)[,)]
Place in match-list: \1

M-x replace-regexp
Replace regexp: \([a-z]+\)\([,)]\)
Replace regexp with: \1=\,(cycle-match-list)\2

そして、XMLの例を解決するには:

[Select fooval strings.]
M-x set-match-list
Match regexp: .+
Place in match-list: \&

[Select XML tags.]
M-x replace-regexp
Replace regexp: foo=""
Replace regexp with: foo="\,(cycle-match-list)"
于 2012-12-26T21:26:43.570 に答える