1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

shell script でも ord と chr したい!!

Last updated at Posted at 2024-09-11

たまたま必要になった、
shell script で ordchr を使う方法です。

できるだけ環境に依存しないようにしました。(zsh は単語展開が他と違うため注意。どうやら ${=val} とするらしいが環境がないため未検証。該当部参照)

エラーチェックの機構は設けてないので必要であれば if 文等で行ってください。

忙しい人向け

一番シンプルな書き方

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

chr () {
    echo -e "\\U$(printf '%x' $1)"
    # もしくは
    # printf "\\U$(printf "%x" $1)"      # POSIX等、互換性はこの方がいいだろう
}

ord A # 65
chr 65 # A

複数になると?

# 与えられた文字列をすべて文字コードにする ords 関数
ords () {      
    str=$1
    args=$(echo "$str" |  sed "s/./'& /g")
    format=$(echo "$str" |  sed "s/./%d /g")
    printf "$format" $args \
    | sed -e "s/^0 /32 /g" -e "s/ 0/ 32/g"  
}

ords 'Hello!, こんにちは'
# 72 101 108 108 111 33 44 32 12371 12435 12395 12385 12399

chrs () {
    str="$1"
    format=$(echo "$str" | sed 's/[0-9]\+/%x/g')
    printf $(echo " $(printf "$format" $str)" | sed 's/[[:space:]]/\\U/g')
}

chrs '72 101 108 108 111 33 44 32 12371 12435 12395 12385 12399'
# Hello!, こんにちは

解説

ord について

ord では printf を用いて数値を文字に変換している。shell の printf はC言語の printf と似たユーザインタフェースを持っているのである。

しかしこれはよく考えると変である。ord A の場合、変数を展開すると printf '%d' "'A" である。%d と言えば整数を指定するフォーマット指定子である。しかしそのあとにきているのはAという文字である。さらによく見ると 'A とかいう変なクオーテーションがついている。
このスニペット自体はまあまあ広く知られているようで、bash ord とかで検索をすると似たようなものがたくさん出てくる。しかしクオーテーションについては、printfman にも記載がなく、なかなかこのクオーテーションの秘密にたどり着くことができない。

だが、 The Open Group の "Shell & Utilities > printf" のページ に以下のようにあった

The argument operands shall be treated as strings if the corresponding conversion specifier is b, c, or s, and shall be evaluated as if by the strtod() function if the corresponding conversion specifier is a, A, e, E, f, F, g, or G. Otherwise, they shall be evaluated as unsuffixed C integer constants, as described by the ISO C standard, with the following extensions:

  • A leading <plus-sign> or <hyphen-minus> shall be allowed.

  • If the leading character is a single-quote or double-quote, the value shall be the numeric value in the underlying codeset of the character following the single-quote or double-quote.

  • Suffixed integer constants may be allowed.

意訳:

指定子が b, c, s (訳注: 文字/文字列を出力するもの) だったら引数は文字列として解釈されるよ。
A, e, E, f, F, g, G だったら strtod() (訳注: C言語で数値を表す文字列を受け取り、浮動小数点実数を返す) のようにふるまうよ
それ以外は以下の拡張を含むCの整数型として引数を解釈するよ。:

  • 先頭の +, - は許容されるよ
  • 先頭がシングルクォーテーションもしくはダブルクォーテーションの場合、そのあとの文字の文字コードの数値として解釈するよ
  • 接尾辞付き整数定数が許される場合があるよ

ここでは指定子は %d なので "Cの整数型として引数を解釈" し、その引数の先頭にシングルクォーテーションがあるため、その引数の文字コードが整数として渡されているのである。

chr について

chr は簡単に言うと

  1. printf "%x" <数値> で16進数に変換
    1. %x は数値を8進数に変換する指定子。
  2. echo -e "<\U + 8桁16進数表記>" で8進数をエスケープシーケンスとして解釈
    1. echo でエスケープシーケンスを有効にすると \U から始まる数字は8桁の16進数表記の文字コードとして解釈される
    2. もしくは printf "\\U$(printf "%x" <10進数数値>)" でも良い
      1. これも 16進数でやり取りしている。互換性はこの方がよいだろう。(エスケープシーケンスは互換性の問題が多い)
      2. 参考サイトの多くは "%o" つまり8進数でやり取りしていたが、それでは日本語などが扱えない。逆に16進数として扱い、\U にすればサロゲートペア(絵文字など 😄 )も扱える.

ということをしている。
chr 65 を例に取ると printf "%x" 6565 を16進数表記の 41 に変換、次に echo -e "\U41"A に変換している。

printf 1つだけでも行けそうな気がするが、shell の printf%c に整数を渡すことができない。

互換性に不安がある場合は echo -e ではなく printf "\\$(printf "%o" <10進数数値>) のようにしても良いかもしれない

ちなみに... 複数になると?

ちなみに例えば複数のアルファベットをすべて文字コードに変換したいときには、printf のフォーマットが printf <format> [arguments] なので、アルファベットの数だけ %d を書いて、アルファベットは各々別の引数として渡せばよい。

printf "%d %d %d %d" \'A \'B \'C \'D
# 65 66 67 68


# 与えられた文字列をすべて文字コードに(ただしスペースは 0 とされる)
str="Hello!"
args=$(echo "$str" |  sed "s/./'& /g")      # `Hello!` => `'H 'e 'l 'l 'o '!`
format=$(echo "$str" |  sed "s/./%d /g")    # 文字数の数だけ "%d" を作成
printf "$format" $args      # zsh では単語分割がうまくいかないことに注意。`${=args}` のようにするらしい
# 72 101 108 108 111 33

# もし空白も評価したいなら 0 を 32 に変換する(ただしタブと空白の区別はつかない)
str="Hello!   World!"
args=$(echo "$str" |  sed "s/./'& /g")
format=$(echo "$str" |  sed "s/./%d /g")
printf "$format" $args \
| sed -e "s/^0 /32 /g" -e "s/ 0/ 32/g"  # 0 を 32 に変換

関数にすると:

# 与えられた文字列をすべて文字コードにする ord 関数
ords () {      
    str=$1
    args=$(echo "$str" |  sed "s/./'& /g")
    format=$(echo "$str" |  sed "s/./%d /g")
    printf "$format" $args \
    | sed -e "s/^0 /32 /g" -e "s/ 0/ 32/g"  
}

ords 'Hello!, こんにちは'
# 72 101 108 108 111 33 44 32 12371 12435 12395 12385 12399

# ただしこれらは絵文字などのサロゲートペアには対応できない

逆に 空白区切りの数字を文字列に変換するには以下のようにする

str="72 101 108 108 111 33"

format=$(echo "$str" | sed 's/[0-9]\+/%x/g')  # 数値の数だけ '%x' を作る
unichr=$(echo " $(printf "$format" $str)" |   # 16進数表記
  sed 's/[[:space:]]/\\U/g')                # それぞれの数字の先頭に '\U' をつける 
printf $unichr

関数化:

chrs () {
    str="$1"
    format=$(echo "$str" | sed 's/[0-9]\+/%x/g')
    printf $(echo " $(printf "$format" $str)" | sed 's/[[:space:]]/\\U/g')
}

chrs '72 101 108 108 111 33 44 32 12371 12435 12395 12385 12399'
# Hello!, こんにちは

参考文献






1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?