我希望printf在计算字段宽度时识别多字节字符,以便列正确排列...我找不到这个问题的答案,并且想知道这里是否有人有任何建议,或者可能是函数/脚本负责处理这个问题。
以下是一个快速而肮脏的例子:
printf "## %5s %5s %5s ##\n## %5s %5s %5s ##\n" '' '*' '' '' "•" ''
>## * ##
>## • ##
显然,我想要结果:
>## * ##
>## • ##
有任何方法可以达到这个目的吗?
答案 0 :(得分:1)
我能想到的最好的是:
function formatwidth
{
local STR=$1; shift
local WIDTH=$1; shift
local BYTEWIDTH=$( echo -n "$STR" | wc -c )
local CHARWIDTH=$( echo -n "$STR" | wc -m )
echo $(( $WIDTH + $BYTEWIDTH - $CHARWIDTH ))
}
printf "## %5s %*s %5s ##\n## %5s %*s %5s ##\n" \
'' $( formatwidth "*" 5 ) '*' '' \
'' $( formatwidth "•" 5 ) "•" ''
使用*
宽度说明符将宽度作为参数,并通过添加多字节字符中的附加字节数来计算所需的宽度。
请注意,在GNU wc中,-c
返回字节,-m
返回(可能是多字节)字符。
答案 1 :(得分:1)
我可能会使用GNU awk:
awk 'BEGIN{ printf "## %5s %5s %5s ##\n## %5s %5s %5s ##\n", "", "*", "", "", "•", "" }'
## * ##
## • ##
你甚至可以在awk顶部编写名为printf的shell包装函数来保持相同的界面:
tr2awk() {
FMT="$1"
echo -n "gawk 'BEGIN{ printf \"$FMT\""
shift
for ARG in "$@"
do echo -n ", \"$ARG\""
done
echo " }'"
}
然后使用简单的函数覆盖printf:
printf() { eval `tr2awk "$@"`; }
测试它:
# buggy printf binary test:
/usr/bin/printf "## %5s %5s %5s ##\n## %5s %5s %5s ##\n" '' '*' '' '' "•" ''
## * ##
## • ##
# buggy printf shell builin test:
builtin printf "## %5s %5s %5s ##\n## %5s %5s %5s ##\n" '' '*' '' '' "•" ''
## * ##
## • ##
# fixed printf function test:
printf "## %5s %5s %5s ##\n## %5s %5s %5s ##\n" '' '*' '' '' "•" ''
## * ##
## • ##
答案 2 :(得分:1)
像python这样的语言可能会以更简单,更可控的方式解决你的问题......
#!/usr/bin/python
# coding=utf-8
import sys
import codecs
import unicodedata
out = codecs.getwriter('utf-8')(sys.stdout)
def width(string):
return sum(1+(unicodedata.east_asian_width(c) in "WF")
for c in string)
a1=[u'する', u'します', u'trazan', u'した', u'しました']
a2=[u'dipsy', u'laa-laa', u'banarne', u'po', u'tinky winky']
for i,j in zip(a1,a2):
out.write('%s %s: %s\n' % (i, ' '*(12-width(i)), j))
答案 3 :(得分:0)
这些是唯一的方法吗?单独使用printf
无法做到这一点?
与ninjalj(thx顺便说一句)的例子一样,我编写了一个脚本来处理这个问题,并将其保存为fprintf
中的/usr/local/bin
:
#! /bin/bash
IFS=' '
declare -a Text=("${@}")
## Skip the whole thing if there are no multi-byte characters ##
if (( $(echo "${Text[*]}" | wc -c) > $(echo "${Text[*]}" | wc -m) )); then
if echo "${Text[*]}" | grep -Eq '%[#0 +-]?[0-9]+(\.[0-9]+)?[sb]'; then
IFS=$'\n'
declare -a FormatStrings=($(echo -n "${Text[0]}" | grep -Eo '%[^%]*?[bs]'))
IFS=$' \t\n'
declare -i format=0
## Check every format string ##
for fw in "${FormatStrings[@]}"; do
(( format++ ))
if [[ "$fw" =~ ^%[#0\ +-]?[1-9][0-9]*(\.[1-9][0-9]*)?[sb]$ ]]; then
(( Difference = $(echo "${Text[format]}" | wc -c) - $(echo "${Text[format]}" | wc -m) ))
## If multi-btye characters ##
if (( Difference > 0 )); then
## If a field width is entered then replace field width value ##
if [[ "$fw" =~ ^%[#0\ +-]?[1-9][0-9]* ]]; then
(( Width = $(echo -n "$fw" | gsed -re 's|^%[#0 +-]?([1-9][0-9]*).*[bs]|\1|') + Difference ))
declare -a Text[0]="$(echo -n "${Text[0]}" | gsed -rne '1h;1!H;${g;y|\n|\x1C|;s|(%[^%])|\n\1|g;p}' | gsed -rne $(( format + 1 ))'s|^(%[#0 +-]?)[1-9][0-9]*|\1'${Width}'|;1h;1!H;${g;s|\n||g;y|\x1C|\n|;p}')"
fi
## If a precision is entered then replace precision value ##
if [[ "$fw" =~ \.[1-9][0-9]*[sb]$ ]]; then
(( Precision = $(echo -n "$fw" | gsed -re 's|^%.*\.([1-9][0-9]*)[sb]$|\1|') + Difference ))
declare -a Text[0]="$(echo -n "${Text[0]}" | gsed -rne '1h;1!H;${g;y|\n|\x1C|;s|(%[^%])|\n\1|g;p}' | gsed -rne $(( format + 1 ))'s|^(%[#0 +-]?([1-9][0-9]*)?)\.[1-9][0-9]*([bs])|\1.'${Precision}'\3|;1h;1!H;${g;s|\n||g;y|\x1C|\n|;p}')"
fi
fi
fi
done
fi
fi
printf "${Text[@]}"
exit 0
fprintf "## %5s %5s %5s ##\n## %5s %5s %5s ##\n" '' '*' '' '' '•' ''
有几点需要注意:
•我没有编写此脚本来处理格式的*
(星号)值,因为我从不使用它们。我为我写了这封信,并不想让事情过于复杂
•我写这个只是检查格式字符串%s
和%b
,因为它们似乎是唯一受此问题影响的字符串。因此,如果有人设法从一个数字中获取一个多字节的unicode字符,那么如果没有做小的修改,它可能无法工作。
•该脚本非常适合printf
(不是一些老式的UNIX黑客)的基本使用,可以随意修改或者全部使用!
- Aesthir
答案 4 :(得分:0)
纯shell解决方案
right_justify() {
# parameters: field_width string
local spaces questions
spaces=''
questions=''
while [ "${#questions}" -lt "$1" ]; do
spaces=$spaces" "
questions=$questions?
done
result=$spaces$2
result=${result#"${result%$questions}"}
}
请注意,这仍然无法在短划线中使用,因为短划线没有区域设置支持。
答案 5 :(得分:0)
这有点晚了,但是我只是碰到了这一点,并认为我会把它发布给其他遇到相同帖子的人。 @ninjalj答案的一种变体可能是创建一个返回给定长度的字符串的函数,而不是计算所需的格式长度:
Routes in JSON format:
<textarea id="inputRoutes">[[1,2,7],[3,6,7]]</textarea><br>
Start: <input id="inputStart" value="1"><br>
Target: <input id="inputTarget" value="6"><br>
Distance: <span id="output"></span>
输出:
#!/bin/bash
function sized_string
{
STR=$1; WIDTH=$2
local BYTEWIDTH=$( echo -n "$STR" | wc -c )
local CHARWIDTH=$( echo -n "$STR" | wc -m )
FMT_WIDTH=$(( $WIDTH + $BYTEWIDTH - $CHARWIDTH ))
printf "%*s" $FMT_WIDTH $STR
}
printf "[%s]\n" "$(sized_string "abc" 20)"
printf "[%s]\n" "$(sized_string "ab•cd" 20)"