Запись эквивалентного кода на Паскале в код C #

1

Я пытаюсь преобразовать этот код в pascal в С#. Нет ошибок компиляции, и метод отлично работает с короткой строкой (4 или 5 символов). Все методы и функции являются эквивалентами, но мой код генерирует это исключение:

System.OverflowException: значение было слишком большим или слишком маленьким для символа.

Это StackTrace:

в System.Convert.ToChar (значение Int32) в ConsoleApplication3.Program.Encrypt(Boolean encrypt, String word, Int32 startKey, Int32 multKey, Int32 addKey) на c:\Users\TRS\Documents\Visual Studio 2013\Projects\ConsoleApplication3\ConsoleApplication3\Program.cs: строка 29.

Это код Pascal:

function TGenericsF.Encrypter(Encrypt: WordBool; Source: AnsiString;
  StartKey, MultKey, AddKey: Integer): AnsiString;
 {$R-} {$Q-}
 var Counter: LongInt;
   S: AnsiString;
   Ret: AnsiString;
   begin
     S := Source;
     Ret := '';
     for Counter := 1 to Length(S) do
     begin
       if Encrypt then
        begin
         Ret := Ret + AnsiChar(Ord(S[Counter]) xor (StartKey shr 8));
         StartKey := (Ord(Ret[Counter]) + StartKey) * MultKey + AddKey;
        end
     else
       begin
         Ret := Ret + AnsiChar(Ord(S[Counter]) xor (StartKey shr 8));
         StartKey := (Ord(S[Counter]) + StartKey) * MultKey + AddKey;
       end;
    end;
  Result := Ret;
end;

И это мой эквивалентный код С#:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication3
{
   class Program
   {
    static void Main(string[] args)
    {
        string username = Encrypt(true, "Administrator", 8, 12, 16);
        Console.WriteLine(username);
        Console.ReadKey();
    }

    public static string Encrypt(bool encrypt, string word, int startKey, int multKey, int addKey)
    {
         string encryptedWord = string.Empty;

         for (int i = 0; i < word.Length; i++)
         {
             if(encrypt)
             {

                 encryptedWord += Convert.ToChar(word[i] ^ (startKey >> 8));
                 startKey = (((int)encryptedWord[i]) + startKey) * multKey + addKey;

             }
             else
             {
                 encryptedWord += Convert.ToChar(word[i] ^ (startKey >> 8));
                 startKey = (((int)word[i]) + startKey) * multKey + addKey;
             }
         }

        return encryptedWord;
    }
}
}

Спасибо.

Теги:

7 ответов

7
Лучший ответ

Первым шагом на пути решения этой проблемы является распознавание того, что шифрование работает с байтовыми массивами и возвращает массивы байтов. Здесь нет места для струн. Текстовые кодировки просто затушевывают арифметику шифрования. Если вам нужно зашифровать строку, вы должны сначала преобразовать ее в массив байтов, используя определенную текстовую кодировку.

Итак, позвольте повторно написать код Delphi с этой стратегией. Это выглядит так:

{$R-} {$Q-}
function EncryptDecrypt(Encrypt: Boolean; const Source: TBytes;
  StartKey, MultKey, AddKey: Integer): TBytes;
var
  i: Integer;
begin
  SetLength(Result, Length(Source));
  for i := low(Source) to high(Source) do
  begin
    if Encrypt then
    begin
      Result[i] := Source[i] xor (StartKey shr 8);
      StartKey := (Result[i] + StartKey) * MultKey + AddKey;
    end
    else
    begin
      Result[i] := Source[i] xor (StartKey shr 8);
      StartKey := (Source[i] + StartKey) * MultKey + AddKey;
    end;
  end;
end;

Реплицировать это на С# достаточно просто. Нам просто нужно убедиться, что мы допускаем переполнение так же, как и код Delphi. Это предполагает использование unchecked. Код работает следующим образом:

public static byte[] EncryptDecrypt(bool Encrypt, byte[] Source,
    int StartKey, int MultKey, int AddKey)
{
    byte[] Dest = new byte[Source.Length];
    for (int i = 0; i < Source.Length; i++)
    {
        if (Encrypt)
        {
            unchecked
            {
                Dest[i] = (byte) (Source[i] ^ (StartKey >> 8));
                StartKey = (Dest[i] + StartKey) * MultKey + AddKey;
            }
        }
        else
        {
            unchecked
            {
                Dest[i] = (byte) (Source[i] ^ (StartKey >> 8));
                StartKey = (Source[i] + StartKey) * MultKey + AddKey;
            }
        }
    }
    return Dest;
}

Мои несколько беглых тестов показывают, что:

  1. Байт-код Delphi ведет себя одинаково с кодом Delphi в вопросе.
  2. Код С# ведет себя одинаково с кодом Delphi.
  3. Обе версии кода кода могут точно расшифровать зашифрованные массивы.

Ваш код приводил к ошибкам, потому что арифметика была проверена для переполнения. Ваша арифметика переполнила тип данных int и, поскольку она выполняется в проверенном контексте, это переполнение приводит к ошибке. Документация содержит подробную информацию: http://msdn.microsoft.com/en-us/library/khy08726.aspx

unchecked ключевое слово подавляет эту ошибку и позволяет переполнение. Для достижения такого же эффекта в коде Delphi используется {$Q-}.

1

