【问题标题】:Output of crc32b in PHP is not equal to PythonPHP中crc32b的输出不等于Python
【发布时间】:2019-07-09 19:20:55
【问题描述】:

我正在尝试将PHP sn-p 转换为Python3 代码,但printecho 的输出不同。

你可以在步骤1中看到它。

你知道问题出在哪里吗?我也在附加输入数组,但我认为它们是相等的。

�W2+ vs ee7523b2

编辑

当我将 raw 从 TRUE 切换到 FALSE 时,第一步的输出是相同的。 $d = strrev(hash("crc32b", $d, FALSE)) . $d

但问题是我必须将 PHP 转换为 Python,而不是相反,因为我在第 2 步中使用它,我需要有相同的输出。

PHP 输出 (CMD)

0 ->    1   1   100 EUR 20190101    11111111                Faktúra 1   SK6807200002891987426353        0   0
1 -> �W2+   1   1   100 EUR 20190101    11111111                Faktúra 1   SK6807200002891987426353        0   0
2 -> 00004e00007715c242b04d5014490af1445dd61c1527ddc5f4461ca5886caf63fd8fbcf7df69c2035760ecb28d8171efdb409c0206996498ea7921e715172e60c210f923f070079ffba40000

Python 输出

-------
0 ->    1   1   100 EUR 20190101    11111111                Faktúra 1   SK6807200002891987426353        0   0
1 -> ee7523b2   1   1   100 EUR 20190101    11111111                Faktúra 1   SK6807200002891987426353        0   0
2 -> b'00006227515c7830302762275c783030325c7865305c7864386a34585c7862346d5c7838665c7865625c7863315c786266625c7839625c786339675c786332785c7831645c7862392c415c7862625c7831645c78663770365c786463735c786236572d606c225c7865355c7865635c7831345c7863655c786331205c7830635c7831315c7861375c7839345c7864665c7865635c7830365c7831652c22265c7866355c7862335c7866345c78616145585c7861625c7866395c7839615c7839645c7865645c7864625c7830305c7864355c7861643b5c7865365f5c7866645c786533405c78303027'

PHP

<?php
$suma = "100";
$datum = "20190101";
$varsym = "11111111";
$konsym = "";
$specsym = "";
$poznamka = "Faktúra";
$iban = "SK6807200002891987426353";
$swift = "";

$d = implode("\t", array(
    0 => '',
    1 => '1',
    2 => implode("\t", array(
        true,
        $suma,                      // SUMA
        'EUR',                      // JEDNOTKA
        $datum,                 // DATUM
        $varsym,                    // VARIABILNY SYMBOL
        $konsym,                        // KONSTANTNY SYMBOL
        $specsym,                       // SPECIFICKY SYMBOL
        '',
        $poznamka,                  // POZNAMKA
        '1',
        $iban,  // IBAN
        $swift,                 // SWIFT
        '0',
        '0'
    ))
));
// 0
echo "0 -> ".$d."\n";
$d = strrev(hash("crc32b", $d, TRUE)) . $d;
// 1
echo "1 -> ".$d."\n";
$x = proc_open("/usr/bin/xz '--format=raw' '--lzma1=lc=3,lp=0,pb=2,dict=128KiB' '-c' '-'", [0 => ["pipe", "r"], 1 => ["pipe", "w"]], $p);
fwrite($p[0], $d);
fclose($p[0]);
$o = stream_get_contents($p[1]);
fclose($p[1]);
proc_close($x);

$d = bin2hex("\x00\x00" . pack("v", strlen($d)) . $o);
// 2
echo "2 -> ".$d."\n";
?>

