从VB6字符串数据转换为.NET字节数组

时间:2009-05-07 08:06:20

标签: c# .net vb.net vb6

我正在编写一个C#应用程序,它从VB6代码生成的SQL数据库中读取数据。数据是单打阵列。我正在尝试将它们转换为float []

下面是在数据库中写入数据的VB6代码(不能更改此代码):

  Set fso = New FileSystemObject
  strFilePath = "c:\temp\temp.tmp"

  ' Output the data to a temporary file
  intFileNr = FreeFile
  Open strFilePath For Binary Access Write As #intFileNr
  Put #intFileNr, , GetSize(Data, 1)
  Put #intFileNr, , GetSize(Data, 2)
  Put #intFileNr, , Data
  Close #intFileNr

  ' Read the data back AS STRING
  Open strFilePath For Binary Access Read As #intFileNr
  strData = String$(LOF(intFileNr), 32)
  Get #intFileNr, 1, strData
  Close #intFileNr

  Call Field.AppendChunk(strData)

如您所见,数据被放入一个临时文件中,然后作为VB6 String读回并在数据库中写入(dbLongBinary类型的行)

我尝试了以下内容:

执行BlockCopy

byte[] source = databaseValue as byte[];
float [,] destination = new float[BitConverter.ToInt32(source, 0), BitConverter.ToInt32(source, 4)];
Buffer.BlockCopy(source, 8, destination, 0, 50 * 99 * 4);

这里的问题是VB6二进制到字符串转换。 VB6字符串字符宽2个字节,我不知道如何将其转换回我可以处理的二进制格式。

下面是VB6代码生成的临时文件的转储: alt text http://robbertdam.nl/share/dump%20of%20text%20file%20generated%20by%20VB6.png

这是我从数据库中读取数据的转储(= VB6字符串): alt text http://robbertdam.nl/share/dump%20of%20database%20field.png

6 个答案:

答案 0 :(得分:3)

我看到的一种可能方式是:

  1. 以System.Char []的形式读取数据,这就像VB BSTR一样是Unicode。
  2. 通过Encoding.ASCII.GetBytes()将其转换为ASCII字节数组。实际上,这将删除所有交错的0。
  3. 将此ASCII字节数组复制到最终的float数组。
  4. 这样的事情:

    char[] destinationAsChars = new char[BitConverter.ToInt32(source, 0)* BitConverter.ToInt32(source, 4)];
    byte[] asciiBytes = Encoding.ASCII.GetBytes(destinationAsChars);
    float[] destination = new float[notSureHowLarge];
    Buffer.BlockCopy(asciiBytes, 0, destination, 0, asciiBytes.Length);
    

    现在目的地应包含原始花车。 CAVEAT:我不确定VB6 Singles的内部格式是否与System.Float的内部格式二进制兼容。如果没有,所有的赌注都会被取消。

答案 1 :(得分:2)

这是我从上面的答案中得出的解决方案。

以unicode char[]的形式读取文件,然后重新编码为我的默认系统编码生成的可读文件。

internal void FixBytes()
{
    //Convert the bytes from VB6 style BSTR to standard byte[].

    char[] destinationAsChars = 
    System.Text.Encoding.Unicode.GetString(File).ToCharArray();

    byte[] asciiBytes =  Encoding.Default.GetBytes(destinationAsChars);
    byte[] newFile = new byte[asciiBytes.Length];
    Buffer.BlockCopy(asciiBytes,0, newFile, 0, asciiBytes.Length);
    File = newFile;
}

答案 2 :(得分:1)

