0

我正在尝试将以下 C 代码(基本上只是尝试将任意整数值转换为字符池中的字符)转换为 PHP:

#include <cstdint>
#include <cstring>
#include <iostream>

uint8_t GetCharacter(uint32_t value) {
    static const char* valid_characters = "0123456789ABCDEFGHIJKLMOPQRSTUVWabcdefghijklmnopqrstuvw";
    static const size_t valid_characters_l = strlen(valid_characters);
    uint8_t c = valid_characters[value % valid_characters_l];
    return valid_characters[(value << c) % valid_characters_l];
}

int main() {
    uint32_t array[] = {176, 52, 608, 855};
    for (size_t i=0; i < 4; i++) {
        uint8_t c = GetCharacter(array[i]);
        std::cout << array[i] << ": " << (uint32_t) c << "\n";
    }
    return 0;
}

哪个产量

176: 109
52: 114
608: 85
855: 65

我已经能够提出的 PHP 代码产生以下结果:

176: 109
52: 114
608: 85
855: 104   // << Here's the problem

我很确定我准确地翻译了它,但我找不到问题。

<?php

function getCharacter($index) {
    $chars = "0123456789ABCDEFGHIJKLMOPQRSTUVWabcdefghijklmnopqrstuvw";
    $c = ord(substr($chars, $index % strlen($chars)));
    return ord(substr($chars, ($index << $c) % strlen($chars)));
}

function main() {
    $array = array(176, 52, 608, 855);
    foreach ($array as $value) {
        echo "$value: " . getCharacter($value) . "\n";
    }
}

main();

有人可以指出我解决这个问题的正确方向吗?

4

2 回答 2

2

我认为问题在于这个数字($index << c)> 3,586,129,92020 亿,并且不能用带符号的 32 位整数正确表示。由于您没有明确定义$valuephp 的数据类型,我认为算术最终取决于实现。

实际上,令人惊讶的是,事情完全正常——我认为,你将 32 位数字移动了一个大于 32 的值,这将导致未定义的行为。您可能需要重新考虑底层数学,特别是考虑代码的下溢/溢出行为。

作为一种潜在的解决方案,您可能会注意到您有有限数量的可能输入和相应的输出 - 您实际上可以创建一个直接查找表。我相信我这样做是正确的(使用您的代码的 C++ 版本并进行了一些修改) - 它没有导致 1:1 映射让我有点惊讶。查找字符串变为:

$lookupString = "6RQtrpp07TU4AP1IDKmjl8QD7WjitmwUAcjT3AT9MuAu3PUKJtIb5vS"

并且您的 php 代码可以简化为

$value = ord(substr($lookupString, $input % 55));

55的长度在哪里lookupString

有趣的观察:多个字符出现不止一次;从不使用其他字符。这意味着这不是一个非常“好”的编码方案(如果这是它想要的)。

作为参考,这是我用来确定查找字符串的代码:

#include <cstring>
#include <iostream>

static const char* valid_characters = "0123456789ABCDEFGHIJKLMOPQRSTUVWabcdefghijklmnopqrstuvw";

uint8_t GetCharacter(uint32_t value) {
    static const size_t valid_characters_l = strlen(valid_characters);
    uint8_t c = valid_characters[value % valid_characters_l];    
    return valid_characters[(value << c) % valid_characters_l];
}

int main() {
    uint32_t array[] = {176, 52, 608, 855};
    for (size_t i=0; i < 55; i++) {
        uint8_t c = GetCharacter(i + '0');
        std::cout << char(c);
    }
    std::cout << "\n";
    return 0;
}
于 2013-09-20T14:29:18.820 回答
1

您几乎肯定会遇到“问题”,因为您运行的是 32 位 PHP 或 Windows 上的 PHP(无论操作系统位数如何,它都不支持 64 位整数)。问题是您在移位操作中溢出了整数:

64 位 PHP:

PHP_INT_MAX: 9223372036854775807
C: 66, index: 176, strlen: 55, shift: 704, substr: mnopqrstuvw :: 176: 109
C: 117, index: 52, strlen: 55, shift: 468374361246531584, substr: 9ABCDEFGHIJKLMOPQRSTUVWabcdefghijklmnopqrstuvw :: 52: 57
C: 51, index: 608, strlen: 55, shift: 1369094286720630784, substr: hijklmnopqrstuvw :: 608: 104
C: 86, index: 855, strlen: 55, shift: 3586129920, substr: ABCDEFGHIJKLMOPQRSTUVWabcdefghijklmnopqrstuvw :: 855: 65

32 位 PHP:

PHP_INT_MAX: 2147483647
C: 66, index: 176, strlen: 55, shift: 704, substr: mnopqrstuvw :: 176: 109
C: 117, index: 52, strlen: 55, shift: 109051904, substr: rstuvw :: 52: 114
C: 51, index: 608, strlen: 55, shift: 318767104, substr: UVWabcdefghijklmnopqrstuvw :: 608: 85
C: 86, index: 855, strlen: 55, shift: -708837376, substr: hijklmnopqrstuvw :: 855: 104

不幸的是,PHP 根本不支持 32 位系统上的长整数(目前)。解决此问题的唯一方法是通过GMPBCMath等外部包。当今年晚些时候发布 PHP v7.0 时,这个问题应该会得到解决

于 2013-09-20T14:29:21.533 回答