Python

    def crc32b(x):
        h = zlib.crc32(x)
        x='%08X' % (h & 0xffffffff,)
        return x.lower()

    t = "\t"
    gen = t.join(["1",
                  "100", # SAME VARIABLES 
                  "EUR",
                  "20190101",
                  "11111111",
                  "",
                  "",
                  "",
                  "Faktúra",
                  "1",
                  "SK6807200002891987426353",
                  "",
                  "0",
                  "0"]
                 )

    d = t.join([
        "", "1", gen])
    # 0
    print(f"0 -> {d}")
    hashD = crc32b(d.encode()) # OK

    hashD = hashD[::-1]
    # hashD = str(binascii.unhexlify(hashD))
    d = hashD + d
    # 1
    print(f"1 -> {d}")
    args = shlex.split("xz '--format=raw' '--lzma1=lc=3,lp=0,pb=2,dict=128KiB' -c -")
    process = subprocess.Popen(args, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE)
    output = process.communicate(d.encode())

    pack = "\x00\x00" + str(struct.pack("H", len(d))) + str(output[0])

    d = binascii.hexlify(pack.encode())
    # 2
    print(f"2 -> {d}")

【问题讨论】:

  • 你会怎么做?我添加了第 2 步。它们必须相等。
  • @aws_apprentice 是 crc32b 而不是 crc32,如您所见,函数内部使用了 zlib

标签: php python crc


【解决方案1】:

你只需要删除函数hash()的第三个参数

如果您将此参数设置为 true,hash 将返回原始二进制数据,并且 php 会尝试将其解析为文本字符串,而您期望得到十六进制结果

【讨论】:

  • 谢谢,但我需要编辑 Python 代码以获得相同的结果,而不是相反。你会怎么做?它是更大脚本的一部分,我需要使用另一行的输出。
【解决方案2】:

不适用于 PHP
根据[Python 3]: zlib.crc32(data[, value])强调是我的):

计算 数据 的 CRC(循环冗余校验)校验和。结果是一个无符号 32 位整数

您混淆了:

  • 它的值——也可以看作是一个长度为4的ASCII字符串
  • 其值的文本表示(在 base 16 中) - 长度为 8 的字符串
>>> crc = 0x2B3257EE  # The value returned by zlib.crc32 for your text
>>> type(crc), crc
(<class 'int'>, 724719598)
>>>
>>> [chr((crc >> shift_bits) & 0xFF) for shift_bits in [0, 8, 16, 24]]
['î', 'W', '2', '+']

注意事项

  • 一种方法是将数字的 4 个字节中的每一个转换为 char
  • 要从 uint32 值中获取一个字节,uint32 必须按该 byte 的顺序向右移动 ([Python.Wiki]: BitwiseOperators) em> 中的值 ([3, 2, 1, 0]) 乘以 8(数量字节中的位)
    • 此外,为了摆脱不需要的字节(除了最右边的字节之外的任何字节),结果值也0xFF (255)
  • 由于little endian,字节以相反的顺序(从右到左)转换为char
  • 1stchar ('î') 看起来不同,但这只是表示的问题(在我的控制台中 vs 你的)

将它集成到您​​的代码中,您需要修改您的 crc32b 函数(并删除对 hashD 的任何进一步处理)以:

def crc32b(x):
    crc = zlib.crc32(x)
    return "".join([chr((crc >> shift_bits) & 0xFF) for shift_bits in [0, 8, 16, 24]])

有关此一般主题的更多详细信息,请查看[SO]: Python struct.pack() behavior (@CristiFati's answer)

@EDIT0

添加从hex表示开始的版本:

>>> crc = 0x2B3257EE
>>> crc_hex = "{:08X}".format(crc)
>>> crc_hex
'2B3257EE'
>>>
>>> list(reversed([chr(int(crc_hex[2 * i] + crc_hex[2 * i + 1], 16)) for i in range(len(crc_hex) // 2)]))
['î', 'W', '2', '+']
  • 从我的 PoV 来看,这更丑陋,而且效率低下(许多来回转换),但无论如何都会发布,因为有些人在进行位操作时遇到了麻烦
  • 关键是一次处理2个hexchar,转换后才反转

【讨论】:

  • 嘿,这回答了你的问题吗?
猜你喜欢
  • 2018-11-23
  • 2023-03-05
  • 1970-01-01
  • 1970-01-01
  • 2012-08-07
  • 2020-07-03
  • 1970-01-01
  • 1970-01-01
  • 2013-04-20
相关资源
最近更新 更多