1行でタブ文字 '\ t'の長さを決定します。

1行でタブ文字 '\ t'の長さを決定します。

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

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

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

次の入力データの場合(タブ区切りフィールドと1つのタブのみ):

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

各行のタブ文字の長さを計算したいです。

11
9
9

答え1

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

不規則な間隔でタップ停止を設定することもできます。

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

端末だけが右TABの数列がカーソルを移動するかどうかを知っています。

タブを転送する前後に端末でカーソル位置を照会することで、この情報を取得できます。

与えられた行に対して手動で計算を実行し、その行が画面の最初の列に印刷されると仮定するには、次のものが必要です。

  • タップ停止位置の把握²
  • 知る各文字の表示幅
  • 画面の幅を調べる
  • 追加の制御文字を処理するかどうかを決定します(例:(\rカーソルを最初の列に移動)または\bカーソルを後ろに移動...)

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

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では機能しません。


1しかし、これはstty tab3ttyデバイスラインルールがタブ処理(ターミナルに送信する前にカーソルがある可能性がある独自のアイデアに基づいてタブを空白に変換)に代わって8列ごとにタブ指定を実装することに注意してください。記号が停止します。 Linuxでテストした結果、CR、LF、BS文字はもちろん、マルチバイトUTF-8文字(オンにiutf8なっている場合)も正しく処理しているようですが、それはすべてです。他のすべての非制御文字(幅0、二重幅文字を含む)に対して幅を1と仮定し(明らかに)、エスケープシーケンスを処理せず、正しくラップされません。次の端末:タブ処理を実行できません。

いずれにせよ、tty行の規則はカーソルがどこにあるかを知る必要があり、上記の経験的方法を使用する必要があります。icanon行エディタを使用する場合(たとえば、独自の行エディタを実装していないアプリケーションにテキストを入力するとき)cat、を押すと、TabBackspace回線規約は送信するBS文字数を知る必要があります。削除表示に使用されるタブ文字です。タブ停止位置(たとえば)を変更すると、tabs 12タブが正しく削除されないことを確認できます。を押す前に2バイト文字を入力しても同じです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のソリューションのように使用できます。

答え2

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

POSIXexpandユーティリティはタブを空白に拡張します。このawkスクリプトは、各行のすべてのスペースを置き換えるのに必要な代替回数を計算して出力します。

入力ファイルにすでに存在するスペースを計算しない場合は、次のようにします。

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

@入力データに文字が存在しないと保証される場所はどこですか?

タブごとに通常の8マスの代わりに10マスが必要な場合:

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

答え3

そして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

タブ文字の長さを変えるには、上記の8を別の値に変更できます。

答え4

また、使用されますが、expandbash引数操作によってスペースの数を計算します。

$ line=$'field0\tfield00\tfield000\tlast-field'
$ tabs2spaces=$(expand <<<"$line")
$ only_spaces=${tabs2spaces//[^ ]/}    # remove all non-space characters
$ echo "${#only_spaces}"
11

関連情報