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

时间:2015-02-17 09:55:47

标签: c# delphi base64 converter

美好的一天。可能有些人知道在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代码,它为soksifikator生成一个based64字符串。我尝试将其转换为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的原因。

1 个答案:

答案 0 :(得分:2)

<强>更新

我认为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单元(它是在XE7中添加的),您可以使用任何其他字节数组到您手头的base64编码器。

我不得不说我对你使用DIMime.MimeEncodeStringNoCRLF持怀疑态度,因为它将文本转换为base64。这将需要将文本隐式编码为二进制表示。而隐式编码至关重要,不应以这种方式隐藏起来。这就是我的意思,说你在文本和二进制之间混淆了。请记住,base64将二进制编码为文本。并将文本解码为二进制。但MimeEncodeStringNoCRLF将文本转换为文本,这意味着隐式文本编码。

我个人的规则是你绝不应该使用隐式文本编码进行这样的转换。如果从文本开始,首先使用明确选择的编码转换为二进制。然后使用base64编码该二进制文件。

最后,我想知道为什么代码会从文本转换为ASCII然后转换为UTF-16。这似乎是一个相当奇怪的决定。 C#代码真的在做你需要做的事吗?