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    -

답변2

여기에 사용된 솔루션이 있습니다 wc -L.

for i in "${a[@]}" "${b[@]}"
do printf "%s%*s|\n" "$i" "$[15 - $(wc -L <<< "$i")]" ""
done

0              |
00             |
000            |
0000           |
00000          |
0              |
├─00           |
├─000          |
├─0000         |
└─00000        |

wc -L입력의 표시 너비를 인쇄하므로 이중 너비 문자 등에서도 작동합니다.

답변3

웹 검색을 좀 했지만 순수 Bash에서는 문제에 대한 해결책을 찾을 수 없었습니다. 아마도 해결책이 없을 수도 있을 것 같습니다. 다음 StackOverflow 게시물을 찾았습니다.

이것가장 많이 투표된 답변거기(사용자가 게시함)그리스도)에는 다음이 포함됩니다.

printf예, 이는 제가 알고 있는 모든 버전의 문제입니다. 이 문제에 대해 간략하게 논의했습니다.이 답변그리고 또한이것.

또한 Unix StackExchange에서 다음 게시물을 찾았습니다.

이것허용되는 솔루션여기에는 다음 설명이 포함됩니다.

POSIX필요 printf%-20s20개를 세어 보자바이트아니요수치printf인쇄와는 관련이 없지만텍스트, 서식 지정(토론 참조오스틴 그룹에서(POSIX) 및bash메일링 리스트).

자신이 원하는 일이 불가능할 수도 있고 printf자신만의 솔루션을 출시해야 할 것 같습니다.

Python 스크립트를 사용하여 원하는 출력을 생성할 수 있습니다. 아마도 유용할 것입니다:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""script.py"""

# Set the default character encoding to UTF-8
import sys
reload(sys)
sys.setdefaultencoding("utf-8")

# Array of ASCII characters
a=[("0")]
a+=[("00")]
a+=[("000")]
a+=[("0000")]
a+=[("00000")]

# Array of UTF-8 Characters
b=[("0")]
b+=[("├─00")]
b+=[("├─000")]
b+=[("├─0000")]
b+=[("└─00000")]

# Print the elements from both arrays
for x in a + b:
    print (u"%-15s|" % x).encode('utf-8')

스크립트를 실행하면 다음과 같은 결과가 나옵니다.

user@host:~$ python script.py

0              |
00             |
000            |
0000           |
00000          |
0              |
├─00           |
├─000          |
├─0000         |
└─00000        |

답변4

printf가 분음 부호를 "축소"하는 이유는 무엇입니까?bash기능이 내부적으로 누락되었기 때문에 적절한 도구를 호출하거나 다른 셸로 전환하여 이를 수행하는 몇 가지 적절한 솔루션이 있지만 실제로 bash내장 명령만 사용하여 이를 수행하려는 경우 이를 수행하는 방법이 있습니다. -width(여러 너비 바이트 가능) 문자.

$string모든 POSIX 셸에서와 마찬가지로 bash에서는 with 의 문자 너비를 얻을 수 있지만 ${#string}C ${#string}로캘에서는 너비를 바이트 단위로 얻습니다.

따라서 차이점을 다음과 같이 설명할 수 있습니다.

clength() { clength=${#1}; }
blength() { local LC_ALL=C; blength=${#1}; }
align() {
  local format="$1" width="$2" arg blength clength
  shift 2
  for arg do
    clength "$arg"; blength "$arg"
    printf "$format" "$((width + blength - clength))" "$arg"
  done
}

a=(0 00 000 0000 00000)
b=(0 ├─00 ├─000 ├─0000 └─00000)
align '%-*s|\n' 12 "${a[@]}" "${b[@]}"

너비가 0(태그 결합과 같은) 또는 이중 너비 문자를 고려하려면 bash스크립트에서 이러한 문자 목록을 하드코딩할 준비가 되어 있지 않은 경우(또는 터미널 이스케이프 시퀀스를 사용하여 터미널에 텍스트를 정렬하도록 지시하는 경우(마지막 예)거기, 또는거기) 및 지원되는 모든 터미널에 대한 하드코드 이스케이프 시퀀스( bashterminfo/termcap에 대한 내장 인터페이스가 없기 때문). 내가 아는 한, zsh와 ksh93은 가변 디스플레이 너비 문자 정렬을 기본적으로 지원하는 유일한 셸입니다(예:연결된 Q&A).

관련 정보