正如您可能知道的那样,VB6端的编码非常糟糕。它试图做的是将Single数据(与C#中的float相同)转换为String。但是,虽然有更好的方法可以做到这一点,但开始时这是一个非常糟糕的主意。

主要原因是将二进制数据读入VB6 BSTR将使用当前代码页将数据从8位字节转换为16位字符。因此,这可以在DB中产生不同的结果,具体取决于它运行的区域设置。(!)

因此,当您从数据库中读取它时,除非您指定编写时使用的相同代码页,否则您将获得不同的浮点数,甚至可能是无效的浮点数。

以二进制(单个)和DB(字符串)形式(十六进制)查看数据示例将有助于验证这是正在发生的事情。

后来的帖子:

  

实际上这不是“糟糕的”VB6代码。

因为它将二进制数据带入字符串域,这违反了现代VB编码的主要规则。这就是Byte数据类型存在的原因。如果忽略这一点,当您创建的数据库跨越语言环境边界时,您可能会收到无法解密的数据。

  

他正在做的是存储阵列   以紧凑的二进制格式保存   它作为数据库中的“块”。   有很多有效的理由要做   此

当然他有理由想要这个(尽管你对'compact'的定义与传统的不同)。结局很好:选择的手段不是。

到OP:

您可能无法改变您作为输入数据所提供的内容,因此上述主要是学术性的。如果仍有时间更改用于创建blob的方法,那么让我们建议不涉及字符串的方法。

在应用任何提供的解决方案时,尽量避免使用字符串,如果不能,请使用与创建它们的代码页匹配的特定代码页对其进行解码。

答案 3 :(得分:0)

您能澄清一下文件的内容(例如)吗?要么是二进制(可能是十六进制)还是字符?如果数据是VB6字符串,那么你必须使用float.Parse()来读取它。 .NET字符串每个字符也是2个字节,但是从文件加载时,您可以使用Encoding来控制它。

答案 4 :(得分:0)

其实那不是“坏”的VB6代码。他正在做的是以紧凑的二进制格式存储数组并将其作为“块”保存到数据库中。有很多有效的理由这样做。

VB6代码将其保存到磁盘并将其读回的原因是因为VB6不支持仅在内存中读取和写入文件的本机支持。如果你想创建一大块二进制数据并把它放在像数据库字段这样的其他地方,这就是常用的算法。

这不是.NET中处理此问题的问题。我的代码是在VB.NET中,因此您必须将其转换为C#。

修改以处理字节和unicode问题。

Public Function DataArrayFromDatabase(ByVal dbData As byte()) As Single(,)
    Dim bData(Ubound(dbData)/2) As Byte
    Dim I As Long
    Dim J As Long

    J=0
    For I = 1 To Ubound(dbData) step 2
        bData(J) = dbData(I)
        J=1
    Next I

    Dim sM As New IO.MemoryStream(bData)
    Dim bR As IO.BinaryReader = New IO.BinaryReader(sM)
    Dim Dim1 As Integer = bR.ReadInt32
    Dim Dim2 As Integer = bR.ReadInt32
    Dim newData(Dim1, Dim2) As Single

    For I = 0 To Dim2
        For J = 0 To Dim1
            newData(J, I) = bR.ReadSingle
        Next
    Next

    bR.Close()
    sM.Close()
    Return newData
End Function

关键技巧是读入数据,就像你在VB6中一样。我们有能力在.NET中使用MemoryStreams,所以这很容易。

首先,我们跳过每隔一个字节以消除Unicode填充。

然后我们从字节数组创建一个内存流。然后使用MemoryStream初始化BinaryReader。

我们在数组的第一维中读取VB6 Long或.NET Int32 我们在数组的第二维读取VB6 Long或.NET Int32

读取循环的构造方式与数组的维度相反。 Dim2是外环,Dim1是内环。原因在于这就是VB6以二进制格式存储数组的方式。

返回newData并且您已成功恢复在VB6中创建的原始数组!

现在你可以尝试使用一些数学技巧。二维是4个字节/字符,每个数组元素是4个字节/字符。但是对于长期可维护性,我发现使用内存流的字节操作更加明确。它需要更多的代码,但是从现在起5年后重新审视它会更加清晰。

答案 5 :(得分:0)

  

首先我们跳过每个其他字节   消除Unicode填充。

嗯...如果这是一个有效的策略,那么DB字符串转储中的每个其他列都只包含零。但是快速扫描第一个显示情况并非如此。事实上,这些列中有很多非零字节。我们可以放弃它们吗?

这表明使用字符串导致的Unicode转换不仅仅是添加“填充”,而是更改数据的字符。你所谓的填充是一个巧合,即ASCII范围(00-7F二进制)被映射到相同的Unicode范围。但二进制80-FF并非如此。

查看第一个存储值,其原始字节值为94 9A 27 3A。当转换为Unicode时,这些不会变为94 00 97 00 27 00 3A 00.它们变为1D 20 61 01 27 00 3A 00.

丢弃所有其他字节会给你1D 61 27 3A - 而不是原来的94 9A 27 3A。