Determina la lunghezza delle schede '\ t' su una linea


10

In un campo di elaborazione del testo c'è un modo per sapere se una scheda è lunga 8 caratteri (la lunghezza predefinita) o meno?

Ad esempio, se ho un file di esempio con delimitatore di tabulazione e il contenuto di un campo si adatta in meno di una tabulazione (≤7), e se dopo ho una tabulazione, quella tab sarà solo "dimensione tab - dimensione campo ' in lunghezza.

C'è un modo per ottenere la lunghezza totale delle schede su una linea? Non sto cercando il numero di schede (ovvero 10 schede non devono restituire 10) ma la lunghezza del carattere di quelle schede.

Per i seguenti dati di input (scheda delimitata tra i campi e solo una scheda):

field0  field00 field000        last-field
fld1    fld11   fld001  last-fld
fd2     fld3    last-fld

Mi aspetto di contare la lunghezza delle schede in ogni riga, quindi

11
9
9

Risposte:


22

Il TABpersonaggio è un carattere di controllo che, quando inviato a un terminale¹, sposta il cursore del terminale al tab-stop successivo. Per impostazione predefinita, nella maggior parte dei terminali, le tabulazioni sono separate da 8 colonne, ma è configurabile.

Puoi anche avere tabulazioni a intervalli irregolari:

$ tabs 3 9 11; printf '\tx\ty\tz\n'
  x     y z

Solo il terminale sa quante colonne a destra una TAB sposta il cursore.

È possibile ottenere tali informazioni eseguendo una query sulla posizione del cursore dal terminale prima e dopo l'invio della scheda.

Se vuoi fare quel calcolo a mano per una data linea e supponendo che quella linea sia stampata nella prima colonna dello schermo, dovrai:

  • sapere dove sono le tabulazioni²
  • conoscere la larghezza di visualizzazione di ogni personaggio
  • conoscere la larghezza dello schermo
  • decidere se si desidera gestire altri caratteri di controllo come \r(che sposta il cursore sulla prima colonna) o \bche sposta il cursore indietro ...)

Può essere semplificato se si presume che le tabulazioni siano ogni 8 colonne, la linea si adatti allo schermo e non vi siano altri caratteri di controllo o caratteri (o non caratteri) che il terminale non può visualizzare correttamente.

Con GNU wc, se la linea è memorizzata in $line:

width=$(printf %s "$line" | wc -L)
width_without_tabs=$(printf %s "$line" | tr -d '\t' | wc -L)
width_of_tabs=$((width - width_without_tabs))

wc -Lfornisce la larghezza della linea più larga nel suo input. Lo fa usando wcwidth(3)per determinare la larghezza dei caratteri e supponendo che le tabulazioni siano ogni 8 colonne.

Per i sistemi non GNU, e con le stesse ipotesi, vedi l'approccio di @ Kusalananda . È ancora meglio in quanto consente di specificare i punti di tabulazione ma purtroppo attualmente non funziona con GNU expand(almeno) quando l'input contiene caratteri multi-byte o 0-larghezza (come la combinazione di caratteri) o caratteri a doppia larghezza.


¹ notare che, in tal caso stty tab3, la disciplina della linea del dispositivo tty assumerà il controllo dell'elaborazione della scheda (converte TAB in spazi in base alla propria idea di dove potrebbe essere il cursore prima di inviarlo al terminale) e implementa le tabulazioni ogni 8 colonne. Test su Linux, sembra gestire correttamente i caratteri CR, LF e BS, nonché quelli UTB-8 multibyte (a condizione che iutf8sia attivo), ma questo è tutto. Presuppone che tutti gli altri caratteri non di controllo (inclusi i caratteri a larghezza zero, a doppia larghezza) abbiano una larghezza di 1, (ovviamente) non gestisce le sequenze di escape, non si avvolge correttamente ... Probabilmente è destinato a terminali che impossibile eseguire l'elaborazione delle schede.

In ogni caso, la disciplina della linea tty ha bisogno di sapere dove si trova il cursore e usa quelle euristiche sopra, perché quando si utilizza l' icanoneditor di linee (come quando si immette il testo per applicazioni come catquelle non implementano il proprio editor di linee), quando si premere TabBackspace, la disciplina di linea deve sapere quanti caratteri BS inviare per cancellare quel carattere Tab per la visualizzazione. Se cambi dove si trovano le tabulazioni (come con tabs 12), noterai che le schede non vengono cancellate correttamente. Lo stesso se si immettono caratteri a doppia larghezza prima di premere TabBackspace.


² Per questo, è possibile inviare caratteri di tabulazione e interrogare la posizione del cursore dopo ognuno. Qualcosa di simile a:

tabs=$(
  saved_settings=$(stty -g)
  stty -icanon min 1 time 0 -echo
  gawk -vRS=R -F';' -vORS= < /dev/tty '
    function out(s) {print s > "/dev/tty"; fflush("/dev/tty")}
    BEGIN{out("\r\t\33[6n")}
    $NF <= prev {out("\r"); exit}
    {print sep ($NF - 1); sep=","; prev = $NF; out("\t\33[6n")}'
  stty "$saved_settings"
)

Quindi, puoi usarlo come expand -t "$tabs"usando la soluzione di @ Kusalananda.


7
$ expand file | awk '{ print gsub(/ /, " ") }'
11
9
9

L' expandutilità POSIX espande le schede negli spazi. Lo awkscript conta e genera il numero di sostituzioni necessarie per sostituire tutti gli spazi su ciascuna riga.

Per evitare di contare eventuali spazi preesistenti nel file di input:

$ tr ' ' '@' <file | expand | awk '{ print gsub(/ /, " ") }'

dove @è garantito un carattere che non esiste nei dati di input.

Se si desidera 10 spazi per scheda anziché l'ordinario 8:

$ tr ' ' '@' <file | expand -t 10 | awk '{ print gsub(/ /, " ") }'
9 
15
13

3
Vorresti sostituire gli spazi con qualche altro carattere di una larghezza (come x) prima di chiamare expandaltrimenti, conteresti anche gli spazi che erano inizialmente nell'input.
Stéphane Chazelas,

1
expandpresuppone anche tab-stop ogni 8 colonne (sebbene sia possibile modificarlo con le opzioni). Si noti che l'implementazione GNU non supporta i caratteri multi-byte (per non parlare di quelli a larghezza 0 o doppia). IIRC quello di FreeBSD è OK.
Stéphane Chazelas,

@ StéphaneChazelas A meno che, ovviamente, non faccia parte del piano per contare la larghezza degli 0x09 negli 0x20 ;-)
can-ned_food

2

Con perl:

perl -F/\\t/ -lpe '$c = 0; $F[-1] eq "" or pop @F; $_ = (map { $c += 8 - (length) % 8 } @F)[-1]' file

In alternativa:

perl -MList::Util=reduce -lpe \
    '@F = split /\t/, $_, -1; pop @F if $F[-1] ne ""; $_ = reduce { $a + $b } map { 8 - (length) % 8 } @F' file

È possibile modificare 8 sopra con qualche altro valore se si desidera che i TAB abbiano una lunghezza diversa.


2

Anche usando expand, ma con la manipolazione dei parametri bash per contare il numero di spazi:

$ line=$'field0\tfield00\tfield000\tlast-field'
$ tabs2spaces=$(expand <<<"$line")
$ only_spaces=${tabs2spaces//[^ ]/}    # remove all non-space characters
$ echo "${#only_spaces}"
11
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.