Сортировка слов (не строк) в VIM
Встроенная команда VIM :sort
сортирует строки текста. Я хочу сортировать слова в одной строке, например. преобразовать линию
b a d c e f
к
a b c d e f
В настоящее время я выполняю это, выбирая строку, а затем используя :!tr ' ' '\n' | sort | tr '\n' ' '
, но я уверен, что там лучше, проще и быстрее. Есть?
Обратите внимание, что я использую bash, поэтому, если для этого есть более короткая и элегантная команда bash, она также прекрасна.
EDIT: Мое использование - это то, что у меня есть строка, которая говорит SOME_VARIABLE="one two three four etc"
, и я хочу, чтобы слова в этой переменной были отсортированы, т.е. я хочу иметь SOME_VARIABLE="etc four one three two"
.
Конечный результат предпочтительно должен отображаться с помощью сочетания клавиш, так как это то, что мне часто нужно.
Ответы
Ответ 1
Используя отличные идеи из ваших ответов, особенно ответ Al, я в итоге придумал следующее:
:vnoremap <F2> d:execute 'normal i' . join(sort(split(getreg('"'))), ' ')<CR>
Это отображает кнопку F2 в режиме visual
, чтобы удалить выделенный текст, разбить, отсортировать и присоединиться к нему, а затем снова вставить его. Когда выбор охватывает несколько строк, это сортирует слова во всех из них и выводит одну отсортированную строку, которую я могу быстро исправить, используя gqq
.
Я буду рад услышать предложения о том, как это можно улучшить.
Большое спасибо, я многому научился:)
EDIT: изменено '<C-R>"'
на getreg('"')
для обработки текста с помощью char '
в нем.
Ответ 2
В чистом vim вы можете сделать это:
call setline('.', join(sort(split(getline('.'), ' ')), " "))
Изменить
Чтобы сделать это, чтобы он работал над диапазоном, который меньше одной строки, немного сложнее (это позволяет либо сортировать несколько строк по отдельности, либо сортировать часть одной строки в зависимости от визуального выбора):
command! -nargs=0 -range SortWords call SortWords()
" Add a mapping, go to your string, then press vi",s
" vi" selects everything inside the quotation
" ,s calls the sorting algorithm
vmap ,s :SortWords<CR>
" Normal mode one: ,s to select the string and sort it
nmap ,s vi",s
function! SortWords()
" Get the visual mark points
let StartPosition = getpos("'<")
let EndPosition = getpos("'>")
if StartPosition[0] != EndPosition[0]
echoerr "Range spans multiple buffers"
elseif StartPosition[1] != EndPosition[1]
" This is a multiple line range, probably easiest to work line wise
" This could be made a lot more complicated and sort the whole
" lot, but that would require thoughts on how many
" words/characters on each line, so that can be an exercise for
" the reader!
for LineNum in range(StartPosition[1], EndPosition[1])
call setline(LineNum, join(sort(split(getline('.'), ' ')), " "))
endfor
else
" Single line range, sort words
let CurrentLine = getline(StartPosition[1])
" Split the line into the prefix, the selected bit and the suffix
" The start bit
if StartPosition[2] > 1
let StartOfLine = CurrentLine[:StartPosition[2]-2]
else
let StartOfLine = ""
endif
" The end bit
if EndPosition[2] < len(CurrentLine)
let EndOfLine = CurrentLine[EndPosition[2]:]
else
let EndOfLine = ""
endif
" The middle bit
let BitToSort = CurrentLine[StartPosition[2]-1:EndPosition[2]-1]
" Move spaces at the start of the section to variable StartOfLine
while BitToSort[0] == ' '
let BitToSort = BitToSort[1:]
let StartOfLine .= ' '
endwhile
" Move spaces at the end of the section to variable EndOfLine
while BitToSort[len(BitToSort)-1] == ' '
let BitToSort = BitToSort[:len(BitToSort)-2]
let EndOfLine = ' ' . EndOfLine
endwhile
" Sort the middle bit
let Sorted = join(sort(split(BitToSort, ' ')), ' ')
" Reform the line
let NewLine = StartOfLine . Sorted . EndOfLine
" Write it out
call setline(StartPosition[1], NewLine)
endif
endfunction
Ответ 3
Здесь эквивалент в чистом vimscript:
:call setline('.',join(sort(split(getline('.'),' ')),' '))
Это не короче или проще, но если вы часто это делаете, вы можете запускать его по целому ряду строк:
:%call setline('.',join(sort(split(getline('.'),' ')),' '))
Или сделайте команду
:command -nargs=0 -range SortLine <line1>,<line2>call setline('.',join(sort(split(getline('.'),' ')),' '))
Что вы можете использовать с
:SortLine
:'<,'>SortLine
:%SortLine
и т.д.
Ответ 4
:!perl -ne '$,=" ";print sort split /\s+/'
Не уверен, требует ли это объяснения, но если да:
perl -ne ''
запускает все, что находится внутри '' для каждой строки ввода, - помещая строку в переменную по умолчанию $_.
$,=" ";
Устанавливает разделитель выходных данных в пробел. Например:
=> perl -e 'print 1,2,3'
123
=> perl -e '$,=" ";print 1,2,3'
1 2 3
=> perl -e '$,=", ";print 1,2,3'
1, 2, 3
Довольно просто.
print sort split /\s+/
Укороченная версия:
print( sort( split( /\s+/, $_ ) ) )
($ _ в конце - переменная по умолчанию).
split - разбивает $_ на массив с использованием заданного regexp, сортирует данный список, печатает - печатает его.
Ответ 5
Возможно, вы предпочитаете Python:
!python -c "import sys; print ' '.join(sorted(sys.stdin.read().split()))"
Визуальный текст выбора и выполните эту строку.
Ответ 6
My Плагин AdvancedSorters теперь имеет команду :SortWORDs
, которая делает это (среди других команд, связанных с сортировкой).