Цезарь Шифр ​​работает не так, как ожидалось

Я играю с Цезарь Шифр, и это не похоже на работу.

Это мой код:

class CaesarCipher {
const CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
protected $encrypt_sequence = array();
protected $decrypt_sequence = array();

public function __construct($seed = 1) {
$total_chars = strlen(self::CHARS);
$seed = $seed % $total_chars;
for ($i = 0; $i < $total_chars; $i++) {
$src = substr(self::CHARS, $i, 1);
$dst = substr(self::CHARS, ($i + $seed) % $total_chars, 1);
$this->encrypt_sequence[$src] = $dst;
$this->decrypt_sequence[$dst] = $src;
}
$this->encrypt_sequence[' '] = ' ';
$this->decrypt_sequence[' '] = ' ';
}

public function encrypt($value) {
$value = strtoupper($value);
return str_replace($this->encrypt_sequence, $this->decrypt_sequence, $value);
}

public function decrypt($value) {
$value = strtoupper($value);
return str_replace($this->decrypt_sequence, $this->encrypt_sequence, $value);
}

public function getEncryptSequence() {
return $this->encrypt_sequence;
}

public function getDecryptSequence() {
return $this->decrypt_sequence;
}
}

Запустить его:

$seed = mt_rand(1, 35);
$cipher = new CaesarCipher($seed);
$source = 'THIS IS JUST A TEST WITH 123 NUMBERS';
$encrypted = $cipher->encrypt($source);
$decrypted = $cipher->decrypt($encrypted);

И это мой вывод:

CAESAR CIPHER (seed=16)

Source:    THIS IS JUST A TEST WITH 123 NUMBERS
Encrypted: X12W 2W 3YWX U XYWX 02X1 567 7Y6VYVW   ENCRYPTED :)
Decrypted: DHIC IC JECD A DECD GIDH LMN NEMBEBC   DOES NOT MATCH SOURCE :(Encryption: A>Q B>R C>S D>T E>U F>V G>W H>X I>Y J>Z K>0 L>1 M>2 N>3 O>4 P>5 Q>6 R>7 S>8 T>9 U>A V>B W>C X>D Y>E Z>F 0>G 1>H 2>I 3>J 4>K 5>L 6>M 7>N 8>O 9>P
Decryption: Q>A R>B S>C T>D U>E V>F W>G X>H Y>I Z>J 0>K 1>L 2>M 3>N 4>O 5>P 6>Q 7>R 8>S 9>T A>U B>V C>W D>X E>Y F>Z G>0 H>1 I>2 J>3 K>4 L>5 M>6 N>7 O>8 P>9

Кто-нибудь может дать мне подсказку, почему это не работает?

4

Решение

Наиболее очевидной проблемой будет этот раздел из руководства по str_replace:

предосторожность

Заказ на замену получил

Поскольку str_replace () заменяет слева направо, это может заменить
ранее вставленное значение при выполнении нескольких замен. Смотрите также
примеры в этом документе.

Вы заменяете некоторые символы несколько раз, и это приводит к полученному результату.

Это относится как к шифрованию, так и к дешифрованию, поэтому вы не можете использовать str_replace() с массивами, чтобы делать то, что вы хотите сделать.

Возможным решением было бы зашифровать (en decrypt …) ваш символ за строкой, чтобы каждый символ передавался только один раз.

3

Другие решения

Понял!

public function encrypt($value) {
$value = strtoupper($value);
return strtr($value, $this->encrypt_sequence);
}

public function decrypt($value) {
$value = strtoupper($value);
return strtr($value, $this->decrypt_sequence);
}

Правильно расшифрует и расшифрует значение. Спасибо jeroen и GoogleHireMe 🙂

2

Это довольно неэффективный способ сделать это, каждый str_replace сканирует всю строку для каждой пары. Скорее, почему бы не отсканировать входную строку и вычислить замену во время шифрования / дешифрования. Вы просили подсказки, а не решения … но вот пример encrypt func (не нужно было бы конструировать, хотя могло бы хранить конструкционное семя вместо того, чтобы передавать его как параметр):

public function encrypt($value,$seed) {
$n=strlen($value);
$nchars=strlen(self::CHARS);
for($i=0;$i<$n;$i++) {
//This saves you doing the space-space replace, and allows
//  any chars not in your ciper to remain unmolested
$pos=strpos(self::CHARS,$value[$i]);
if ($pos>=0) $value[$i]=self::CHARS[($pos+$seed)%$nchars];
}
}
0