2009-08-19 9 views
10

Ho un file di registro con caratteri di backspace (^ H). Sto guardando il file in Vim e può essere abbastanza difficile vedere cosa sta succedendo.Come "applicare" caratteri di backspace all'interno di un file di testo (idealmente in vim)

Idealmente mi piacerebbe essere in grado di "applicare" tutti i^H su una determinata linea/intervallo in modo che possa vedere il risultato finale.

Preferisco farlo in Vim su base riga per riga, ma una soluzione che converte l'intero file è meglio di niente.

+0

Per curiosità, qual è lo scopo dei caratteri^H?Cosa intendono raggiungere, sia in una stampa, sia sulla console? –

+0

@John Saunders: Sulle stampanti vecchio stile, hanno causato la sovrastampa che permetteva di eseguire il grassetto ripetendo: x^Hx^Hx^Hx o sottolineato: _^Ha_^Hb_^Hc. "less" su Linux usato per onorare questi e visualizzare il testo in grassetto o sottolineato (forse lo è ancora!) –

+0

Funziona ancora: $ echo 'h_^Inferno^Hlo' | meno –

risposta

12

Attivare l'opzione 'paste' (usando :set paste), quindi premere dd i <CTRL-R> 1 <ESC> su ogni riga che si desidera applicare le backspace a. Questo funziona anche se elimini più righe o anche l'intero file.

La chiave qui è che si sta utilizzando <CTRL-R> 1 in modalità di inserimento di 'Tipo out' il contenuto del registro 1 (dove le linee cancellati ottenuto appena messo), e l'opzione 'paste' impedisce Vim di usare qualsiasi mapping o abbreviazioni.

+1

Grazie! Ho registrato questo (qa) così posso applicarlo a una riga con @a – Draemon

+1

Clever. Mi piace. –

+0

Per voler fare più righe, il secondo 'd' dovrebbe essere cambiato. Ad esempio, per eseguire un intero file di 100 righe, posiziona il cursore in alto, quindi 'd 100 ' – jnovack

0

Basta eliminare tutte le occorrenze di^H (dove è l'interpretazione regex di..):.

:s/.^H//g 

(inserto^H letteralmente immettendo Ctrl-V Ctrl-H)

che verrà applicare alla riga corrente. Usa qualsiasi intervallo desideri se desideri applicarlo ad altre linee.

Dopo aver eseguito un comando :s..., è possibile ripetere su un'altra riga digitando :sg (è necessario g alla fine per riapplicare a tutte le occorrenze sulla riga corrente).

+0

Questo non funzionerà per "somethadr^h^h^hing come questo" – Draemon

+1

Questo non "applica" i backspaces come richiesto. Abbiamo bisogno di cancellare i caratteri che il^H's ar dovrebbe fare anche backspace. –

+0

Non hai visto il punto nella regex? Sono d'accordo che non gestisce una stringa di caratteri backspace, ma elimina il carattere che il backspace sarebbe. – camh

8

risposta semplicistico:

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

dove ^^ H è:

  1. letterale carattere^
  2. Ctrl-V Ctrl-H

e ripetere due volte (fino a quando non ti dirò che non sono state fatte sostituzioni

Se si desidera senza ripetizione, e non ti dispiace utilizzando% perl:

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

Tutti i personaggi sono letterale - vale a dire che non dovete fare ctrl-v ... dovunque nel linea di cui sopra!.

Dovrebbe funzionare nella maggior parte dei casi.

+0

Mi piace, ma mi chiedevo se c'era un modo senza doverlo ripetere (non con regex ovviamente) – Draemon

+0

Certo, qui ce l'hai :) –

+0

'[^^ H]' non sembra giusto. Non corrisponderà a nessun carattere che non sia né "^" né "H"? Qualcosa di più simile a '[. \ A] (? eyelidlessness

0

Come circa la seguente funzione? Ho usato \% x08 invece di^H in quanto è più facile copiare e incollare il codice risultante. Si potrebbe digitarlo e utilizzare Ctrl - VCtrl - H, se si preferisce, ma ho pensato \% x08 potrebbe essere più facile. Questo tenta anche di gestire i backspaces all'inizio della riga (li elimina semplicemente).

" 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 

uso con:

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

Per ulteriori informazioni, vedere:

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

Modifica

Si noti che se si desidera lavorare su una sola linea che, potrebbe fare qualcosa di simile:

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

Oppure si può semplicemente fare:

nmap ,b :.ApplyBackspaces<CR> 
1

Va bene, ecco una soluzione di metallo nudo.

Copia il codice in un file di nome 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); 
} 

compilare il codice con il compilatore preferito:

gcc crush.c -o crush 

Quindi utilizzare in questo modo di schiacciare quei fastidiosi sequenze:

./crush <infilename >outfilename 

Oppure utilizzarlo in una pipeline ("say" è un'app vocale per il testo sul Mac)

man date | ./crush | say 

È possibile copiare cotta nella directory preferita eseguibile (/ usr/local/bin, o qualcosa del genere) e quindi fare riferimento come segue

man date | crush | say 
8

Ho cercato su google questo mentre cercando di ricordare il comando Avevo usato prima di "applicare" i backspaces, e poi me ne sono ricordato: col -b - ecco lo manpage. (Si fa un po 'di più e viene da BSD o più esattamente AT & T UNIX come il man dice, quindi se siete su Linux potrebbe essere necessario installare un pacchetto aggiuntivo, su Debian la sua in bsdmainutils.)

+0

sei un dio !!! – JeffG

+0

hai vinto la discussione :) – Joe

0

Ecco un filtro Bash-based è possibile utilizzare per elaborare l'intero file:

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

si noti che in cui appare ^h, è entrato in vim utilizzando CTRL-v CTRL-h, e il ^^ h viene immesso come SHIFT-6 CTRL-v CTRL-h.

0

Ecco un filtro Awk molto più veloce che fa lo stesso:

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

crushify($0) 

Si noti che in cui appare ^^ H, il primo punto di inserimento in ^^ H è un accento circonflesso (shift-6) e il secondo segno con H viene immesso (in vim) digitando CTRL-v CTRL-H

Problemi correlati