ANSIカラーコードがprintfアライメントを台無しにするのを止める方法は?
-
03-10-2019 - |
質問
Ruby Printfの使用中にこれを発見しましたが、Cのprintfにも適用されます。
出力文字列にANSIカラーエスケープコードを含めると、アライメントが台無しになります。
ルビー:
ruby-1.9.2-head > printf "%20s\n%20s\n", "\033[32mGreen\033[0m", "Green"
Green # 6 spaces to the left of this one
Green # correctly padded to 20 chars
=> nil
Cプログラムの同じラインが同じ出力を生成します。
とにかく、Printf(または他の何か)を取得して出力を調整し、印刷されていない文字のスペースを追加しないようにしますか?
これはバグですか、それとも正当な理由がありますか?
アップデート: ANSIコードと幅広い文字があるときにデータを調整するためにprintfを依存することはできないため、Rubyのコンソールに色付きの表形式データを並べるためのベストプラクティスの方法はありますか?
解決
私は、緑の緑の後の9つのスペース」のあなたの特徴づけに同意しません。 RubyではなくPerlを使用しますが、ステートメントの変更を使用して、文字列の後にパイプシンボルを印刷する場合は、次のようになります。
perl -e 'printf "%20s|\n%20s|\n", "\033[32mGreen\033[0m", "Green";'
Green|
Green|
これは私にそれを示しています printf()
ステートメントは文字列内の14文字を数えたため、6つのスペースを準備して、20文字を右に調整しました。ただし、端子はこれらのキャラクターの9つを飲み込み、色が変化すると解釈しました。したがって、出力はあなたが望んでいたよりも9文字短いように見えました。しかし printf()
最初の「グリーン」の後に9ブランクを印刷しませんでした。
アラインド出力(色付き)のベストプラクティスに関しては、色を扱う単純な「%s」フィールドに囲まれた各サイズと整列のフィールドが必要だと思います。
printf "%s%20.20s%s|%s%-10d%s|%s%12.12s%s|\n",
co_green, column_1_data, co_plain,
co_blue, column_2_data, co_plain,
co_red, column_3_data, co_plain;
ここで、明らかに、 co_XXXX
変数(定数?)には、名前付き色に切り替えるエスケープシーケンスが含まれています(および co_plain
より良いかもしれません co_black
)。何らかのフィールドで色彩が必要でないことが判明した場合は、その代わりに空の文字列を使用できます co_XXXX
変数(またはそれを呼び出します co_empty
).
他のヒント
それはバグではありません:Rubyが知るべき方法はありません(少なくともPrintf内では、Cursesのようなものにとっては別の話です)そのstdoutはVT100エスケープシーケンスを理解する端末に行くことです。
背景の色を調整していない場合、このようなものがより良いアイデアかもしれません:
GREEN = "\033[32m"
NORMAL = "\033[0m"
printf "%s%20s%s\n", GREEN, "Green", NORMAL
printf
フィールド幅の指定器は、すでに発見した制御文字の問題を除いて、表形式データ、インターフェイス要素などを調整するのに役立ちません。 Tは、レガシーキャラクターのエンコーディングに制限したい(多くのユーザーが非推奨だと考えています)。
使用することを主張する場合 printf
このように、あなたはおそらく次のようなことをする必要があります:
printf("%*s\n%*s\n", bytestopad("\033[32mGreen\033[0m", 20), "\033[32mGreen\033[0m", bytestopad("Green", 20), "Green");
どこ bytestopad(s,n)
文字列になるために必要な合計バイトの数(文字列とパディングスペース)を計算する関数です s
取り上げる n
端子列。これには、エスケープの解析とマルチバイト文字の処理、および施設の使用が含まれます(POSIXなど wcwidth
関数)それぞれの端子列の数を検索します。の使用に注意してください *
の一定のフィールド幅の代わりに printf
フォーマット文字列。これにより、渡すことができます int
への議論 printf
ランタイム変動フィールド幅用。
すべての問題を避けるために、エスケープシーケンスを実際のテキストから分離します。
# in Ruby
printf "%s%20s\n%s%20s\n", "\033[32m", "Green", "\033[0m", "Green"
また
/* In C */
printf("%s%20s\n%s%20s\n", "\033[32m", "Green", "\033[0m", "Green");
ANSIエスケープシーケンスはRubyまたはCの一部ではないため、これらのキャラクターを特別に扱う必要があるとは考えていません。
ターミナルカラーの多くのことをする場合は、さまざまな種類の端末で機能する色の変更を行う関数を提供する呪いとncursesを調べる必要があります。また、テキストベースのウィンドウ、機能キー、時にはマウスの相互作用など、はるかに多くの機能を提供します。
これが私が最近思いついたソリューションです。これにより、使用できます color("my string", :red)
で printf
声明。ヘッダーとデータに同じフォーマット文字列を使用するのが好きです - 乾燥しています。これにより可能になります。また、私はを使用します 虹 カラーコードを生成するための宝石。それは完璧ではありませんが、仕事を終わらせます。 CPAD
ハッシュには、それぞれ左右のパディングに対応する各色の2つの値が含まれています。当然のことながら、このソリューションは拡張して、太字や下線などの他の色や修飾子を促進する必要があります。
CPAD = {
:default => [0, 2],
:green => [0, 3],
:yellow => [0, 2],
:red => [0, 1],
}
def color(text, color)
"%*s%s%*s" % [CPAD[color][0], '', text.color(color), CPAD[color][1], '']
end
例:
puts "%-10s %-10s %-10s %-10s" % [
color('apple', :red),
color('pear', :green),
color('banana', :yellow)
color('kiwi', :default)
]