hack のためのネタ帳, etc,,,

状況

"abcd" みたいな文字列があったときに、"61626364" みたいな 16 進表記の文字コード列に変換したい。
また "61626364" みたいな 16 進表記の文字コード列があったとき、逆に "abcd" みたいな文字列に変換したい。

解法

bash get charcode」で検索すると以下スレッドを見つけた。

printf の引数としてシングルクォーテーション「'」に文字を続けると文字コードが得られるので以下のようにすると良いらしい。
$ printf "%02x" \'a \'b \'c \'d
61626364
また、エスケープコード \8進3桁、\x16進2桁で文字コードによる文字指定が可能なので、以下のようにすると良いらしい。
$ printf "\x61\x62\x63\x64"
abcd

しかし、文字列に対して1文字毎に何らかの文字列を挿入するリーズナブルな方法が見つからない。

Parameter Expansion だと
${parameter/pattern/string} はマッチする先頭の一ヶ所を置換
${parameter//pattern/string} はマッチするすべての箇所を置換置換
の意味なのでこれが使えそうな気がしたのだが、
どうも長さゼロの pattern は何にもマッチしてくれない(つまり狙いである各文字の間にマッチしてくれない)らしく、以下のように何も変化が得られなかった。
$ x=abcd
$ echo "${x///@}"
abcd

readarray (mapfile の別名) のデリミタに "" も用いてみたがこれも効果なし。
$ x=abcd
$ readarray -d "" x < <(echo -n "$x")
$ declare -p x
declare -a x=([0]="abcd")
ワイルドカードや正規表現のつもりで、"?"、"*"、"." を与えてみたがこれも当然のように効果がなかった。

sed とかを使えば良さそうなものだが、外部プロセス作成するペナルティが嫌だし、特に Cygwin の場合、致命的に遅くなるのでこれは論外。
速度的な観点だとループ組むのも極めて不本意なのだが、特に Cygwin の場合、プロセス生成のペナルティに比べればまだマシって事で、以下のようなコードでお茶を濁すことにした。
function str2hex () # [-v <var>] <str>
{
  local setvar=( "${@:1:$#-1}" )
  local str="${@: -1}"
  local n=${#str}
  local i tmp
  for (( i = 0; i < n; i++ )); do
    tmp+=( "'${str:i:1}" )
  done
  printf "${setvar[@]}" "%02x" "${tmp[@]}"
  (( ${#setvar[@]} )) || echo
}
function hex2str () # [-v <var>] <str>
{
  local setvar=( "${@:1:$#-1}" )
  local str="${@: -1}"
  local n=${#str}
  local i tmp
  for (( i = 0; i < n; i+=2 )); do
    tmp+="\x${str:i:2}"
  done
  printf "${setvar[@]}" "${tmp}"
  (( ${#setvar[@]} )) || echo
}
タグ

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

Wiki内検索

フリーエリア

管理人/副管理人のみ編集できます