簡體   English   中英

在Pascal中編寫等效代碼以編寫C#

[英]Writing equivalent code in Pascal to code C#

我試圖將此代碼在pascal中轉換為c#。 沒有編譯錯誤,該方法適用於短字符串(4個或5個字符)。 所有方法和函數都是等效的,但是我的代碼拋出此異常:

System.OverflowException:一個字符的值太大或太小。

這是StackTrace

在ConsoleApplication3.Program.Encrypt(布爾加密,字符串字,Int32 startKey,Int32 multKey,Int32 addKey)的System.Convert.ToChar(Int32值)中位於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;
    }
}
}

非常感謝。

解決此問題的第一步是認識到加密對字節數組進行操作,然后返回字節數組。 這里沒有放置字符串的地方。 文本編碼只是掩蓋了加密算法。 如果需要加密字符串,則必須首先使用一些定義良好的文本編碼將其轉換為字節數組。

因此,讓我們使用該策略重新編寫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. 這兩個版本的代碼都可以忠實地解密加密的數組。

您的代碼導致錯誤,因為已檢查算法是否溢出。 您的算術溢出了int數據類型,並且由於它在檢查的上下文中執行,因此該溢出導致錯誤。 該文檔有詳細信息: http : //msdn.microsoft.com/zh-cn/library/khy08726.aspx

unchecked關鍵字可抑制該錯誤並允許溢出發生。 Delphi代碼使用{$Q-}來達到相同的效果。

Pascal的AnsiChar是一個八位值。 可能允許任何下溢或上溢。

您可能想編寫C#代碼以使用字節數組而不是字符和字符串,因為C#字符為16位。

如果代碼取決於上溢/下溢的工作原理,則使用字節將對其進行修復。

正如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包含一些值大於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來自此處: 如何在C#中獲得字符串的一致字節表示,而無需手動指定編碼?

Delphi代碼顯式地使用{$Q-}關閉溢出檢查。 如果您確保在Delphi中啟用了溢出,那么我也希望在那里也會出現溢出錯誤。

這種事情在編碼例程中很常見,在這種編碼例程中,應以期望溢出但與最終結果無關的方式編寫它們,因此將其忽略。

C#提供了unchecked選項來顯式禁用溢出檢查。

但是,如果您有任何其他細微的錯誤,則可能不會產生正確的結果。 因此,請確保您仍然對其進行徹底的測試 (有關轉換中的語義差異,請參見Sergey的回答 ,這幾乎肯定會破壞實現。)

word[i] ^ (startKey >> 8)

的計算結果為81867,該值太高而無法轉換為char。

當傳遞的值對於char而言太大時, Convert.ToChar()會引發異常,而在Pascal中,強制轉換為AnsiChar只會截斷該值。 另請注意,C#字符為16位,而在Pascal中為8位。

您可避免異常並致電之前使你的代碼工作像帕斯卡爾代碼通過屏蔽掉頂位Convert.ToChar兩個是追加到線encryptedWord ,就像這樣:

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

嘗試使用

Char.Parse()

Convert.ToChar()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM