在Python中散列一个整数,以匹配Oracle的标准_散列

2024-10-03 11:24:10 发布

您现在位置:Python中文网/ 问答频道 /正文

在Oracle中,我的数据是通过将一个整数传入“STANDARD_HASH”进行哈希处理的,如下所示。如何使用Python获得相同的哈希值?在

当一个整数传递到标准的\u散列时,会产生Oracle:

SELECT STANDARD_HASH(123, 'SHA256') FROM DUAL;
# A0740C0829EC3314E5318E1F060266479AA31F8BBBC1868DA42B9E608F52A09F

传入字符串时生成Python:

^{pr2}$

也许这些信息也会有所帮助。我不能在Oracle端更改任何内容,但是如果可以的话,我会将该列转换为CHAR,它将给出与当前Python实现相同的值。下面是一个例子。在

当一个字符串传递到标准的\u散列时,会导致Oracle:

SELECT STANDARD_HASH('123', 'SHA256') FROM DUAL;
# A665A45920422F9D417E4867EFDC4FB8A04A1F3FFF1FA07E998E86F7F7A27AE3 (matches Python result)

我做了几次尝试,比如简单地向Python传递一个整数,但是这会导致需要字符串的错误。我也在寻找一种对整数进行编码的方法,但没有取得任何进展。在


Tags: 数据字符串from信息内容标准整数hash
2条回答

警告:该线程的原始解决方案来自@Matthew McPeak,这是应该得到奖励的答案,下面您会发现一个稍微修改的版本,其中我为他的算法添加了一些重构:

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"

Oracle用它自己的内部格式表示数字,使用Oracle中的dump()函数可以看到这种格式。E、 g

SELECT dump(123) FROM dual;
Typ=2 Len=3: 194,2,24

因此,要想在Python中散列一个数字并得到与Oracle中相同的结果,您需要将Python数字转换为一组字节,就像Oracle在其内部进行的那样。在

对Oracle使用的内部逻辑的一个很好的分析可以在here中找到。它是正确的,有一个小的遗漏与终止负数有关。另外,它是从从字节解码甲骨文数字的角度编写的。在我们的例子中,我们需要将一个Oracle数字编码为其内部字节格式。然而,我在形成这个答案时广泛地使用了它。在

下面的代码显示了一个Python函数to_oracle_number(),它将返回一个整数数组,该数组具有与Oracle数据库计算的数字相同的字节表示形式。它应该处理任何数字(正、负、分数、零等)。在

最底层的代码还显示了如何调用此函数并对其结果进行哈希处理以获得与Oracle数据库中计算的哈希值相同的哈希值,我相信这是您问题的核心。在

注意:该函数要求将要转换的数字作为字符串传入,以避免精度损失。在

^{pr2}$

输出

Typ=2 Len=3: 194,2,24
[194, 2, 24]
A0740C0829EC3314E5318E1F060266479AA31F8BBBC1868DA42B9E608F52A09F

相关问题 更多 >