Déterminer la longueur des onglets '\ t' sur une ligne


10

Dans un champ de traitement de texte, existe-t-il un moyen de savoir si un onglet est de 8 caractères (la longueur par défaut) ou moins?

Par exemple, si j'ai un exemple de fichier avec un délimiteur de tabulation et que le contenu d'un champ tient dans moins d'un onglet (≤7), et si j'ai un onglet après cela, alors cet onglet ne sera que 'taille de l'onglet - taille du champ ' en longueur.

Existe-t-il un moyen d'obtenir la longueur totale des onglets sur une ligne? Je ne cherche pas le nombre d'onglets (c'est-à-dire que 10 onglets ne devraient pas retourner 10) mais la longueur des caractères de ces onglets.

Pour les données d'entrée suivantes (tabulation délimitée entre les champs et un seul onglet):

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

Je m'attends à compter la longueur des onglets dans chaque ligne, donc

11
9
9

Réponses:


22

Le TABcaractère est un caractère de contrôle qui, lorsqu'il est envoyé à un terminal¹, fait passer le curseur du terminal au prochain taquet de tabulation. Par défaut, dans la plupart des terminaux, les taquets de tabulation sont espacés de 8 colonnes, mais c'est configurable.

Vous pouvez également avoir des taquets de tabulation à intervalles irréguliers:

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

Seul le terminal sait combien de colonnes à droite un TAB déplacera le curseur.

Vous pouvez obtenir ces informations en interrogeant la position du curseur depuis le terminal avant et après l'envoi de l'onglet.

Si vous souhaitez effectuer ce calcul à la main pour une ligne donnée et en supposant que cette ligne est imprimée dans la première colonne de l'écran, vous devrez:

  • savoir où sont les taquets de tabulation²
  • connaître la largeur d'affichage de chaque caractère
  • connaître la largeur de l'écran
  • décidez si vous voulez gérer d'autres caractères de contrôle comme \r(qui déplace le curseur vers la première colonne) ou \bqui ramène le curseur en arrière ...)

Cela peut être simplifié si vous supposez que les taquets de tabulation sont toutes les 8 colonnes, la ligne tient dans l'écran et il n'y a pas d'autres caractères de contrôle ou caractères (ou non-caractères) que votre terminal ne peut pas afficher correctement.

Avec GNU wc, si la ligne est stockée dans $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 -Ldonne la largeur de la ligne la plus large dans son entrée. Il le fait en utilisant wcwidth(3)pour déterminer la largeur des caractères et en supposant que les taquets de tabulation sont toutes les 8 colonnes.

Pour les systèmes non GNU, et avec les mêmes hypothèses, voir l'approche de @ Kusalananda . C'est encore mieux car il vous permet de spécifier les taquets de tabulation mais malheureusement ne fonctionne actuellement pas avec GNU expand(au moins) lorsque l'entrée contient des caractères multi-octets ou 0-largeur (comme la combinaison de caractères) ou des caractères double-largeur.


¹ Notez cependant que si vous le faites stty tab3, la discipline de ligne de périphérique tty prendra en charge le traitement des onglets (convertira TAB en espaces en fonction de sa propre idée de l'endroit où le curseur pourrait être avant d'envoyer au terminal) et implémentera des arrêts de tabulation toutes les 8 colonnes. Test sous Linux, il semble gérer correctement les caractères CR, LF et BS ainsi que les caractères UTF-8 multi-octets (fournis iutf8également), mais c'est à peu près tout. Il suppose que tous les autres caractères non contrôlés (y compris les caractères à largeur nulle et à double largeur) ont une largeur de 1, il (évidemment) ne gère pas les séquences d'échappement, ne s'emballe pas correctement ... Cela est probablement destiné aux terminaux qui impossible de traiter les onglets.

Dans tous les cas, la discipline de ligne tty a besoin de savoir où se trouve le curseur et utilise ces heuristiques ci-dessus, car lorsque vous utilisez l' icanonéditeur de ligne (comme lorsque vous entrez du texte pour des applications comme catcelles-ci n'implémentent pas leur propre éditeur de ligne), lorsque vous appuyez sur TabBackspace, la discipline de ligne doit savoir combien de caractères BS envoyer pour effacer ce caractère de tabulation à afficher. Si vous changez l'emplacement des taquets de tabulation (comme avec tabs 12), vous remarquerez que les tabulations ne sont pas effacées correctement. Idem si vous entrez des caractères double largeur avant d'appuyer sur TabBackspace.


² Pour cela, vous pouvez envoyer des caractères de tabulation et interroger la position du curseur après chacun. Quelque chose comme:

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

Ensuite, vous pouvez utiliser cela comme expand -t "$tabs"utilisant la solution de @ Kusalananda.


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

L' expandutilitaire POSIX étend les tabulations en espaces. Le awkscript compte et génère le nombre de substitutions nécessaires pour remplacer tous les espaces sur chaque ligne.

Pour éviter de compter les espaces préexistants dans le fichier d'entrée:

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

@est un caractère dont il est garanti qu'il n'existe pas dans les données d'entrée.

Si vous voulez 10 espaces par tabulation au lieu des 8 ordinaires:

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

3
Vous voudriez remplacer les espaces par un autre caractère d'une largeur (comme x) avant d'appeler expandautrement, vous compteriez également les espaces qui étaient initialement dans l'entrée également.
Stéphane Chazelas

1
expandsuppose également des tabulations toutes les 8 colonnes (bien que vous puissiez changer cela avec des options). Notez que l'implémentation GNU ne prend pas en charge les caractères multi-octets (sans parler des caractères de largeur 0 ou double largeur). IIRC celui de FreeBSD est OK.
Stéphane Chazelas

@ StéphaneChazelas À moins, bien sûr, que cela fasse partie du plan pour compter la largeur des 0x09 avec les 0x20 ;-)
can-ned_food

2

Avec perl:

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

Alternativement:

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

Vous pouvez changer 8 ci-dessus avec une autre valeur si vous voulez que les TAB aient une longueur différente.


2

Utilisant également expand, mais avec la manipulation des paramètres bash pour compter le nombre d'espaces:

$ line=$'field0\tfield00\tfield000\tlast-field'
$ tabs2spaces=$(expand <<<"$line")
$ only_spaces=${tabs2spaces//[^ ]/}    # remove all non-space characters
$ echo "${#only_spaces}"
11
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.