Как "применить" обратные символы в текстовом файле (в идеале в vim)

У меня есть файл журнала с обратными символами в нем (^ H). Я просматриваю файл в Vim, и может быть довольно сложно понять, что происходит.

В идеале я бы хотел "применить" все ^ H в заданной строке/диапазоне, чтобы я мог видеть конечный результат.

Я бы предпочел сделать это внутри Vim по очереди, но решение, которое преобразует весь файл, лучше, чем ничего.

8 ответов

Включите параметр "вставить" (используя :set paste), а затем нажмите dd i <ctrl-r> 1 </ctrl-r> в каждой строке, к которой вы хотите применить обратные пространства. Это также работает, если вы удаляете несколько строк или даже весь файл.

Ключ здесь состоит в том, что вы используете <ctrl-r> 1</ctrl-r> в режиме вставки, чтобы "вывести" содержимое регистра 1 (где только что удалили ваши удаленные строки) и опция "вставить" предотвращает Vim из любых сопоставлений или сокращений.


Я искал это, пытаясь вспомнить команду, которую я использовал раньше, чтобы "применить" обратные пространства, а затем я это вспомнил: col -b - вот страницы руководства. (Он делает немного больше и исходит из BSD или более точно AT & T UNIX, поскольку manpage говорит, поэтому, если вы находитесь в Linux, вам может потребоваться установить дополнительный пакет, на debian в bsdmainutils.)


Упрощенный ответ:

:%s/[^^H]^H//g

где ^^ H:

  • Литеральный символ
  • Ctrl-V Ctrl-H

и повторите его пару раз (пока vim не скажет вам, что никаких замещений не было сделано

Если вы хотите без повторения, и вы не против использовать%! perl:

%!perl -0pe 's{([^\x08]+)(\x08+)}{substr$1,0,-length$2}eg'

Все символы являются буквальными - т.е. вам не нужно делать ctrl-v... в любом месте над строкой.

Должен работать в большинстве случаев.


Хорошо, вот голое решение.

Скопируйте этот код в файл с именем crush.c:

#include <stdio.h>
// crush out x^H sequences
// there was a program that did this, once
// cja, 16 nov 09
main()
{
 int c, lc = 0;
 while ((c = getchar()) != EOF) {
 if (c == '\x08')
 lc = '\0';
 else {
 if (lc)
 putchar(lc);
 lc = c;
 }
 }
 if (lc)
 putchar(lc);
}
</stdio.h>

Скомпилируйте этот код с вашим любимым компилятором:

gcc crush.c -o crush

Затем используйте его так, чтобы сокрушить эти назойливые последовательности:

./crush <infilename>outfilename
</infilename>

Или использовать его в конвейере ( "скажем" - это приложение "речь-текст" на Mac)

man date | ./crush | say

Вы можете скопировать раздачу в ваш любимый исполняемый каталог (/usr/local/bin или некоторые такие), а затем ссылаться на него следующим образом

man date | crush | say


Просто удалите все вхождения. ^ H (где. является интерпретацией регулярных выражений.):

:s/.^H//g

(вставьте ^ H буквально, введя Ctrl-V Ctrl-H)

Это применимо к текущей строке. Используйте любой диапазон, который вы хотите, если хотите применить его к другим строкам.

Как только вы выполнили одну команду :s..., вы можете повторить на другой строке, просто набрав :sg (вам нужно, чтобы g в конце повторного применения ко всем вхождениям в текущей строке).


Здесь гораздо более быстрый фильтр Awk, который делает то же самое:

#!/usr/bin/awk -f
function crushify(data) {
 while (data ~ /[^^H]^H/) {
 gsub(/[^^H]^H/, "", data) 
 } 
 print data
}
crushify($0)

Обратите внимание, что, когда появляется ^^ H, первая каретка в ^^ H является кареткой (shift-6), а вторая каретка с H вводится (в vim), набирая CTRL-v CTRL-H


Как насчет следующей функции? Я использовал \%x08 вместо ^ H, так как проще скопировать и вставить полученный код. Вы можете ввести его и использовать Ctrl - VCtrl - H, если хотите, но я думал, что \%x08 может быть проще. Это также пытается обрабатывать промежутки в начале строки (они просто удаляют их).

" Define a command to make it easier to use (default range is whole file)
command! -range=% ApplyBackspaces <line1>,<line2>call ApplyBackspaces()
" Function that does the work
function! ApplyBackspaces() range
 " For each line in the selected lines
 for index in range(a:firstline, a:lastline)
 " Get the line as a string
 let thisline = getline(index)
 " Remove backspaces at the start of the line
 let thisline = substitute(thisline, '^\%x08*', '', '')
 " Repeatedly apply backspaces until there are none left
 while thisline =~ '.\%x08'
 " Substitute any character followed by backspace with nothing
 let thisline = substitute(thisline, '.\%x08', '', 'g')
 endwhile
 " Remove any backspaces left at the start of the line
 let thisline = substitute(thisline, '^\%x08*', '', '')
 " Write the line back
 call setline(index, thisline)
 endfor
endfunction
</line2></line1>

Использовать с:

" Whole file:
:ApplyBackspaces
" Whole file (explicitly requested):
:%ApplyBackspaces
" Visual range:
:'<,'>ApplyBackspaces

Для получения дополнительной информации см.

:help command
:help command-range
:help function
:help function-range-example
:help substitute()
:help =~
:help \%x

Изменить

Обратите внимание: если вы хотите работать с одной строкой, вы можете сделать что-то вроде этого:

" Define the command to default to the current line rather than the whole file
command! -range ApplyBackspaces <line1>,<line2>call ApplyBackspaces()
" Create a mapping so that pressing ,b in normal mode deals with the current line
nmap ,b :ApplyBackspaces</line2></line1>

или вы можете просто сделать:

nmap ,b :.ApplyBackspaces


Здесь фильтр Bash, который вы можете использовать для обработки всего файла:

#!/bin/bash
while read LINE; do
 while [[ "$LINE" =~ '^H' ]]; do
 LINE="${LINE/[^^H]^H/}"
 done 
 echo "$LINE"
done

Обратите внимание, что там, где появляется ^ H, он вводится в vim с помощью CTRL-v CTRL-h, а ^^ H вводится как SHIFT-6 CTRL-v CTRL-h.

licensed under cc by-sa 3.0 with attribution.