Преобразование из строковых данных VB6 в байтовый массив .NET - PullRequest
1 голос
/ 07 мая 2009

Я пишу приложение на C #, которое считывает данные из базы данных SQL, сгенерированной кодом VB6. Данные представляют собой массив синглов. Я пытаюсь преобразовать их в число с плавающей точкой []

Ниже приведен код 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 и записываются в базу данных (строка типа 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. Строка char VB6 имеет ширину 2 байта, и я не знаю, как преобразовать ее обратно в двоичный формат, который я могу обработать.

Ниже приведен дамп временного файла, который генерирует код VB6: альтернативный текст http://robbertdam.nl/share/dump%20of%20text%20file%20generated%20by%20VB6.png

А вот дамп данных, которые я прочитал из базы данных в (= строка VB6): альтернативный текст http://robbertdam.nl/share/dump%20of%20database%20field.png

Ответы [ 6 ]

3 голосов
/ 07 мая 2009

Один из возможных способов увидеть это:

  1. Считать данные обратно как System.Char [], который является Unicode так же, как VB BSTR.
  2. Преобразуйте его в байтовый массив ASCII с помощью Encoding.ASCII.GetBytes (). Это эффективно удаляет все чередующиеся нули.
  3. Скопируйте этот байтовый массив ASCII в ваш последний массив с плавающей точкой.

Примерно так:

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);

Теперь пункт назначения должен содержать оригинальные поплавки. ПРЕДУПРЕЖДЕНИЕ: я не уверен, что внутренний формат VB6 Singles совместим двоично с внутренним форматом System.Float. Если нет, все ставки выключены.

2 голосов
/ 24 апреля 2015

Это решение, которое я получил из ответа выше.

Чтение файла в формате 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;
}
1 голос
/ 24 апреля 2009

Как вы, наверное, знаете, это очень плохое кодирование на конце VB6. То, что он пытается сделать - это привести данные Single - которые совпадают с плавающей точкой в ​​C # - как String. Но хотя есть и лучшие способы сделать это, начать с действительно плохой идеи.

Основная причина заключается в том, что чтение двоичных данных в BSTR VB6 преобразует данные из 8-битных байтов в 16-битные символы, используя текущую кодовую страницу. Таким образом, это может привести к различным результатам в БД в зависимости от того, в какой локали она работает. (!)

Поэтому, когда вы читаете его обратно из БД, если вы не укажете ту же кодовую страницу, которая использовалась при записи, вы получите разные значения с плавающей точкой, возможно, даже недопустимые.

Было бы полезно увидеть примеры данных как в двоичном (одиночном), так и в DB (строковом) виде, в шестнадцатеричном виде, чтобы убедиться, что это то, что происходит.

Из более позднего поста:

На самом деле это не "плохой" код VB6.

Это потому, что он переносит двоичные данные в строковую область, что нарушает основное правило современного кодирования VB. Вот почему существует тип данных Byte. Если вы проигнорируете это, вы можете получить неразборчивые данные, когда создаваемая БД пересекает границы локали.

Что он делает, так это хранит массив в компактном двоичном формате и сохранение это как «кусок» в базу данных. Есть много веских причин, чтобы сделать это.

Конечно, у него есть веская причина для этого (хотя ваше определение «компактный» отличается от обычного). Концы хороши: средства не выбраны.

К ОП:

Вы, вероятно, не можете изменить то, что вам дают в качестве входных данных, поэтому вышеизложенное в основном носит академический характер. Если у вас еще есть время изменить метод, используемый для создания больших двоичных объектов, давайте предложим методы, в которых не используются строки.

Применяя любое предоставленное решение, старайтесь избегать строк и, если не можете, декодируйте их, используя конкретную кодовую страницу, соответствующую той, которая их создала.

0 голосов
/ 25 апреля 2009

Сначала мы пропускаем каждый второй байт устранить отступы Юникода.

Хммм ... если бы это была правильная стратегия, то каждый второй столбец в дампе строки БД состоял бы только из нулей. Но быстрое сканирование первого показывает, что это не так. На самом деле в этих столбцах много ненулевых байтов. Можем ли мы позволить себе просто отбросить их?

Это показывает, что преобразование в Unicode, вызванное использованием Strings, не просто добавляет «padding», но меняет характер данных. То, что вы называете заполнением, является совпадением того факта, что диапазон 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.

0 голосов
/ 24 апреля 2009

На самом деле это не «плохой» код VB6. Что он делает, так это хранит массив в компактном двоичном формате и сохраняет его как «кусок» в базе данных. Есть много веских причин для этого.

Причина, по которой код VB6 сохраняет его на диск и читает его, заключается в том, что в VB6 отсутствует встроенная поддержка чтения и записи файлов только в памяти. Это обычный алгоритм, если вы хотите создать кусок двоичных данных и заполнить их где-нибудь еще, например, полем базы данных.

Это не проблема, связанная с этим в .NET. У меня есть код на VB.NET, поэтому вам придется конвертировать его в C #.

Изменено для обработки байтов и проблемы с юникодом.

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. У нас есть возможность использовать MemoryStreams в .NET, так что это довольно просто.

Сначала мы пропускаем каждый второй байт, чтобы исключить заполнение Unicode.

Затем мы создаем поток памяти из массива байтов. Затем BinaryReader инициализируется с помощью MemoryStream.

Мы читаем в первом измерении массива VB6 Long или .NET Int32 Мы читаем во втором измерении массива VB6 Long или .NET Int32

Циклы чтения строятся в порядке, обратном размеру массива. Dim2 - это внешний цикл, а Dim1 - это внутренний. Причина этого заключается в том, что именно так VB6 хранит массивы в двоичном формате.

Верните newData, и вы успешно восстановили исходный массив, созданный в VB6!

Теперь вы можете попробовать использовать математический трюк. Два измерения составляют 4 байта / символа, а каждый элемент массива составляет 4 байта / символа. Но для долгосрочной ремонтопригодности я считаю использование манипулирования байтами с потоками памяти более явным. Это займет немного больше кода, но намного яснее, когда вы вернетесь к нему через 5 лет.

0 голосов
/ 24 апреля 2009

Можете ли вы уточнить, каково содержимое файла (например, пример)? Или как двоичный (возможно, шестнадцатеричный) или как символы? Если данные представляют собой строку VB6, вам придется использовать float.Parse () для ее чтения. Строки .NET также по 2 байта на символ, но при загрузке из файла вы можете управлять этим с помощью Encoding.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...