ユニークな文字列(キー)を生成したい
はじめに
開発を行っていく中で、ユニークな文字列を必要なタイミングがあった。
本記事では、Pythonを利用した環境において、生成する文字列がユニークであることを目標とし、
その手法にどんなものがあるかをまとめる。
前提条件
- 生成する文字列は衝突する確率が低いこと
- 実装はPythonで行う
考えられる手段
- ランダムモジュールを利用する
- タイムスタンプを利用する
- UUIDを利用する
- ハッシュ関数を利用する
- ユニークキー生成ライブラリを利用する
1. ランダムモジュールを利用する
まずは単純にランダムな数字や文字を組み合わせてユニークな文字列を生成してみる。
だがしかし、いくらランダムとはいえ完全にユニークか?と言われるとそんなことはない。生成する文字列の長さや条件によって確率は変わってくるが、一定確率で重複してしまう可能性があり、都度重複チェックでも行わない限り不安は残るといえる。
Pythonのrandomモジュールを使うことで、ランダムな文字列を生成できる。
import random
import string
letters = string.ascii_letters + string.digits # アルファベット大文字小文字+0〜9の数字
print(''.join(random.choices(letters, 10))) # 10個ランダムに選んで文字列を生成
ou749CcLN0
2. タイムスタンプを利用する
現在時刻を取得し、それを文字列として置き換える。
確かにその瞬間は一度しかないので同じ文字列になることはなさそうだが、同じような方式を採用しているシステムなどがいたとしたら、重複してしまうしユニークと言っていいのかは微妙なところ。
Pythonのdatetimeモジュールを使うことで、現在時刻を取得できる。
from datetime import datetime
unique_string = datetime.now().strftime("%Y%m%d%H%M%S%f") # 現在時刻を取得して文字列に変換
print(unique_string)
20240209232853820291
3. UUIDを利用する
UUID(Universally Unique IDentifier)は36文字の英数字で構成されており、
340,282,366,920,938,463,463,374,607,431,768,211,456通りも表現することが可能であるため、世界的に一意である可能性が高い。
実際には生成されたUUIDが他のシステム上で利用されている可能性も0ではないが、同じシステム内で重複する可能性は限りなく低いといってもよい。
UUIDの一例:4916b6de-be83-412e-8f80-85a3b094ca4b
Pythonのuuidモジュールを使うことで、簡単にUUIDが生成できる。
import uuid
print(str(uuid.uuid4())
4916b6de-be83-412e-8f80-85a3b094ca4b
欠点としては、UUIDだと36文字もあるので、長くて使いにくい場合もある。
UUIDにはいくつかバージョンが存在していて、バージョンごとにそれぞれ生成ロジックが異なるようだ。
4. ハッシュ関数を利用する
ハッシュ値とはあるデータから固定長の値(文字列や数字の列)を生成することによって得られる値のこと。
この変換を行う関数をハッシュ関数と呼び、様々なアルゴリズムに応じて関数が用意されている。
ハッシュ関数は任意の長さのデータを入力しても常に固定長のハッシュ値を出力する。また、ハッシュ関数は一方向関数とされており、生成されたハッシュ値から元のデータを復元することは不可能となる。これらの特性を活かしてパスワードを直接DBに保存しないようにしたり、データの整合性の検証を行うといった用途で用いられる。
Pythonのhashlibモジュールを使うことで、ハッシュ値を生成してみる。
import hashlib
hash_object = hashlib.sha256(b"Hello Zenn")
hex_dig = hash_object.hexdigest()
print(hex_dig)
0fc65aeb8500ddebd3cb632439b250295be187add1bbf684bcc17aeed7419f04
5. ユニークキー生成ライブラリを利用する
Pythonに標準で用意されているライブラリだけではなく、ユニークキー生成用のライブラリも存在するため、それらを試してみる。今回はshortuuidとSqidsというライブラリを試してようと思う。
shortuuid
shortuuidは一般的な36文字で生成されるUUIDをより短い文字列として生成することで、UUIDの使いにくさを一部解消したライブラリ。これによってURLやデータベースの主キーとして使用する際に便利に扱うことができる。
また本ライブラリは生成される文字列の形式や長さをカスタマイズすることも可能となっている。
$ pip install shortuuid
import shortuuid
import uuid
# 短縮系のUUIDを生成する
print("UUID:" + shortuuid.uuid())
# 生成済みのUUIDのから短縮形のUUIDを生成, その逆も
normal_uuid = uuid.uuid4()
short_uuid = shortuuid.encode(normal_uuid)
print("Short UUID" + short_uuid)
print("Normal UUID" + shortuuid.decode(short_uuid))
# 生成するUUIDの長さを指定
print("Length11:" + shortuuid.ShortUUID().random(length=11))
# 生成するUUIDに含める文字列を指定
shortuuid.set_alphabet("abcdefg12345")
print("abcdefg12345:" + shortuuid.uuid())
UUID:AtmJKRfwoJZ4hvdCNcTiws
Short UUID:WexBXJSW9wK4CN2XWcG6C2
Normal UUID:a1043ea1-621d-4aac-9df1-e7a86a5b34ec
Length11:JVHfXwTV67
abcdefg123345:4g3c413adca2daff4142fe5111dcb2gdf151
Sqids
Sqidsは与えられた数値から短いユニーク文字列を生成することが可能なライブラリでPython以外にも様々なプログラミング言語で用意されている。Hashidsというライブラリが新しくなったものらしい。
生成される文字列の長さは、与える数値の桁数によって変化するため、必ず一意の長さとなるわけではないというところに注意が必要であった。ただ、本ライブラリも生成する文字列に使用する文字種類や最小長さを指定するといったことは可能であった。
$ pip install sqids
from sqids import Sqids
# 指定のリストからユニーク文字列を生成、その逆も
sqids = Sqids()
id = sqids.encode([1, 3, 6])
print(id)
print(sqids.decode(id))
# 生成する文字列の最小長さを指定
sqids = Sqids(min_length=10)
print(sqids.encode([1, 3, 6]))
# 生成する文字列に使用する文字を指定
sqids = Sqids(alphabet="abcdefg12345")
print(sqids.encode([1,3,6]))
147OLD
[1, 3, 6]
147OLDtn5s
521ded
まとめ
ユニークな文字列といっても完全にユニークであることを保証するのは難しい。ただ前述したようにいずれかの方法を用いて、高確率でユニーク(重複する可能性が少ない)ものを作ることは可能なので、いざというときはこれらを駆使して実装していきましょう。
なにかもっといい方法あるよって方がいましたらコメントお待ちしております。
Discussion