タブ「\ t」が行にある長さを決定します


10

テキスト処理フィールドで、タブの長さが8文字(デフォルトの長さ)以下かどうかを知る方法はありますか?

たとえば、タブ区切りのサンプルファイルがあり、フィールドの内容が1つ未満のタブに収まる場合(≤7)、その後にタブがある場合、そのタブは「タブサイズ-フィールドサイズ」のみになります。 ' 長さで。

行のタブの全長を取得する方法はありますか?私はタブの数(つまり、10個のタブが10を返すべきではない)を探しているのではなく、それらのタブの文字長を探しています。

以下の入力データの場合(フィールド間で区切られたタブと1つのタブのみ):

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

各行のタブの長さを数えるので、

11
9
9

回答:


22

このTAB文字は、端末に送信されると、端末のカーソルを次のタブストップに移動させる制御文字です。デフォルトでは、ほとんどの端末でタブストップは8列離れていますが、これは設定可能です。

不規則な間隔でタブ位置を設定することもできます。

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

端末だけが、TABがカーソルを移動する右側の列数を認識しています。

タブが送信される前後に端末からカーソル位置を問い合わせることで、その情報を取得できます。

特定の行について手動で計算を行い、その行が画面の最初の列に印刷されると想定する場合は、次のことを行う必要があります。

  • タブストップがどこにあるかを知る²
  • すべての文字の表示幅を知る
  • 画面の幅を知っている
  • \r(カーソルを最初の列に\b移動する)などの他の制御文字を処理するか、カーソルを後ろに移動するかを決定します...)

タブストップが8列ごとであり、行が画面に収まり、端末が正しく表示できない他の制御文字または文字(または非文字)がない場合は、簡略化できます。

GNU wcでは、行が次の場所に格納されている場合$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 -Lその入力で最も広い線の幅を与えます。これはwcwidth(3)、文字の幅を決定するために使用し、タブストップが8列ごとであると想定することで行われます。

非GNUシステムについて、同じ前提で、@ Kusalanandaのアプローチを参照してください。タブストップを指定できるのでさらに便利ですが、残念ながらexpand、入力にマルチバイト文字または0幅(結合文字など)またはダブル幅文字が含まれている場合、現在(少なくとも)GNUでは動作しません。


doただし、これを行うstty tab3と、ttyデバイスの回線制御がタブ処理を引き継ぎ(カーソルが端末に送信される前の位置に関する独自の考えに基づいてTABをスペースに変換し)、タブを8列ごとに実装します。Linuxでのテストでは、CR、LF、BSの文字とマルチバイトUTF-8の文字(iutf8オンになっている場合も同様)は適切に処理されているようですが、それで問題ありません。他のすべての非制御文字(ゼロ幅、倍幅文字を含む)の幅が1であることを前提としています。(明らかに)エスケープシーケンスを処理せず、適切に折り返されません...これはおそらく、タブ処理はできません。

いずれにせよ、tty icanonエディターは、カーソルがどこにあるかを知る必要があり、上記のヒューリスティックを使用catします。を押すTabBackspaceと、ライン規律は、タブ文字を消去して表示するために送信するBS文字の数を知る必要があります。タブストップの場所を変更すると(などtabs 12)、タブが適切に消去されないことがわかります。を押す前に全角文字を入力した場合も同様ですTabBackspace


²そのために、タブ文字を送信し、それぞれの後にカーソル位置を問い合わせることができます。何かのようなもの:

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

次に、expand -t "$tabs"@ Kusalanandaのソリューションを使用するのと同じように使用できます。


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

POSIX expandユーティリティはタブをスペースに展開します。awkスクリプトの数と出力の各ライン上のすべてのスペースを置き換えるために必要な置換の数。

入力ファイル内の既存のスペースをカウントしないようにするには:

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

ここ@で、は、入力データに存在しないことが保証されている文字です。

通常の8ではなく、タブごとに10のスペースが必要な場合:

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

3
xに呼び出す前に、スペースを他の半角文字(など)で置き換えたいexpand場合も、最初に入力にあったスペースもカウントします。
ステファンChazelas

1
expandまた、タブストップは8列ごとに想定しています(オプションで変更できます)。GNU実装はマルチバイト文字をサポートしないことに注意してください(0幅または2倍幅の文字はもちろんです)。FreeBSDのIIRCは問題ありません。
ステファンChazelas

@StéphaneChazelasもちろん、それが0x09の幅を0x20sでカウントする計画の一部でない限り;-)
can-ned_food

2

perl

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

または:

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

TABの長さを変えたい場合は、上記の8を他の値に変更できます。


2

も使用expandしますが、スペースの数をカウントするbashパラメータ操作を使用します。

$ line=$'field0\tfield00\tfield000\tlast-field'
$ tabs2spaces=$(expand <<<"$line")
$ only_spaces=${tabs2spaces//[^ ]/}    # remove all non-space characters
$ echo "${#only_spaces}"
11
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.