How to extract regex matches using Vim

后端 未结 5 1729
悲哀的现实
悲哀的现实 2020-12-08 15:52

An example:

case Foo:
    ...
    break;
case Bar:
    ...
    break;
case More: case Complex:
    ...
    break:
...

I’d like to retrieve al

相关标签:
5条回答
  • 2020-12-08 16:22
    :g/^case\s\L\l\+\scase.*/s/case/\r&/g
    :let @a=''|g/^case\s\L\l\+:/y A
    

    Now open a new buffer or tmp file, and aply:

    "ap
    :%s_^\vcase ([^:]+):_\1_
    

    Or if you don't care for your current buffer (you can undo this of course) (updated for the complex example):

    :g/^case\s\L\l\+\scase.*/s/case/\r&/g
    :v/^case\s\L\l\+:/d
    :%s_^\vcase ([^:]+):_\1_
    
    0 讨论(0)
  • 2020-12-08 16:30

    There is a general way of collecting pattern matches throughout a piece of text. The technique takes advantage of the substitute with an expression feature of the :substitute command (see :help sub-replace-\=). The key idea is to use a substitution enumerating all of the pattern matches to evaluate an expression storing them without replacement.

    First, let us consider saving the matches. In order to keep a sequence of matching text fragments, it is convenient to use a list (see :help List). However, it is not possible to modify a list straightforwardly, using the :let command, since there is no way to run Ex commands in expressions (including \= substitute expressions). Yet, we can call one of the functions that modify a list in place. For example, the add() function is designed to append a given item to the specified list (see :help add()).

    Another problem is how to avoid text modifications while running a substitution. One approach is to make the pattern always have a zero-width match by prepending \ze or by appending \zs atoms to it (see :help /\zs, :help /\ze). The pattern modified in this way captures an empty string preceding or succeeding an occurrence of the original pattern in text (such matches are called zero-width matches in Vim; see :help /zero-width). Then, if the replacement text is also empty, substitution effectively changes nothing: it just replaces a zero-width match with an empty string.

    Since the add() function, as well as the most of the list modifying functions, returns the reference to the changed list, for our technique to work, we need to somehow get an empty string from it. The simplest way is to extract a sublist of zero length from it by specifying a range of indices such that a starting index is greater than an ending one.

    Combining the aforementioned ideas, we obtain the following Ex command.

    :let t=[] | %s/\<case\s\+\(\w\+\):\zs/\=add(t,submatch(1))[1:0]/g
    

    After its execution, all matches of the first subgroup are accumulated in the list referenced by the variable t, and can be used as is or processed in some way. For instance, to paste contents of the list one by one on separate lines in Insert mode, type

    Ctrl+R=tEnter

    To do the same in Normal mode, simply use the :put command:

    :pu=t
    
    0 讨论(0)
  • 2020-12-08 16:32

    Though it's not possible to write a one-liner to accomplish your example, it's hard to type commands such as :%s/case \([^:]*\):/\=.../ interactively.

    I prefer using vim-grex with the following steps:

    1. Use / to check whether a regular expression matches to expected lines. For example: /^\s*\<case\s\+\([^:]*\):.*$<Enter>
    2. Execute :Grey. It yanks lines matched to the current search pattern.
    3. Open a new buffer by :new etc.
    4. Put the yanked lines by p etc.
    5. Trim uninteresting parts by :%s//\1/.
    0 讨论(0)
  • 2020-12-08 16:34

    How to use vim regex to extract the word from the following line, given that 'help' might be any word like 'rust' or 'perlang'.

    vim:tw=78:ts=8:ft=help:norl:
    

    Solution:

    let foo = substitute(foo, '^\s*vim:.*:ft=\([a-z]\+\).*:\s*$', '\1', '')
    echo "foo: '" . foo . "'"
    

    Prints:

    foo: 'help'
    

    Guru meditation: What's going on here?

    Take the string in the variable foo and match it to assert the beginning of the line, then any number of spaces, the literal vim and a literal colon, then any number of any characters followed by colon ft= with any word with letters, then anything, and assert the line ends with a colon. Throw all that into a register named 1, then get that back in parameter 2 which substitute takes on and replaces the prior string with.

    As a general philosophy, any regex longer than your finger on the screen is an epic fail, so decrease screen resolution until it fits.

    0 讨论(0)
  • As small addition to ib.'s accepted answer, which works well as is. It seems like the flag n is enough avoid the issues with unwanted substitution.

    :let t=[] | %s/\<case\s\+\(\w\+\):/\=add(t,submatch(1))/gn
    

    From the s_flag help:

    [n] Report the number of matches, do not actually substitute. The [c] flag is ignored. The matches are reported as if 'report' is zero. Useful to count-items. If \= sub-replace-expression is used, the expression will be evaluated in the sandbox at every match.

    0 讨论(0)
提交回复
热议问题