从字符串中收集字符并打印它们的 unicode

Phi*_*pos 4 character-encoding shell-script text-processing sort unicode

上下文(如果你不在乎,请跳过;如果你怀疑我完全走错了路,请阅读)

\n

对于内存较小的嵌入式系统,我想生成仅包含实际需要的字形的字体。因此,在构建时,我需要扫描语言文件,从字符串中提取字符并使用它们的代码作为字体生成工具的参数。

\n

包含相关字符串的翻译文件(当然,这只是一个例子,但至少它涵盖了一些 unicode 内容)

\n
TEXT_1=Foo\nTEXT_2=Bar\nTEXT_3=Baz\nTEXT_4=\xc3\x9cnic\xc3\xb8d\xc3\xa9\nTEXT_5=\xce\xb5\xce\xbb\xce\xbb\xce\xb7\xce\xbd\xce\xb9\xce\xba\xce\xac\n
Run Code Online (Sandbox Code Playgroud)\n

预期产出

\n
0x42,0x61,0x72,0x42,0x61,0x7A,0x46,0x6F,0x6F,0xDC,0x6E,0x69,0x63,0xF8,0x64,0xE9,0x3B5,0x3BB,0x3BB,0x3B7,0x3BD,0x3B9,0x3BA,0x3AC\n
Run Code Online (Sandbox Code Playgroud)\n

到目前为止我的方法

\n

该脚本只是执行我所描述的操作:sed读取文件,提取字符串,并准备将其格式化printfsort -u删除重复项:

\n
for char in $(sed "s/[^=]*=//;s/./'& /g" myLang.translation|sort -u); do\n  printf "0x%02X\\n" $char\ndone\n
Run Code Online (Sandbox Code Playgroud)\n

这适用于这个例子,但感觉丑陋、不可靠、错误,对于真实文件来说可能很慢,所以你能说出一个更好的工具、更好的方法、更好的东西吗?

\n

Sté*_*las 7

perl

perl -C -lne '
  if (/=(.*)/) {$c{$_}++ for split //, $1}
  END{print join ",", map {sprintf "0x%X", ord$_} sort keys %c}
  ' your-file
Run Code Online (Sandbox Code Playgroud)

给出:

0x42,0x46,0x61,0x63,0x64,0x69,0x6E,0x6F,0x72,0x7A,0xDC,0xE9,0xF8,0x3AC,0x3B5,0x3B7,0x3B9,0x3BA,0x3BB,0x3BD
Run Code Online (Sandbox Code Playgroud)
  • -C如果语言环境使用 UTF-8 作为其字符映射,则执行 UTF-8 I/O
  • -ln sed -n模式,其中代码在输入的每一行上运行。-l从输入中删除行分隔符,并将其添加回输出中(执行 a $\ = $/
  • -e 'code'指定要在命令行而不是从脚本运行的代码。
  • /=(.*)/要匹配的行至少包含一个=捕获$1 (第一个捕获组)中第一次出现之后的内容。
  • split //, $1用空分隔符将其拆分为单个字符
  • $c{$_}++ for that-above循环该字符列表并递增相应的关联数组元素。%c将字符映射到其出现次数。我们在这里不使用该计数。
  • END{code}code只有跑到最后。
  • sort keys %c按词法对该关联数组的键进行排序
  • map { code } @list通过在每个元素上应用代码来转换列表。
  • ord$_获取字符的数值。
  • sprintf "0x%X"将其格式化为十六进制(大写ABCDEF,但小写0x)。
  • join ",", @list加入列表,
  • print打印它,后跟$\(换行符)。

在 zsh 中(可能效率低很多):

$ set -o cbases -o extendedglob
$ LC_COLLATE=C
$ echo ${(j[,])${(ous[])"$(<your-file cut -sd= -f2- | tr -d '\n')"}/(#m)?/$(([#16]#MATCH))}
0x42,0x46,0x61,0x63,0x64,0x69,0x6E,0x6F,0x72,0x7A,0xDC,0xE9,0xF8,0x3AC,0x3B5,0x3B7,0x3B9,0x3BA,0x3BB,0x3BD
Run Code Online (Sandbox Code Playgroud)

或者不使用外部实用程序:

$ set -o cbases -o extendedglob
$ LC_COLLATE=C
$ echo ${(j[,])${(@ous[])${(f)"$(<your-file)"}#*=}/(#m)?/$(([#16]#MATCH))}
0x42,0x46,0x61,0x63,0x64,0x69,0x6E,0x6F,0x72,0x7A,0xDC,0xE9,0xF8,0x3AC,0x3B5,0x3B7,0x3B9,0x3BA,0x3BB,0x3BD
Run Code Online (Sandbox Code Playgroud)
  • "$(<you-file)"文件的内容,删除了尾随换行符,并用引号引起来,因此它不是 IFS 分割的
  • ${(f)param}按行分割f以将行作为列表获取
  • ${array#*=}从数组元素中删除最短的前导部分匹配*=
  • @确保 列表处理的标志
  • o按词法顺序(基于 C 语言环境中的代码点)
  • unique 删除重复项
  • s[]分裂成单独的字符。
  • ${array/(#m)?/$(([#16]#MATCH))}将感谢中捕获的字符 ( ?)替换为以 16 为基数格式化的值(在算术表达式中) 。有了这个选项,就可以代替$MATCH(#m)#MATCH[#16]cbases0xBEEF16#BEEF
  • j[,]加入,.

将其分解为单独的步骤将使其更清晰:

set -o cbases -o extendedglob
LC_COLLATE=C
contents=$(<your-file)
lines=( ${(f)contents} )
values=( ${lines#*=} )
chars=( ${(@ous[])values} )
codepoints=( ${chars/(#m)?/$(( [#16] #MATCH ))} )
echo ${(j[,])codepoints}
Run Code Online (Sandbox Code Playgroud)