Delphi 对C#(TStringStream + Base64)的不同结果

e5nszbig  于 2022-11-04  发布在  C#
关注(0)|答案(1)|浏览(219)

你好。也许有人知道一个真正的方法来获得同样的结果在 Delphi 和C#的下一行。

var
  aStrStream: TStringStream;
  aStr: string;
begin
  aStr := 'Test';
  aStrStream := TStringStream.Create('');
  aStrStream.Write(aStr, SizeOf(Length(aStr)));
  aStrStream.Position := 0;
  aStr := DIMime.MimeEncodeStringNoCRLF(aStrStream.DataString);
  aStrStream.Free;

  //Got yJFVAA==

end;

以及

Encoding dest = Encoding.ASCII;
Encoding src = Encoding.Unicode;
byte[] srcBytes = src.GetBytes("Test");
byte[] destBytes = Encoding.Convert(src, dest, srcBytes);
Console.WriteLine(Convert.ToBase64String(destBytes));

//Got VGVzdA==

更新1:
谢谢你的详细回答。但情况是下一个。我有一个src代码的一些程序生成一个based64字符串的soksifikator。我尝试将它转换为C#。这个程序有很多行这样:

aLen := Length(aObj.RuleProxyName); //aObj.RuleProxyName - string
aStrStream.Write(aLen, SizeOf(aLen));
if aLen > 0 then
  aStrStream.Write(aObj.RuleProxyName[1], aLen);

这就是为什么我不能使用aStrStream.WriteString的原因。

ej83mcc0

ej83mcc01#

更新

我之所以假设C#代码是目标代码,是因为 Delphi 代码显然是错误的。但是正如我所讨论的,C#代码也很奇怪。现在看来,原问题中的两个代码摘录都是假的。试图让C#与您发布的Delphi代码匹配是没有意义的,因为Delphi代码完全是错误的。
你真正需要做的是弄清楚真正的 Delphi 代码(而不是你的模拟代码)在做什么。
让我们来看看这个:

aLen := Length(aObj.RuleProxyName); //aObj.RuleProxyName - string
aStrStream.Write(aLen, SizeOf(aLen));
if aLen > 0 then
  aStrStream.Write(aObj.RuleProxyName[1], aLen);

它将一个4字节的小端整数字符串长度写入一个流,然后在其后跟随ANSI编码的文本。 Delphi 代码使用了一个TStringStream,但这是对该类的滥用。该类用于存储文本,但很明显它包含了二进制和ANSI编码文本的混合。这段代码实际上应该使用内存流或类似的东西。
在C#中,上面的摘录将翻译为:

string str = "Test";
MemoryStream stream = new MemoryStream();
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(str.Length);
writer.Write(Encoding.Default.GetBytes(str));

下一个问题是DIMime.MimeEncodeStringNoCRLF做什么。我们无法知道,因为它不是一个标准类。正如我在最初的答案中所说的,你应该警惕任何试图对文本而不是二进制输入进行base64编码的代码。
因此,为了取得进展,您需要尝试了解DIMime.MimeEncodeStringNoCRLF的实际功能。考虑到这似乎是一个Unicode之前的 Delphi ,它几乎肯定会将输入字符串视为字节数组并对其进行编码,在这种情况下,您可以使用Convert.ToBase64String(stream.ToArray())来完成上面的摘录。

原始答案

Delphi 代码有点混乱。例如,SizeOf(Length(aStr))没有任何意义。但无论如何,你都不应该使用Write,而应该使用WriteString
但即便如此,你还是把文本和二进制搞混了。你费了很大的劲把文本转换成ASCII,然后执行aStrStream.DataString,简单地把它转换回UTF-16。然后你把它馈送给MimeEncodeStringNoCRLF。你还不如写:MimeEncodeStringNoCRLF('Test'),我想它也会以同样的方式失败。
我会重新开始,完全按照C#版本的方式编写代码。我会避免使用TStringStream,并利用 Delphi 的TEncoding类在使用上尽可能接近.net Encoding类的这一事实。因此,您确实可以对这段代码进行字面翻译。

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.NetEncoding;

procedure Main;
var
  dest, src: TEncoding;
  srcBytes, destBytes: TBytes;
begin
  dest := TEncoding.ASCII;
  src := TEncoding.Unicode;
  srcBytes := src.GetBytes('Test');
  destBytes := TEncoding.Convert(src, dest, srcBytes);
  Writeln(TNetEncoding.Base64.EncodeBytesToString(destBytes));
end;

begin
  Main;
  Readln;
end.

输出

VGVzdA==

如果您没有可用的NetEncoding单元(它是在XE 7中添加的),您可以使用手头上的任何其他base64编码器字节数组。
我不得不说,我对您使用DIMime.MimeEncodeStringNoCRLF的做法相当怀疑,因为它会将文本转换为base64。这需要将文本隐式编码为二进制表示。而这种隐式编码至关重要,不应该以这种方式隐藏起来。这就是我说您混淆了文本和二进制的意思。请记住,base64将二进制编码为文本。并将文本解码为二进制。但MimeEncodeStringNoCRLF将文本转换为文本,这意味着隐式文本编码。
我个人的规则是,你永远不应该使用隐式文本编码进行这样的转换。如果你从文本开始,首先使用显式选择的编码转换为二进制,然后使用base64编码该二进制。
最后,我想知道为什么代码从文本转换为ASCII,然后转换为UTF-16。这似乎是一个相当奇怪的决定。C#代码真的在做你需要它做的事情吗?

相关问题