chr

chr

我使用以下内容进行转换整数字符字符整数在bash中。但我不明白如何printf \\$(printf '%03o' $1)printf '%d' "'$1"工作。请解释如何 printf \\$(printf '%03o' $1)工作printf '%d'

#!/bin/bash
# chr() - converts decimal value to its ASCII character representation
# ord() - converts ASCII character to its decimal value

chr() {
  printf \\$(printf '%03o' $1)
}

ord() {
  printf '%d' "'$1"
}

ord A
echo
chr 65
echo

答案1

printf '\101'其中101是八进制数,输出具有该值的字节。

当发送到 ASCII 终端时,它将被呈现为AASCIIA中的字符 65(八进制 101)和所有 ASCII 兼容的字符集(其中包括大多数现代字符集,但某些 IBM 系统上仍在使用的 EBCDIC 字符集除外)。

printf \\$(printf '%03o' $1)

应该写成:

printf "\\$(printf '%03o' "$1")"

因为在类似 Bourne 的 shell 中,保留参数扩展(如$1)或命令替换 ( $(...)) 不加引号是 split+glob 运算符,这里不需要它

  • printf '%03o' "$1"将数字转换$1为 3 位八进制
  • printf "\\$(...)"将该八进制附加到 a \\\双引号内变为\)并将其传递给,printf以便它将输出相应的字节值。

请注意,它仅适用于字符集为每个字符一个字节的语言环境(例如iso8859-1),或者在具有多字节字符集的语言环境中,仅适用于值 0 到 127。

bash

printf '%d\n' "'A"

打印字符的 Unicode 代码点A(或者至少在 GNU 系统上返回的值mbtowc()至少是 Unicode 代码点)。

其他一些实现(包括独立的 GNUprintf实用程序)则返回字符第一个字节的值。

对于基于 ASCII 的系统上的 和 等 ASCII 字符来说A,这没有任何区别,但对于其他字符来说,这很重要。例如希腊α字符 (U+03B1) 编码为:

  • iso8859-7 中的字节 225(标准希腊单字节字符集)
  • UTF-8 中的字节 206 177(类 Unix 系统上最常用的 Unicode 编码)
  • GB18030(Unicode 的官方中文编码)中的字节 166 193。

Bashprintf '%d\n' "'α"将始终输出945(十六进制的 0x03b1),这是无论语言环境如何(至少在 GNU 系统上)的 Unicode 代码点α,但其他语言可能会根据语言环境返回 225、206 或 166。

您可以从中看到,对于 ASCII 字符(或值 0 到 127),或者在使用所有字符的字符集(值 0 到 255)的语言环境中,这些chrord仅仅是彼此相反。iso8859-1

如果ord()要返回 Unicode 代码点,则相反(打印与 Unicode 代码点对应的字符)将是:

chr() {
  printf "\U$(printf %08X "$1")"
}

(假设bash4.3 或更高版本(\UXXXXXXXX在 4.2 中添加,但在 4.3 之前对于字符 U+0080 到 U+00FF 无法正常工作))。

然后,在任何语言环境中:

$ ord α
945
$ chr 945
α

或者ord()返回给定字符的编码字节值(在当前语言环境中):

ord() {
  printf %s "$1" | od -An -vtu1
}

chr()输出这些字节:

chr() {
  printf "$(printf '\\%o' "$@")"
}

然后,例如在 UTF-8 语言环境中:

$ ord α
 206 177
$ chr 206 177
α

(你ord α会给出 945,你chr会给出 和 的垃圾chr 945chr 206 177

或者在语言环境中使用iso8859-7

$ ord α
 225
$ chr 225
α

(你会给出 945,但如果在 GNU 系统上被替换为if 则ord α可能给出 225 )。printf/usr/bin/printf

答案2

内部printf '%03o' $1返回$1八进制值 (即 65) 的值 (65 -> 101)。

外部printf \\$(..)打印八进制值表示的字符。

参见man printf行:

\NNN 八进制值 NNN 字节(1 到 3 位数字)

因为printf '%d' "'$1"您需要指定'以指示$1应将其视为单个字符常量,否则 printf 会抛出错误,指示该值是无效数字。然后使用字符常量的值以printf十进制格式打印"%d"

答案3

chr

printf \\$(printf '%03o' $1) # 应该是...printf "$(printf '\\%03o' "$1")"

第一个printf使用第二个的输出来产生一个字符。我们需要逆向工作来解密它。

第二个printf用于将整数(从$1)转换为八进制整数('%03o'格式)。

$ printf '%03o' 12
014
$ printf '%03o' 65
101
$ printf '%03o' 255
377

然后,该结果与 an 的等效项连接起来\(必须将其加倍或用引号引起来,以避免其对 shell 的特殊含义)。得到的字符串\101解释为字节的八进制值。但只有最多三位数(在 printf 的大多数 shell 实现中)。仅解释三个八进制数字是因为该类型的格式旨在用于单字节 0-255(或八进制的 0-377)。因此,大多数 printf 实现仅将八进制数转换为一字节。如果该字节表示控制台使用的语言环境中的 ascii 字符,则该字符将被打印出来。

$ printf \\101\\n
A
$ printf \\377\\n

要打印受区域设置影响的字符(而不是字节),我们需要:

  • 能够生成的 printf多字节正在使用的语言环境中的字符,以及
  • 某种形式的表达,我们想要一个字符而不是一个字节,

例如,在 bash(4.3+) 中:

$ printf \\U263A\\n

请注意,printf可以打印许多数字:

$ printf '%03o\n' 1495195076287004671
122777777777777777777

但只有前三个被接受为八进制数:

$ printf '\122777777777777777777'
R777777777777777777                     # Note the R in the front.

秩序

printf '%d' "'$1"

数据(非格式)字符串删除外部引号,生成的字符串以'.以 a'或 a开头的字符串"对于 printf 来说是特殊的。对于这些字符串,如果格式为数字, 这价值第一个字符打印(以数字格式字符串给出的格式):

$ printf '%d\n' '"A'      # decimal (doesn't expand quoted vars '"$var')
65
$ printf '%d\n' "'A"      # decimal
65
$ printf '%o\n' "'A"      # octal
101
$ printf '%x\n' "'A"      # Hexadecimal
41

"'…"请注意,对于什么的解释存在一些变化第一个字符实现之间(字节或字符,如果是字节,则结果受所使用的区域设置的影响)。

相关内容