文字列の操作について以前のエントリで少しだけ触れたが、今回は数値についての操作だ。2進数から16進数へ変換したり、その逆を行ったり。桁揃えしたり。深く意識しなくても呼吸を行うようにやることになると思う。
開発環境:
Python 3.9.2
参考情報:
まずは単純な変換。
>>> num = 0b11111111 # 2進数を
>>> oct(num) # 8進数に
'0o377'
>>> int(num) # 10進数に
255
>>> hex(num) # 16進数に
'0xff'
>>> num = 0xaf82 # 16進数を
>>> bin(num) # 2進数に
'0b1010111110000010'
>>> oct(num) # 8進数に
'0o127602'
>>> int(num) # 10進数に
44930
淡々とゼロパディング。
>>> num = 999
>>> format(num, '010') # 10桁かつ10進数
'0000000999'
>>> format(num, '010x') # 10桁かつ16進数
'00000003e7'
>>> format(num, '010b') # 10桁かつ2進数
'1111100111'
>>> f'{num:010}' # f-stringsでもOK
'0000000999'
>>> num = '999' # strにはzfillでゼロパディングできる
>>> num.zfill(10)
'0000000999'
フォーマットに関して細かな指定を行う場合は下記のように行う。
>>> num = 1234.5678
>>> f'{num:0>20,.3f}' # 0埋めかつ右寄せ(>)の20桁 3桁区切り文字は「,」 、小数点以下3桁まで(四捨五入)
'000000000001,234.568'
以前、RFC2132で記載されている「9.14. Client-identifier」をサーバのAPI経由で取得した際に、想定と異なるフォーマットの値だった。やむなく値を幾つか採取して下記のようなクラスを実装した。このクラスでサーバから得た値に含まれる8進数の変換や、文字列をASCIIコードへ変換する処理を行なっている。
class UIDConv:
def __init__(self):
self.uid = ''
self.client_id = ''
def make_client_id(self):
# uidの先頭と末尾のダブルクオートのみ削除する
self.uid = self.uid[1:-1]
# uidを\\3桁の数字(oct)、\\文字列の組み合わせ、それ以外に分割したリスト作成
uid_split = re.findall(r'\\[0-7]{3}|\\\D|\D|\d', self.uid)
# リストから\\を削除
uid_omitted = [None] * len(uid_split)
for i, element in enumerate(uid_split):
if element == '\\\\':
uid_omitted[i] = '\\'
elif re.search(r'\\[0-7]{3}|\\\D', element):
uid_omitted[i] = element.strip('\\')
else:
uid_omitted[i] = element
# octをhexにそれ以外をhexへ変換
try:
# このような書き方はリスト内包表記といえど律速になるはず
finished = [(format(int(elm, 8), '02x') \
if re.search('[0-7]{3}', elm)\
else format(ord(elm), '02x')) for elm in uid_omitted]
except TypeError as e:
print(f'wrong type element: {uid_omitted}')
sys.exit(1)
except ValueError as e:
print(f'wrong value element: {uid_omitted}')
sys.exit(1)
else:
# :をデリミタとしてjoin
self.client_id = ':'.join(finished)
このクラスへ適当なダミーデータ与えてコンバートすると「:」区切りの文字列が得られる。これが欲しかった形式の値であり、特定セグメントで定期的に自動登録するプログラムに用いている。
>>> pc1 = r'"\001\024\213\365\004R\\"'
>>> pc2 = r'"\001\070\340o\0518Y"'
>>> pc3 = r'"\001\157\340o\121\177\'"'
>>> pc4 = r'"\001\177\340o\021\177\""'
>>>
>>> test = UIDConv()
>>> test.uid = pc1
>>> test.make_client_id()
>>> test.client_id
'01:14:8b:f5:04:52:5c'
>>> del test
>>>
>>> test = UIDConv()
>>> test.uid = pc2
>>> test.make_client_id()
>>> test.client_id
'01:38:e0:6f:29:38:59'
>>> del test
>>>
>>> test = UIDConv()
>>> test.uid = pc3
>>> test.make_client_id()
>>> test.client_id
'01:6f:e0:6f:51:7f:27'
>>> del test
>>>
>>> test = UIDConv()
>>> test.uid = pc4
>>> test.make_client_id()
>>> test.client_id
'01:7f:e0:6f:11:7f:22'
>>> del test
上記のクラスのformatの部分もf-stringsにしてみよう。
format(int(elm, 8), '02x') # 3桁の8進数(str)を2桁の16進数(str)に変換するための処理
format(ord(elm), '02x')) # [0-9A-Za-z]{1}をASCIIコード(2桁の16進数(str))に変換するための処理
>>> num = '377'
>>> f'{int(num, 8):02x}'
'ff'
>>> foo = 'A'
>>> f'{ord(foo):02x}'
'41'
まとめ:
アプローチが異なるため操作対象がstrかintか意識しよう
とにかく使うから自然に身に付く