猿问

在 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,但这会导致需要一个字符串的错误。我还搜索了一种对整数进行编码的方法,但没有取得任何进展。


呼啦一阵风
浏览 179回答 2
2回答

至尊宝的传说

Oracle 以自己的内部格式表示数字,可以使用dump()Oracle 中的函数查看。例如,SELECT dump(123) FROM dual;Typ=2 Len=3: 194,2,24因此,要在 Python 中散列一个数字并获得与在 Oracle 中相同的结果,您需要将 Python 数字转换为一组字节,就像 Oracle 在其内部所做的那样。可以在此处找到对 Oracle 使用的内部逻辑的良好分析。这是正确的,有一个与终止负数有关的小遗漏。此外,它是从从字节解码Oracle 数字的角度编写的。在我们的例子中,我们需要将Oracle 数字编码为其内部字节格式。尽管如此,我在形成这个答案时广泛使用了它。下面的代码显示了一个 Python 函数,to_oracle_number(),它将返回一个整数数组,该数组具有与 Oracle 数据库计算的数字相同的字节表示形式。它应该处理任何数字(正数、负数、小数、零等)。最底部的代码还显示了如何调用此函数并对其结果进行散列以获得与在 Oracle 数据库中计算的相同的散列值,我认为这是您问题的核心。注意:该函数期望您要转换的数字作为字符串传入,以避免精度损失。import mathimport decimalimport hashlibdef to_oracle_number( nstr ):&nbsp; # define number n that we want to convert&nbsp; n = decimal.Decimal(nstr)&nbsp; # compute exponent (base 100) and convert to Oracle byte along with sign&nbsp; #print (abs(n))&nbsp; l_exp = 0&nbsp; l_len = 0&nbsp; l_abs_n = abs(n)&nbsp; if l_abs_n != 0:&nbsp; &nbsp; l_exp = math.floor(math.log(l_abs_n,100))&nbsp; &nbsp; # Oracle adds 1 to all bytes when encoding&nbsp; &nbsp; l_exp = l_exp + 1&nbsp; &nbsp; # Oracle adds 64 to exponent whe encoding&nbsp; &nbsp; l_exp = l_exp + 64&nbsp; if n < 0:&nbsp; &nbsp; # take 1's complement of exponent so far (bitwise xor)&nbsp; &nbsp; l_exp = (l_exp ^ 127)&nbsp; if n >= 0:&nbsp; &nbsp; # add sign bit.&nbsp; zero is considered positive.&nbsp; &nbsp; l_exp = l_exp + 128&nbsp; l_bytes = []&nbsp; l_bytes.append(l_exp)&nbsp; l_len = l_len + 1&nbsp; &nbsp;# exponent and sign take 1 byte&nbsp; l_whole_part = str(int(l_abs_n))&nbsp; # make sure there is an even number of digits in the whole part&nbsp; if len(l_whole_part) % 2 == 1:&nbsp; &nbsp; l_whole_part = '0' + l_whole_part&nbsp; # get the fractional digits, so if 0.01234, just 01234&nbsp; l_frac_part = str(l_abs_n - int(l_abs_n))[2:]&nbsp; # make sure there is an even number of digits in the fractional part&nbsp; if len(l_frac_part) % 2 == 1:&nbsp; &nbsp; l_frac_part = l_frac_part + '0'&nbsp; l_mantissa = l_whole_part + l_frac_part&nbsp; # chop off leading 00 pairs&nbsp; while l_mantissa[0:2] == '00':&nbsp; &nbsp; l_mantissa = l_mantissa[2:]&nbsp; # chop off trailing 00 pairs&nbsp; while l_mantissa[-2:] == '00':&nbsp; &nbsp; l_mantissa = l_mantissa[:-2]&nbsp; # compute number of 2-character chunks&nbsp; l_chunk_count = int(len(l_mantissa) / 2)&nbsp; l_chunks = '';&nbsp; for i in range(0, l_chunk_count):&nbsp; &nbsp; l_chunk = int(l_mantissa[i*2:i*2+2])&nbsp; &nbsp; if n < 0:&nbsp; &nbsp; &nbsp; # for negative numbers, we subtract from 100&nbsp; &nbsp; &nbsp; l_chunk = 100-l_chunk&nbsp; &nbsp; # Oracle adds 1 to all bytes&nbsp; &nbsp; l_chunk = l_chunk + 1&nbsp; &nbsp; # Add the chunk to our answer&nbsp; &nbsp; l_chunks = l_chunks + ',' + str(l_chunk)&nbsp; &nbsp; l_bytes.append(l_chunk)&nbsp; &nbsp; l_len = l_len + 1&nbsp; &nbsp;# we have computed one more byte&nbsp; &nbsp; #print (str(i) + ':' + str(l_chunk))&nbsp; if n < 0 and l_len < 21:&nbsp; &nbsp; # terminating negative numbers always end in byte 102 (do not know why)&nbsp; &nbsp; l_chunks = l_chunks + ',102'&nbsp; &nbsp; l_bytes.append(102)&nbsp; &nbsp; l_len = l_len + 1&nbsp; l_computed_dump = 'Typ=2 Len=' + str(l_len) + ': ' + str(l_exp) + l_chunks&nbsp; print&nbsp; (l_computed_dump)&nbsp; print&nbsp; (l_bytes)&nbsp; return l_bytes# test itm = hashlib.sha256()b = bytes(to_oracle_number('123'))&nbsp; # pass a string so no precision errorsm.update(b)print(m.hexdigest().upper())输出Typ=2 Len=3: 194,2,24[194, 2, 24]A0740C0829EC3314E5318E1F060266479AA31F8BBBC1868DA42B9E608F52A09F

