在Python中散列一个整数以匹配Oracle的STANDARD_HASH
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在Python中散列一个整数以匹配Oracle的STANDARD_HASH相关的知识,希望对你有一定的参考价值。
在Oracle中,我的数据通过将整数传递到“STANDARD_HASH”进行了散列,如下所示。如何使用Python获取相同的哈希值?
传递给STANDARD_HASH的整数导致Oracle中的结果:
SELECT STANDARD_HASH(123, 'SHA256') FROM DUAL;
# A0740C0829EC3314E5318E1F060266479AA31F8BBBC1868DA42B9E608F52A09F
传入字符串时Python中的结果:
import hashlib
hashlib.sha256(str.encode(str(123))).hexdigest().upper()
# A665A45920422F9D417E4867EFDC4FB8A04A1F3FFF1FA07E998E86F7F7A27AE3
# I want to modify this function to get the hash value above.
也许这些信息也会有所帮助。我无法改变Oracle方面的任何内容,但如果可以,我会将列转换为CHAR
,它将提供与我当前的Python实现相同的值。一个例子如下。
当字符串传递给STANDARD_HASH时,Oracle中的结果:
SELECT STANDARD_HASH('123', 'SHA256') FROM DUAL;
# A665A45920422F9D417E4867EFDC4FB8A04A1F3FFF1FA07E998E86F7F7A27AE3 (matches Python result)
我做了几次尝试,比如简单地将一个整数传递给Python,但是这会导致需要字符串的错误。我也搜索了一种编码整数的方法,但没有取得任何进展。
Oracle以其自己的内部格式表示数字,可以使用Oracle中的dump()
函数查看。例如。,
SELECT dump(123) FROM dual;
Typ=2 Len=3: 194,2,24
因此,要在Python中对数字进行散列并获得与Oracle中相同的结果,您需要将Python数字转换为一组字节,就像Oracle在其内部进行操作一样。
可以在here找到对Oracle使用的内部逻辑的一个很好的分析。一个小的遗漏与终止负数有关是正确的。此外,它是从解码Oracle数字的角度编写的。在我们的例子中,我们需要将Oracle编号编码为其内部字节格式。然而,我在形成这个答案时广泛使用它。
下面的代码显示了一个Python函数to_oracle_number()
,它将返回一个整数数组,该数组具有与Oracle数据库计算的数字相同的字节表示形式。它应该处理任何数字(正数,负数,小数,零等)。
底部的代码还显示了如何调用此函数并对其结果进行散列以获得与Oracle数据库中计算的相同的散列值,我认为这是您问题的核心。
注意:该函数要求将要转换的数字作为字符串传入,以避免精度损失。
import math
import decimal
import hashlib
def to_oracle_number( nstr ):
# define number n that we want to convert
n = decimal.Decimal(nstr)
# compute exponent (base 100) and convert to Oracle byte along with sign
#print (abs(n))
l_exp = 0
l_len = 0
l_abs_n = abs(n)
if l_abs_n != 0:
l_exp = math.floor(math.log(l_abs_n,100))
# Oracle adds 1 to all bytes when encoding
l_exp = l_exp + 1
# Oracle adds 64 to exponent whe encoding
l_exp = l_exp + 64
if n < 0:
# take 1's complement of exponent so far (bitwise xor)
l_exp = (l_exp ^ 127)
if n >= 0:
# add sign bit. zero is considered positive.
l_exp = l_exp + 128
l_bytes = []
l_bytes.append(l_exp)
l_len = l_len + 1 # exponent and sign take 1 byte
l_whole_part = str(int(l_abs_n))
# make sure there is an even number of digits in the whole part
if len(l_whole_part) % 2 == 1:
l_whole_part = '0' + l_whole_part
# get the fractional digits, so if 0.01234, just 01234
l_frac_part = str(l_abs_n - int(l_abs_n))[2:]
# make sure there is an even number of digits in the fractional part
if len(l_frac_part) % 2 == 1:
l_frac_part = l_frac_part + '0'
l_mantissa = l_whole_part + l_frac_part
# chop off leading 00 pairs
while l_mantissa[0:2] == '00':
l_mantissa = l_mantissa[2:]
# chop off trailing 00 pairs
while l_mantissa[-2:] == '00':
l_mantissa = l_mantissa[:-2]
# compute number of 2-character chunks
l_chunk_count = int(len(l_mantissa) / 2)
l_chunks = '';
for i in range(0, l_chunk_count):
l_chunk = int(l_mantissa[i*2:i*2+2])
if n < 0:
# for negative numbers, we subtract from 100
l_chunk = 100-l_chunk
# Oracle adds 1 to all bytes
l_chunk = l_chunk + 1
# Add the chunk to our answer
l_chunks = l_chunks + ',' + str(l_chunk)
l_bytes.append(l_chunk)
l_len = l_len + 1 # we have computed one more byte
#print (str(i) + ':' + str(l_chunk))
if n < 0 and l_len < 21:
# terminating negative numbers always end in byte 102 (do not know why)
l_chunks = l_chunks + ',102'
l_bytes.append(102)
l_len = l_len + 1
l_computed_dump = 'Typ=2 Len=' + str(l_len) + ': ' + str(l_exp) + l_chunks
print (l_computed_dump)
print (l_bytes)
return l_bytes
# test it
m = hashlib.sha256()
b = bytes(to_oracle_number('123')) # pass a string so no precision errors
m.update(b)
print(m.hexdigest().upper())
OUTPUT
Typ=2 Len=3: 194,2,24 [194, 2, 24] A0740C0829EC3314E5318E1F060266479AA31F8BBBC1868DA42B9E608F52A09F
警告:线程的原始解决方案来自@Matthew McPeak's,答案应该得到回报,下面你会发现一个稍微修改过的版本,我在其算法中添加了一些重构:
import math
import decimal
import hashlib
def to_oracle_number(nstr):
n = decimal.Decimal(nstr)
# compute exponent (base 100) and convert to Oracle byte along with sign
l_exp, l_len, l_abs_n = 0, 0, abs(n)
if l_abs_n != 0:
l_exp = math.floor(math.log(l_abs_n, 100)) + 65
l_exp = (l_exp ^ 127) if n < 0 else l_exp + 128
l_bytes = [l_exp]
l_len += 1 # exponent and sign take 1 byte
l_whole_part = str(int(l_abs_n))
# make sure there is an even number of digits in the whole part
if len(l_whole_part) % 2 == 1:
l_whole_part = '0' + l_whole_part
# get the fractional digits, so if 0.01234, just 01234
l_frac_part = str(l_abs_n - int(l_abs_n))[2:]
# make sure there is an even number of digits in the fractional part
if len(l_frac_part) % 2 == 1:
l_frac_part += '0'
l_mantissa = l_whole_part + l_frac_part
# chop off leading 00 pairs
while l_mantissa[0:2] == '00':
l_mantissa = l_mantissa[2:]
# chop off trailing 00 pairs
while l_mantissa[-2:] == '00':
l_mantissa = l_mantissa[:-2]
# compute number of 2-character chunks
l_chunks = ''
for i in range(0, int(len(l_mantissa) / 2)):
l_chunk = int(l_mantissa[i * 2:i * 2 + 2])
if n < 0:
l_chunk = 100 - l_chunk
l_chunk += 1
l_chunks = f"{l_chunks},l_chunk"
l_bytes.append(l_chunk)
l_len += 1
if n < 0 and l_len < 21:
# terminating negative numbers always end in byte 102 (do not know why)
l_chunks += ',102'
l_bytes.append(102)
l_len += 1
# bytes(l_bytes)l_computed_dump = f"Typ=2 Len={l_len}: {l_exp}{l_chunks}"
m = hashlib.sha256()
m.update(bytes(l_bytes))
return m.hexdigest().upper()
if __name__ == '__main__':
assert to_oracle_number('123') == "A0740C0829EC3314E5318E1F060266479AA31F8BBBC1868DA42B9E608F52A09F"
以上是关于在Python中散列一个整数以匹配Oracle的STANDARD_HASH的主要内容,如果未能解决你的问题,请参考以下文章