0

Is there a way to operate on a specific column of data without specifying the exact column number but only the column number? For example, if I have data like this:

abc   123  45.6  jkq
df    456   7.8  sfladf
ghij   92  98.0  adad

and I want to do something on the third column (whitespace delimited), for example a search and replace. How can I do that?

I know I can use

:%s/\%25c\d\+/xyz/

or something like that if I know the exact column is 25, but if I do not know the exact column and just specify the 3rd column which is whitespace delimited, how can I do that?

2

I think this sounds like a job for awk.

$ awk 'sub(/[0-9]+/, "foo", $3)' input.txt > output.txt

Using inside of Vim via filter, :!:

:%!awk 'sub(/[0-9]+/, "foo", $3)'

Note: this will effect the output whitespace. May want to adjust OFS.

  • +1 for the use of awk! Unfortunately, it isn't always available – Christian Brabandt Feb 21 '18 at 20:04
1

This is a very very rough sketch of how to do it:

First construct your search pattern, that is add the appropriate number of whitespace delimited columns in front of your pattern, then add your pattern then add something to fix the pattern at the next whitespace separated pattern.

Here is a simple proof of concept, that needs a lot more error management and probably also fix a couple of corner cases (one being how to find the same regex several times in your column)

function! GetColumnPat(pat, nr)
    let start='^\%(\S\+\s\+\)'
    let end='\S*'
    let pat=start.
    \ '\{'.(a:nr-1).'\}'.
    \ '.\{-\}\zs'. a:pat. '\ze.\{-\}'.
    \ end
    return pat
endfunction

function! SearchC(arg)
    try
        let args=split(a:arg, a:arg[0])
        if len(args) != 2
            throw "unexpected input"
        endif
    catch
        echo "Unexpected input"
    endtry
    let arg=escape(args[0], '/')
    let a=GetColumnPat(arg, args[1])
    let @/=a
    try
        exe "norm! n"
    catch /^Vim\%((\a\+)\)\=:E486/
        echoerr "Pattern '". a:args[0]. "not found"
    endtry
endfu

:com! -nargs=+ SearchInColumn :call SearchC(<q-args>)

Now you can use :SearchInColumn /foobar/4 to search for foobar in the fourth column.

I implemented a similar command in my csv plugin, see the definition of the SearchColumn() function. It still suffers from the find several times the same thing in the same column problem and I haven't found a good way around it. Despite that, it surprisingly works well in enough in practice and I haven't received many complaints.

Another approach might be to visually select the region you need to search, open it up in a new window and do your search (and possible replace) there. You can use my NrrwRgn plugin for that

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.