Как правильно указал Дэвид Кроуэлл, AnsiChar - 1 байт. Поэтому вам нужно изменить это:

encryptedWord += Convert.ToChar(word[i] ^ (startKey >> 8));

в нечто подобное:

encryptedWord += (char)((byte)(word[i] ^ (startKey >> 8)) & 0xFF);

Кроме того, ваш код на С# довольно неэффективен. Я бы сделал это так:

public static string Encrypt(bool encrypt, string word, int startKey, int multKey, int addKey)
{
    try
    {
        StringBuilder encryptedWord = new StringBuilder(word.Length);
        for (int i = 0; i < word.Length; i++)
        {
            encryptedWord.Append((char)((byte)(word[i] ^ (startKey >> 8)) & 0xFF));
            if(encrypt)
                startKey = (((int)encryptedWord[i]) + startKey) * multKey + addKey;
            else
                startKey = (((int)word[i]) + startKey) * multKey + addKey;
        }
        return encryptedWord.ToString();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        throw;
    }    
}

Обновление Дэвид Хеффернан прав в своих двух комментариях:

Вздох. Еще один вопрос шифрования, который работает с строками, а не с байтовыми массивами.

а также

Основная проблема с этим кодом заключается в том, что он пытается массировать байтовые массивы в кодированные строки UTF-16. Я ценю, что код в вопросе делает то же самое, но это действительно суть дела.

Если word содержит некоторый символ со значением> 255, то результат, созданный кодом С#, будет отличаться от того, что будет производить код Pascal. Чтобы решить эту проблему, вам необходимо работать с байтовыми массивами. Что-то вроде этого должно преуспеть:

private static byte[] StringToBytes(string str)
{
    byte[] bytes = new byte[str.Length * sizeof(char)];
    System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
    return bytes;
}

private static string BytesToString(byte[] bytes)
{
    char[] chars = new char[bytes.Length / sizeof(char)];
    System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
    return new string(chars);
}

public static string Encrypt(bool encrypt, string word, int startKey, int multKey, int addKey)
{
    try
    {
        byte[] source = StringToBytes(word);
        byte[] result = new byte[source.Length];
        for (int i = 0; i < source.Length; ++i)
        {
            result[i] = (byte)((word[i] ^ (startKey >> 8)) & 0xFF);
            if (encrypt)
                startKey = ((result[i]) + startKey) * multKey + addKey;
            else
                startKey = ((word[i]) + startKey) * multKey + addKey;
        }
        return BytesToString(result);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        throw;
    }
}

StringToBytes и BytesToString отсюда: Как получить согласованное байтовое представление строк в С# без ручного указания кодировки?

  • 1
    Исправление: AnsiChar составляет 8 бит = 1 байт .
  • 0
    Блин да :) Не 8 байт конечно: D Спасибо
Показать ещё 8 комментариев
1

Pascal AnsiChar - это восьмибитовое значение. Возможно, разрешено любое недополнение или переполнение.

Вероятно, вы захотите написать код С# для использования байтовых массивов вместо символов и строк, поскольку символы С# равны 16 бит.

Если код зависит от работы переполнения/недоработки, использование байтов исправит его.

  • 0
    Очень хорошо, спасибо большое.
1

Код Delphi явно отключает проверку переполнения с помощью {$Q-}. Если вы убедитесь, что переполнение включено в Delphi, я ожидаю, что вы также получите ошибку переполнения.

Подобные вещи распространены с подпрограммами кодирования, где они написаны таким образом, что ожидаются наложения, но не имеют отношения к окончательному результату и поэтому игнорируются.

С# предоставляет непроверенную опцию, позволяющую явно отключить проверку переполнения.

Однако, если у вас есть другие тонкие ошибки, это может привести к неправильным результатам. Поэтому убедитесь, что вы все еще тщательно проверяете его. (См. Ответ Сергея для семантического несоответствия в вашем преобразовании, которое почти наверняка нарушит реализацию).

  • 1
    Даже отключение проверки переполнения не решит проблему, поскольку AnsiChar имеет значение в один байт.
  • 0
    @ Сергей Да, как я уже говорил, могут быть и другие тонкие ошибки, и требуется тщательное тестирование. (У меня нет желания пытаться понять, является ли алгоритм / реализация разумным.) Вы хорошо поработали над устранением семантического недостатка в его преобразовании. Но если значение переполняется в 16 битах, оно также переполняется в 8.
0

Convert.ToChar() выдает исключение, когда значение, которое вы передаете, слишком велико для символа, тогда как в Pascal литье в AnsiChar просто AnsiChar значение. Также обратите внимание, что символ С# - 16 бит, тогда как в Pascal - 8 бит.

Вы можете избежать исключения и заставить свой код работать как код Pascal, маскируя верхние биты перед вызовом Convert.ToChar в обеих строках, которые добавляются в encryptedWord, например:

encryptedWord += Convert.ToChar((word[i] ^ (startKey >> 8)) & 0xff);
0
word[i] ^ (startKey >> 8)

будет оцениваться до 81867, что слишком велико для преобразования в char.

-3

попробуй использовать

Char.Parse()

в то время как

Convert.ToChar()
  • 0
    почему понижение ????
  • 4
    Я не отрицал, но вы не предоставили абсолютно никаких объяснений, и похоже, что вы просто стреляете в темноте.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню