将Pascal中的等效代码写入代码C#

时间:2014-05-22 14:21:04

标签: c# .net delphi

我正在尝试将pascal中的此代码转换为c#。没有编译错误,并且该方法适用于短字符串(4或5个字符)。所有方法和函数都是等价的,但我的代码抛出了这个例外:

  

System.OverflowException:对于一个字符,值太大或太小   

这是StackTrace

  System.Convert.ToChar中的

(Int32值)    在ConsoleApplication3.Program.Encrypt中(布尔加密,字符串字,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;

这是我的等效C#代码:

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 个答案:

答案 0 :(得分: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;

在C#中复制它很容易。我们只需要确保以与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. C#代码的行为与Delphi代码完全相同。
  3. 两个版本的代码都可以忠实地解密加密数组。
  4. 您的代码导致错误,因为已检查算术是否存在溢出。您的算法溢出int数据类型,并且因为它在已检查的上下文中执行,该溢出会导致错误。文档包含详细信息:http://msdn.microsoft.com/en-us/library/khy08726.aspx

    unchecked关键字会抑制该错误并允许发生溢出。 Delphi代码使用{$Q-}来实现相同的效果。

答案 1 :(得分:1)

Pascal的AnsiChar是一个八位值。可能允许任何下溢或溢出。

您可能希望编写C#代码以使用字节数组而不是字符和字符串,因为C#字符是16位。

如果代码依赖于溢出/下溢工作,使用字节将修复它。

答案 2 :(得分:1)

正如David Crowell正确指出的那样,AnsiChar是1字节值。所以你需要改变这个:

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

这样的事情:

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

此外,您在C#中的代码效率很低。我会这样做:

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;
    }    
}

<强>更新 David Heffernan在他的两个评论中都是正确的:

  

叹息。另一个加密问题是对字符串而不是字节数组进行操作。

  

此代码的基本问题是它尝试将字节数组填充为UTF-16编码字符串。我很欣赏问题中的代码也是如此,但这确实是问题的关键。

如果word包含一些值为&gt;的char 255,那么C#代码产生的结果将与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来自此处:How do I get a consistent byte representation of strings in C# without manually specifying an encoding?

答案 3 :(得分:1)

Delphi代码明确地使用{$Q-}关闭溢出检查。如果你确保在Delphi中启用溢出,我希望你也会遇到溢出错误。

这种情况在编码例程中很常见,它们的编写方式是预期过度逼近但与最终结果无关,因此被忽略。

C#提供unchecked选项以显式禁用溢出检查。

但是,如果您有任何其他细微错误,则可能无法产生正确的结果。 因此,请确保您仍然彻底测试。 (请参阅Sergey's answer了解转换中的语义差异,这几乎肯定会破坏实现。)

答案 4 :(得分:0)

word[i] ^ (startKey >> 8)

将评估为81867,这个值太高而无法转换为char。

答案 5 :(得分:0)

当您传递的值对于char而言太大时,

Convert.ToChar()会抛出异常,而在Pascal中,转换为AnsiChar只会截断该值。还要注意,C#字符是16位,而在Pascal中它是8位。

您可以避免异常并使代码像Pascal代码一样工作,方法是在追加Convert.ToChar的两行中调用encryptedWord之前屏蔽顶部位,如下所示:

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

答案 6 :(得分:-3)

尝试使用

Char.Parse()

Convert.ToChar()