2

I am trying to translate some data in one file based on the mappings in another file.

My encryption scheme looks like this:

0 => 129367
1 => 998023
2 => 971513
3 => 365366
4 => 247647
5 => 131747
6 => 86151
7 => 898342
8 => 591121
9 => 828966
a => 185363
b => 420016
c => 472728
d => 185475
e => 669058
f => 472944
g => 932915
h => 109171
i => 940561
j => 483250
k => 240419
l => 158223
m => 494635
n => 205943
o => 19961
p => 989725
q => 125777
r => 195604
s => 818227
t => 83779
u => 990539
v => 639213
w => 669743
x => 546240
y => 950155
z => 631005
A => 126161
B => 403898
C => 323151
D => 536228
E => 653494
F => 810236
G => 808261
H => 900915
I => 60916
J => 417663
K => 371534
L => 384244
M => 900004
N => 300998
O => 346538
P => 5044
Q => 558707
R => 404479
S => 183163
T => 505254
U => 497969
V => 197795
W => 953877
X => 394637
Y => 760236
Z => 211436
! => 601326
" => 15745
# => 428427
$ => 602548
% => 938126
& => 159405
' => 528113
( => 8021
) => 910309
* => 747795
+ => 232242
, => 731593
- => 808534
. => 429705
/ => 916854
: => 241543
; => 755104
< => 314595
= => 398161
> => 606925
? => 804662
@ => 713498
[ => 431477
\ => 80381
] => 36645
^ => 156790
_ => 34787
` => 107682
{ => 283663
| => 650856
} => 91921
~ => 752056
  => 494223
     => 521932

 => 816279
 => 301703
 => 17163
 => 867641
ą => 817209
ś => 594615
ż => 570356
ź => 913303
ł => 752084
ó => 838178
ę => 693877
ń => 564418
ć => 30975
Ż => 707466

My encrypted text looks like:

631005,323151,810236,60916,384244,346538,404479

I tried to replace characters in a loop using the below code:

$my_file = 'encrypted_text.txt';
$handle = fopen($my_file, 'r');
$data = fread($handle,filesize($my_file));
echo "\n".$data."\n";

$file = fopen("encryption_scheme.txt", "r");
$members = array();

while (!feof($file)) 
{
    $code = substr(fgets($file), strrpos(fgets($file), '=> ' )+1); 
    $code = str_replace('>', '', $code);
    $code = str_replace(' ', '', $code);
    $letter = substr(fgets($file), 0, 1);
    $data = str_replace(',', ' ', $data);
    $data = str_replace($code, $letter, $data); 
}
echo $data;

fclose($file);

But instead of getting decrypted text it's repeating the codes like

631005,323151,810236,60916,384244,346538,404479

631005 323151 810236 60916 384244 346538 404479

Update

while (!feof($file)) 
{
    $text = fgets($file);
    $code = substr($text, strrpos($text, '=> ' )+1); 
    $code = str_replace('>', '', $code);
    $code = str_replace(' ', '', $code);
    $letter = substr($text, 0, 1);
    $data = str_replace(',', ' ', $data);
    $data = str_replace($code, $letter, $data); 
}

I am expect a result of z C F I L O R.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Haniewas
  • 23
  • 6

2 Answers2

2

your first problem is, that fgets returns the text including the new line character (and depending on system carriage return).

your second problem is, that some of the "letters" are not "one character" in length (namely the unicode chars at the end).

your third problem is, that if the encryption scheme changes, you might experience unexpected results. let's say your encryption scheme is just this:

a => 1
b => 12
c => 2

the text 12 which clearly is b would turn into ac instead (because of order).

in my view, this leads to the conclusion, that mickmackusa's answer is the best one - specifically the second part of his answer, going through the ciphertext code by code.

my old solution

I would probably use

$data = str_replace(',', ' ', $data); // do this once, not every iteration

while($text = fgets($file) { // fgets returns false on eof
    list($letter, $code) = explode(' => ', $text, 2);
    $code = trim($code); // <-- removes whitespace and new lines and so on
    $data = str_replace($code, $letter, $data);
}

update:

// expand the text, if no "=>" is found:
while(...) {
    if(strpos($text, '=>') === FALSE) {
        $text = fgets($text);
    }
    if(!trim($text)) { // completely empty lines, even after expanding (EOF)
        break;
    }
    // rest as before
}
Jakumi
  • 8,043
  • 2
  • 15
  • 32
  • It's working but i'm getting something like that instead of only string https://pastebin.com/xahAdfdE – Haniewas Mar 12 '19 at 10:08
  • some of your lines don't match the "letter => code" pattern, apparently, there is a new line line ^^ added some update. – Jakumi Mar 12 '19 at 10:11
  • You mean something like that https://pastebin.com/LC7Ue4zY ? If yes, then it's not working https://pastebin.com/ZDmkGzv0 – Haniewas Mar 12 '19 at 18:50
  • yeah, I thought of something like that, I'm missing something. However, please have a look at mickmackusa's answer. – Jakumi Mar 13 '19 at 01:35
2

You could make iterated scans for a single translation value like this: (Demo)

$map = file_get_contents('encryption_scheme.txt');
$encrypted = file_get_contents('encrypted_text.txt');
echo preg_replace_callback(
         '/(\d+)(,)?/',
         fn($m) => preg_match(
             '/^(?:\R|.+)(?= => ' . $m[1] . '$)/um',
             $map,
             $found
         )
         ? $found[0] . (isset($m[2]) ? ' ' : '')
         : $m[0],
         $encrypted
     );

But depending on how many translations you need to do, performance may suffer.


To reduce the total regex calls for better performance, parse the whole encryption_scheme file with regex and store it as an array where the multi-digit numbers are keys and the returned character(s) are the values. Lookup arrays of this nature have very good performance and strtr() will beautifully, concisely translate your string for you. (Demo)

$lookup = preg_match_all(
              '/^(.+?) => (\d+)$/ums',
              file_get_contents('encryption_scheme.txt'),
              $m,
              PREG_SET_ORDER
          )
          ? array_column($m, 1, 2) + [',' => ' ']
          : [];

echo strtr(file_get_contents('encrypted_text.txt'), $lookup);

For obvious reasons, I strongly recommend this second snippet if making more than one translation/replacement.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136