정수를 파이썬에서 가장 짧은 URL 안전 문자열로 변환하는 방법은 무엇입니까?
URL에서 정수를 표현하는 가장 짧은 방법을 원합니다. 예를 들어 11234는 16 진수를 사용하여 '2be2'로 줄일 수 있습니다. base64 사용은 64 자 인코딩이므로 16 진수보다 적은 문자를 사용하여 base64로 정수를 나타낼 수 있어야합니다. 문제는 Python을 사용하여 정수를 base64로 (그리고 다시) 변환하는 가장 깨끗한 방법을 알아낼 수 없다는 것입니다.
base64 모듈에는 바이트 문자열을 처리하는 방법이 있습니다. 따라서 한 가지 해결책은 정수를 이진 표현으로 Python 문자열로 변환하는 것입니다 ...하지만 그 방법도 잘 모르겠습니다.
이 답변은 Douglas Leeder의 정신과 유사하며 다음과 같은 변경 사항이 있습니다.
- 실제 Base64를 사용하지 않으므로 패딩 문자가 없습니다.
먼저 숫자를 바이트 문자열 (기본 256)로 변환하는 대신 기본 64로 직접 변환하므로 부호 문자를 사용하여 음수를 나타낼 수 있다는 장점이 있습니다.
import string ALPHABET = string.ascii_uppercase + string.ascii_lowercase + \ string.digits + '-_' ALPHABET_REVERSE = dict((c, i) for (i, c) in enumerate(ALPHABET)) BASE = len(ALPHABET) SIGN_CHARACTER = '$' def num_encode(n): if n < 0: return SIGN_CHARACTER + num_encode(-n) s = [] while True: n, r = divmod(n, BASE) s.append(ALPHABET[r]) if n == 0: break return ''.join(reversed(s)) def num_decode(s): if s[0] == SIGN_CHARACTER: return -num_decode(s[1:]) n = 0 for c in s: n = n * BASE + ALPHABET_REVERSE[c] return n
>>> num_encode(0)
'A'
>>> num_encode(64)
'BA'
>>> num_encode(-(64**5-1))
'$_____'
몇 가지 참고 사항 :
- 알파벳에 string.digits를 먼저 넣고 기호 문자 '-'를 만들어 base-64 숫자의 가독성을 약간 높일 수 있습니다 . 저는 Python의 urlsafe_b64encode를 기반으로 한 순서를 선택했습니다.
- 많은 음수를 인코딩하는 경우 부호 문자 대신 부호 비트 또는 1/2의 보수를 사용하여 효율성을 높일 수 있습니다.
- 영숫자 문자로만 제한하거나 "URL 안전"문자를 추가하기 위해 알파벳을 변경하여이 코드를 다른 기반에 쉽게 적용 할 수 있어야합니다.
- 내가 추천 에 대해 대부분의 URI에서 기본 10 이외의 표현을 사용하는 경우를-는 복잡성을 추가하고 더 열심히 상당한 절감없이 오버 헤드에 비해 디버깅하게 HTTP-하지 않는 한 당신은 뭔가 티니 URL - 억양에 대한 것입니다.
Base64에 관한 모든 답변은 매우 합리적인 솔루션입니다. 그러나 그들은 기술적으로 잘못되었습니다. 정수를 가능한 가장 짧은 URL 안전 문자열 로 변환하려면 원하는 것은 기본 66입니다 ( 66 개의 URL 안전 문자가 있음 ).
해당 코드는 다음과 같습니다.
from io import StringIO
import urllib
BASE66_ALPHABET = u"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.~"
BASE = len(BASE66_ALPHABET)
def hexahexacontadecimal_encode_int(n):
if n == 0:
return BASE66_ALPHABET[0].encode('ascii')
r = StringIO()
while n:
n, t = divmod(n, BASE)
r.write(BASE66_ALPHABET[t])
return r.getvalue().encode('ascii')[::-1]
다음은 pip 설치 가능 패키지로 사용할 준비가 된 이와 같은 체계의 완전한 구현입니다.
https://github.com/aljungberg/hhc
이를 위해 실제 base64 인코딩을 원하지 않을 것입니다. 패딩 등을 추가하여 잠재적으로 작은 숫자의 경우 16 진수보다 큰 문자열을 생성 할 수 있습니다. 다른 것과 상호 운용 할 필요가 없으면 자체 인코딩을 사용하십시오. 예 : 다음은 모든 염기로 인코딩하는 함수입니다 (숫자는 실제로 추가 reverse () 호출을 피하기 위해 최하위 우선으로 저장됩니다.
def make_encoder(baseString):
size = len(baseString)
d = dict((ch, i) for (i, ch) in enumerate(baseString)) # Map from char -> value
if len(d) != size:
raise Exception("Duplicate characters in encoding string")
def encode(x):
if x==0: return baseString[0] # Only needed if don't want '' for 0
l=[]
while x>0:
l.append(baseString[x % size])
x //= size
return ''.join(l)
def decode(s):
return sum(d[ch] * size**i for (i,ch) in enumerate(s))
return encode, decode
# Base 64 version:
encode,decode = make_encoder("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
assert decode(encode(435346456456)) == 435346456456
이것은 인코더의 기본 문자열에 적절한 문자를 추가하기 만하면 원하는 기본을 사용할 수 있다는 장점이 있습니다.
그러나 더 큰 기지에 대한 이득은 그다지 크지 않을 것입니다. 기본 64는 크기를 기본 16의 2/3로만 줄입니다 (4 대신 6 비트 / 문자). 각 배가는 문자 당 하나의 비트 만 추가합니다. 실제로 압축 할 필요가없는 한 16 진수를 사용하는 것이 가장 간단하고 빠른 옵션 일 것입니다.
인코딩하려면 n
:
data = ''
while n > 0:
data = chr(n & 255) + data
n = n >> 8
encoded = base64.urlsafe_b64encode(data).rstrip('=')
디코딩하려면 s
:
data = base64.urlsafe_b64decode(s + '===')
decoded = 0
while len(data) > 0:
decoded = (decoded << 8) | ord(data[0])
data = data[1:]
일부 "최적"인코딩에 대해 다른 것과 동일한 정신으로 RFC 1738에 따라 73 자를 사용할 수 있습니다 ( "+"를 사용할 수있는 것으로 간주하면 실제로 74 자).
alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_`\"!$'()*,-."
encoded = ''
while n > 0:
n, r = divmod(n, len(alphabet))
encoded = alphabet[r] + encoded
그리고 디코딩 :
decoded = 0
while len(s) > 0:
decoded = decoded * len(alphabet) + alphabet.find(s[0])
s = s[1:]
쉬운 비트는 바이트 문자열을 웹 안전 base64로 변환하는 것입니다.
import base64
output = base64.urlsafe_b64encode(s)
까다로운 비트는 첫 번째 단계입니다. 정수를 바이트 문자열로 변환합니다.
정수가 작 으면 16 진수로 인코딩하는 것이 좋습니다 -saua 참조
그렇지 않으면 (해키 재귀 버전) :
def convertIntToByteString(i):
if i == 0:
return ""
else:
return convertIntToByteString(i >> 8) + chr(i & 255)
base64 인코딩을 원하지 않고 숫자 base X에서 base 10 숫자를 나타내려고합니다.
사용 가능한 26 글자로 기본 10 숫자를 표시하려면 http://en.wikipedia.org/wiki/Hexavigesimal을 사용할 수 있습니다 . (모든 유효한 URL 문자를 사용하여 훨씬 더 큰 기반으로이 예제를 확장 할 수 있습니다.)
최소한 기본 38 (26 개의 문자, 10 개의 숫자, +, _)을 얻을 수 있어야합니다.
Base64는 4 바이트 / 문자를 사용하여 3 바이트를 인코딩하고 3 바이트의 배수 만 인코딩 할 수 있습니다 (그렇지 않으면 패딩 추가).
따라서 Base64에서 4 바이트 (평균 int)를 나타내는 데 8 바이트가 걸립니다. 같은 4 바이트를 16 진수로 인코딩하면 8 바이트가 사용됩니다. 따라서 단일 int에 대해서는 아무것도 얻지 못할 것입니다.
약간 해키하지만 작동합니다.
def b64num(num_to_encode):
h = hex(num_to_encode)[2:] # hex(n) returns 0xhh, strip off the 0x
h = len(h) & 1 and '0'+h or h # if odd number of digits, prepend '0' which hex codec requires
return h.decode('hex').encode('base64')
.encode ( 'base64')에 대한 호출을 urlsafe_b64encode ()와 같은 base64 모듈의 항목으로 바꿀 수 있습니다.
zbase62라는 작은 라이브러리를 유지합니다. http://pypi.python.org/pypi/zbase62
이를 통해 Python 2 str 객체에서 base-62로 인코딩 된 문자열로 또는 그 반대로 변환 할 수 있습니다.
Python 2.7.1+ (r271:86832, Apr 11 2011, 18:13:53)
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> d = os.urandom(32)
>>> d
'C$\x8f\xf9\x92NV\x97\x13H\xc7F\x0c\x0f\x8d9}\xf5.u\xeeOr\xc2V\x92f\x1b=:\xc3\xbc'
>>> from zbase62 import zbase62
>>> encoded = zbase62.b2a(d)
>>> encoded
'Fv8kTvGhIrJvqQ2oTojUGlaVIxFE1b6BCLpH8JfYNRs'
>>> zbase62.a2b(encoded)
'C$\x8f\xf9\x92NV\x97\x13H\xc7F\x0c\x0f\x8d9}\xf5.u\xeeOr\xc2V\x92f\x1b=:\xc3\xbc'
그러나 여전히 정수에서 str으로 변환해야합니다. 이것은 Python 3에 내장되어 있습니다.
Python 3.2 (r32:88445, Mar 25 2011, 19:56:22)
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> d = os.urandom(32)
>>> d
b'\xe4\x0b\x94|\xb6o\x08\xe9oR\x1f\xaa\xa8\xe8qS3\x86\x82\t\x15\xf2"\x1dL%?\xda\xcc3\xe3\xba'
>>> int.from_bytes(d, 'big')
103147789615402524662804907510279354159900773934860106838120923694590497907642
>>> x= _
>>> x.to_bytes(32, 'big')
b'\xe4\x0b\x94|\xb6o\x08\xe9oR\x1f\xaa\xa8\xe8qS3\x86\x82\t\x15\xf2"\x1dL%?\xda\xcc3\xe3\xba'
파이썬 2에서 int에서 바이트로 또는 그 반대로 변환하려면 내가 아는 한 편리하고 표준적인 방법이 없습니다. 편의를 위해 https://github.com/warner/foolscap/blob/46e3a041167950fa93e48f65dcf106a576ed110e/foolscap/banana.py#L41 과 같은 일부 구현 을 zbase62에 복사해야 할 것 같습니다 .
If you are looking for a way to shorten the integer representation using base64, I think you need to look elsewhere. When you encode something with base64 it doesn't get shorter, in fact it gets longer.
E.g. 11234 encoded with base64 would yield MTEyMzQ=
When using base64 you have overlooked the fact that you are not converting just the digits (0-9) to a 64 character encoding. You are converting 3 bytes into 4 bytes so you are guaranteed your base64 encoded string would be 33.33% longer.
I needed a signed integer, so I ended up going with:
import struct, base64
def b64encode_integer(i):
return base64.urlsafe_b64encode(struct.pack('i', i)).rstrip('=\n')
Example:
>>> b64encode_integer(1)
'AQAAAA'
>>> b64encode_integer(-1)
'_____w'
>>> b64encode_integer(256)
'AAEAAA'
I'm working on making a pip package for this.
I recommend you use my bases.py https://github.com/kamijoutouma/bases.py which was inspired by bases.js
from bases import Bases
bases = Bases()
bases.toBase16(200) // => 'c8'
bases.toBase(200, 16) // => 'c8'
bases.toBase62(99999) // => 'q0T'
bases.toBase(200, 62) // => 'q0T'
bases.toAlphabet(300, 'aAbBcC') // => 'Abba'
bases.fromBase16('c8') // => 200
bases.fromBase('c8', 16) // => 200
bases.fromBase62('q0T') // => 99999
bases.fromBase('q0T', 62) // => 99999
bases.fromAlphabet('Abba', 'aAbBcC') // => 300
refer to https://github.com/kamijoutouma/bases.py#known-basesalphabets for what bases are usable
For your case
I recommend you use either base 32, 58 or 64
Base-64 warning: besides there being several different standards, padding isn't currently added and line lengths aren't tracked. Not recommended for use with APIs that expect formal base-64 strings!
Same goes for base 66 which is currently not supported by both bases.js and bases.py but it might in the future
I'd go the 'encode integer as binary string, then base64 encode that' method you suggest, and I'd do it using struct:
>>> import struct, base64
>>> base64.b64encode(struct.pack('l', 47))
'LwAAAA=='
>>> struct.unpack('l', base64.b64decode(_))
(47,)
Edit again: To strip out the extra 0s on numbers that are too small to need full 32-bit precision, try this:
def pad(str, l=4):
while len(str) < l:
str = '\x00' + str
return str
>>> base64.b64encode(struct.pack('!l', 47).replace('\x00', ''))
'Lw=='
>>> struct.unpack('!l', pad(base64.b64decode('Lw==')))
(47,)
Pure python, no dependancies, no encoding of byte strings etc. , just turning a base 10 int into base 64 int with the correct RFC 4648 characters:
def tetrasexagesimal(number):
out=""
while number>=0:
if number == 0:
out = 'A' + out
break
digit = number % 64
out = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[digit] + out
number /= 64 # //= 64 for py3 (thank spanishgum!)
if number == 0:
break
return out
tetrasexagesimal(1)
'IT Share you' 카테고리의 다른 글
진술 / 텍스트가 얼마나 긍정적인지 부정적인지를 결정하는 알고리즘 (0) | 2020.11.24 |
---|---|
C ++의 리더 / 라이터 잠금 (0) | 2020.11.23 |
파일이 존재하고 C ++에서 읽을 수 있는지 확인하는 방법은 무엇입니까? (0) | 2020.11.23 |
행렬의 행에 벡터를 곱 하시겠습니까? (0) | 2020.11.23 |
동일한 사용자 이름으로 다른 테이블의 테이블 값 업데이트 (0) | 2020.11.23 |