在 Pascal 中编写与代码 C# 等效的代码
本文关键字:代码 Pascal | 更新日期: 2023-09-27 18:04:49
我正在尝试将帕斯卡中的这段代码转换为 c#。没有编译错误,该方法适用于短字符串(4 或 5 个字符(。所有方法和函数都是等效的,但我的代码抛出此异常:
系统溢出异常:值对于字符来说太大或太小 .
这是StackTrace
:
in System.Convert.ToChar(Int32 值( in ConsoleApplication3.Program.Encrypt(Boolean encrypt, String word, Int32 startKey, Int32 multKey, Int32 addKey( on c:''Users''TRS''Documents''Visual Studio 2013''Projects''ConsoleApplication3''ConsoleApplication3''Program.cs:line 29 .
这是帕斯卡代码:
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;
}
我粗略的测试表明:
- 字节数组德尔菲代码
- 的行为与问题中的德尔菲代码相同。
- C# 代码的行为与 Delphi 代码相同。
- 两个版本的代码代码都可以忠实地解密加密数组。
您的代码导致错误,因为检查了算术是否溢出。您的算术溢出了int
数据类型,并且由于它在检查的上下文中执行,因此溢出会导致错误。该文档包含详细信息:http://msdn.microsoft.com/en-us/library/khy08726.aspx
unchecked
关键字可抑制该错误并允许发生溢出。 德尔菲代码使用{$Q-}
来达到相同的效果。
Pascal 的 AnsiChar 是一个八位值。 可能允许任何下溢或溢出。
你可能希望编写 C# 代码以使用字节数组而不是字符和字符串,因为 C# 字符是 16 位。
如果代码依赖于溢出/下溢工作,使用字节将修复它。
Delphi 代码使用 {$Q-}
显式关闭溢出检查。如果您确保在 Delphi 中启用了溢出,我希望您在那里也会遇到溢出错误。
这种事情在编码例程中很常见,其中它们的编写方式是预期的溢出流,但与最终结果无关,因此被忽略。
C# 提供了未选中的选项来显式禁用溢出检查。
但是,如果您有任何其他细微错误,这可能不会产生正确的结果。因此,请确保您仍然对其进行彻底测试。(请参阅谢尔盖的答案,了解转换中的语义差异,这几乎肯定会破坏实现。
正如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# 中字符串的一致字节表示形式?
word[i] ^ (startKey >> 8)
将计算结果为 81867,该值太高,无法转换为字符。
当您传递的值对于字符来说太大时,Convert.ToChar()
会引发异常,而在 Pascal 中,转换为 AnsiChar
只会截断该值。另请注意,C# 字符是 16 位,而在 Pascal 中是 8 位。
您可以避免异常,并通过在附加到encryptedWord
的两行中调用Convert.ToChar
之前屏蔽顶部位来使您的代码像 Pascal 代码一样工作,如下所示:
encryptedWord += Convert.ToChar((word[i] ^ (startKey >> 8)) & 0xff);
尝试使用
Char.Parse()
而
Convert.ToChar()