慕盖茨4494581

警告:该线程的原始解决方案来自@Matthew McPeak's,这就是应该得到奖励的答案,下面你会发现一个稍微修改过的版本,但我在他的算法中添加了一些重构:import mathimport decimalimport hashlibdef to_oracle_number(nstr):&nbsp; &nbsp; n = decimal.Decimal(nstr)&nbsp; &nbsp; # compute exponent (base 100) and convert to Oracle byte along with sign&nbsp; &nbsp; l_exp, l_len, l_abs_n = 0, 0, abs(n)&nbsp; &nbsp; if l_abs_n != 0:&nbsp; &nbsp; &nbsp; &nbsp; l_exp = math.floor(math.log(l_abs_n, 100)) + 65&nbsp; &nbsp; l_exp = (l_exp ^ 127) if n < 0 else l_exp + 128&nbsp; &nbsp; l_bytes = [l_exp]&nbsp; &nbsp; l_len += 1&nbsp; &nbsp;# exponent and sign take 1 byte&nbsp; &nbsp; l_whole_part = str(int(l_abs_n))&nbsp; &nbsp; # make sure there is an even number of digits in the whole part&nbsp; &nbsp; if len(l_whole_part) % 2 == 1:&nbsp; &nbsp; &nbsp; &nbsp; l_whole_part = '0' + l_whole_part&nbsp; &nbsp; # get the fractional digits, so if 0.01234, just 01234&nbsp; &nbsp; l_frac_part = str(l_abs_n - int(l_abs_n))[2:]&nbsp; &nbsp; # make sure there is an even number of digits in the fractional part&nbsp; &nbsp; if len(l_frac_part) % 2 == 1:&nbsp; &nbsp; &nbsp; &nbsp; l_frac_part += '0'&nbsp; &nbsp; l_mantissa = l_whole_part + l_frac_part&nbsp; &nbsp; # chop off leading 00 pairs&nbsp; &nbsp; while l_mantissa[0:2] == '00':&nbsp; &nbsp; &nbsp; &nbsp; l_mantissa = l_mantissa[2:]&nbsp; &nbsp; # chop off trailing 00 pairs&nbsp; &nbsp; while l_mantissa[-2:] == '00':&nbsp; &nbsp; &nbsp; &nbsp; l_mantissa = l_mantissa[:-2]&nbsp; &nbsp; # compute number of 2-character chunks&nbsp; &nbsp; l_chunks = ''&nbsp; &nbsp; for i in range(0, int(len(l_mantissa) / 2)):&nbsp; &nbsp; &nbsp; &nbsp; l_chunk = int(l_mantissa[i * 2:i * 2 + 2])&nbsp; &nbsp; &nbsp; &nbsp; if n < 0:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; l_chunk = 100 - l_chunk&nbsp; &nbsp; &nbsp; &nbsp; l_chunk += 1&nbsp; &nbsp; &nbsp; &nbsp; l_chunks = f"{l_chunks},l_chunk"&nbsp; &nbsp; &nbsp; &nbsp; l_bytes.append(l_chunk)&nbsp; &nbsp; &nbsp; &nbsp; l_len += 1&nbsp; &nbsp; if n < 0 and l_len < 21:&nbsp; &nbsp; &nbsp; &nbsp; # terminating negative numbers always end in byte 102 (do not know why)&nbsp; &nbsp; &nbsp; &nbsp; l_chunks += ',102'&nbsp; &nbsp; &nbsp; &nbsp; l_bytes.append(102)&nbsp; &nbsp; &nbsp; &nbsp; l_len += 1&nbsp; &nbsp; # bytes(l_bytes)l_computed_dump = f"Typ=2 Len={l_len}: {l_exp}{l_chunks}"&nbsp; &nbsp; m = hashlib.sha256()&nbsp; &nbsp; m.update(bytes(l_bytes))&nbsp; &nbsp; return m.hexdigest().upper()if __name__ == '__main__':&nbsp; &nbsp; assert to_oracle_number('123') == "A0740C0829EC3314E5318E1F060266479AA31F8BBBC1868DA42B9E608F52A09F"
随时随地看视频慕课网APP

相关分类

Python
我要回答