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

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

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

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

Ответ 1

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

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

Ответ 2

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

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

где ^^ H:

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

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

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

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

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

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

Ответ 3

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

Ответ 4

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

Скопируйте этот код в файл с именем 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);
}

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

gcc crush.c -o crush

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

./crush <infilename >outfilename

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

 man date | ./crush | say

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

  man date | crush | say

Ответ 5

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

:s/.^H//g

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

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

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

Ответ 6

Как насчет следующей функции? Я использовал \%x08 вместо ^ H, так как проще скопировать и вставить полученный код. Вы можете ввести его и использовать Ctrl - V Ctrl - 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

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

" 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<CR>

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

nmap ,b :.ApplyBackspaces<CR>

Ответ 7

Здесь фильтр 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.

Ответ 8

Здесь гораздо более быстрый фильтр 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