printf: マルチバイト文字

printf: マルチバイト文字

printfマルチバイト文字を含む文字列に関連する出力形式を指定しようとすると、printfリテラル文字は計算せずにバイト数のみを計算するため、シングルバイト文字とマルチバイト文字を混在させるとテキスト形式が複雑になります。難しい。たとえば、

$ cat script
#!/bin/bash
declare -a a b
a+=("0")
a+=("00")
a+=("000")
a+=("0000")
a+=("00000")
b+=("0")
b+=("├─00")
b+=("├─000")
b+=("├─0000")
b+=("└─00000")
printf "%-15s|\n" "${a[@]}" "${b[@]}"

$ ./script
0              |
00             |
000            |
0000           |
00000          |
0              |
├─00       |
├─000      |
├─0000     |
└─00000    |

さまざまな提案の解決策が見つかりました(主に他の言語やユーティリティを使用してテキストを印刷するラッパー)。基本的なbashソリューションはありますか?何の記録もないprintf 書式文字列役に立つようです。この場合、localeUTF-32などの固定幅の文字エンコーディングを使用するなどの設定は関連していますか?

ベストアンサー1

文字数を数えるのではなく、端末に目的の位置にカーソルを移動するように指示することでこの問題を解決できますprintf

$ printf "%s\033[10G-\n" "abc" "├─cd" "└──ef"
abc      -
├─cd     -
└──ef    -

まあ、端末に印刷したいと仮定すると...

制御シーケンスがあります<ESC>[nnGnn移動する列で、10進数で表示されます。

もちろん、最初の列が割り当てられたスペースよりも長い場合、結果は良くありません。

$ printf "%s\033[10G-\n" "abcdefghijkl"
abcdefghi-kl

この問題を解決するには、<ESC>[K次の列を印刷する前に残りの行()を明示的に消去できます。

$ printf "%s\033[10G\033[K-\n" "abcdefghijkl"
abcdefghi-

別の方法は、文字列の文字長を決定できるものがあると仮定して手動でパディングを実行することです。これはBashの単純な文字に対してうまくいくようですが、もちろん少し見苦しいです。幅が0で幅が2つの文字はこれを破ることができ、文字の結合もテストしませんでした。

#!/bin/bash
pad() { 
    # parameters:
    #  1: name of variable to pad
    #  2: length to pad to
    local string=${!1}
    local len=${#string}
    printf -v "$1" "%s%$(($2 - len))s" "$string" ""
}
echo "1234567890"
for x in "abc" "├─cd" "└──ef" ; do
    pad x 9
    printf "%s-\n" "$x"
done

出力は次のとおりです

1234567890
abc      -
├─cd     -
└──ef    -

おすすめ記事