Проблема чтения бинарного файла

Программирование на Visual Basic, главный форум. Обсуждение тем программирования на VB 1—6.
Даже если вы плохо разбираетесь в VB и программировании вообще — тут вам помогут. В разумных пределах, конечно.
Правила форума
Темы, в которых будет сначала написано «что нужно сделать», а затем просьба «помогите», будут закрыты.
Читайте требования к создаваемым темам.
uk8amk
Обычный пользователь
Обычный пользователь
 
Сообщения: 67
Зарегистрирован: 26.07.2007 (Чт) 16:52
Откуда: Tashkent

Проблема чтения бинарного файла

Сообщение uk8amk » 26.07.2007 (Чт) 17:30

Всем привет!
Решил я сделать небольшую програмку для издевательства над файлами векторной графики. Формат файлов ILDA малоизвестен и даже бесплатных примитивных редакторов для него не найти. Но дело не в этом... Файл состоит из двух частей - заголовка и области данных. В заголовке указываются название, число точек, кадров и различная служебная информация. Получается,что необходимо считывать в различные переменные 1-2 байта. Одиночные байты в переменные типа Byte(0-255) читаются без проблем. Но как быть с числами в 2 байта? Дело в том, что эти 2 байта несут в себе 65536 значений(0-65535) - что-то типа Unsigned Integer(как в Си). Но в VB6 есть двухбайтовый Integer(-32767 до 32767). Когда я в него начинаю читать значения то получается какая-то чушь, а точнее сказать ничего не получается. Читаю я так: Get#1,,point_number где point_number типа Integer
Как мне быть с этой проблемой?
Исходник прилагается.
Вложения
ILDviewer.rar
(8.02 Кб) Скачиваний: 72

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 26.07.2007 (Чт) 17:43

А в vb только Signed Integer. Проблема в переводе из Unsigned в Signed?
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

keks-n
Доктор VB наук
Доктор VB наук
Аватара пользователя
 
Сообщения: 2509
Зарегистрирован: 19.09.2005 (Пн) 17:17
Откуда: г. Москва

Сообщение keks-n » 26.07.2007 (Чт) 18:46

uk8amk
Во первых, прочитай весь файл в байтовый массив - тогда можно будет использовать фишки типа CopyMemory.
А если у нас 2 байта, представляющие беззнаковое число, то заставить VB их переварить можно следущим образом:
Dim uint(1) As Byte
'Здесь заполняем их
Dim uint2 As Long
CopyMemory uint2,uint(0),2

В итоге, эти два байта скопировались в наш лонг. Поскольку младший байт идёт первым, то два старших не изменились, а в 2-х младших оказалось положительное число
Изображение

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 26.07.2007 (Чт) 19:06

CopyMemory - зло в данном случае. GetMemX рулят. Почему их так старательно все игнорируют?

И да. В unsined можно перевести вообще без API, голой VB-шной математикой.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Andrey Fedorov
Член-корреспондент академии VBStreets
Член-корреспондент академии VBStreets
 
Сообщения: 3287
Зарегистрирован: 21.05.2004 (Пт) 9:28
Откуда: Москва

Сообщение Andrey Fedorov » 27.07.2007 (Пт) 0:00

Ну, побайтно читать файл глупо хотя бы из-за низкой скорость.

В идеале надо просто считывать весь файл одним Get-ом в переменную пользовательского типа. Никакое API тут даром не нужно.

Не вижу ничего мешающего этому... Да и проще это... :D
Фиг Вам! - Сказал Чебурашка, обгладывая Крокодила Гену...

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 27.07.2007 (Пт) 4:56

Ну, побайтно читать файл глупо хотя бы из-за низкой скорость.


Кто-то предлагал читать по-байтно? :roll:
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

uk8amk
Обычный пользователь
Обычный пользователь
 
Сообщения: 67
Зарегистрирован: 26.07.2007 (Чт) 16:52
Откуда: Tashkent

Сообщение uk8amk » 27.07.2007 (Пт) 7:11

Спасибо всем что присоединились к обсуждению моей проблемы.
Пойду пробовать с CopyMemory.
В принципе мне здесь большой скорости чтения и не требуется - файлы обычно весят пару килобайт. Хотя и попадаются сцены и по 40-50МБ. В любом случае как говориться сначала нужно ввязаться в драку(т.е. прочитать файл), а потом уже думать как все это оптимизировать.

Andrey Fedorov
Член-корреспондент академии VBStreets
Член-корреспондент академии VBStreets
 
Сообщения: 3287
Зарегистрирован: 21.05.2004 (Пт) 9:28
Откуда: Москва

Сообщение Andrey Fedorov » 27.07.2007 (Пт) 8:14

Хакер писал(а):
Ну, побайтно читать файл глупо хотя бы из-за низкой скорость.


Кто-то предлагал читать по-байтно? :roll:


Побайтно - у автора в оригинале (исходниках что он приложил).

Проще уж считать в байтовый массив и работать с ним, как тут уже предлагалось...
Последний раз редактировалось Andrey Fedorov 27.07.2007 (Пт) 8:28, всего редактировалось 1 раз.
Фиг Вам! - Сказал Чебурашка, обгладывая Крокодила Гену...

Andrey Fedorov
Член-корреспондент академии VBStreets
Член-корреспондент академии VBStreets
 
Сообщения: 3287
Зарегистрирован: 21.05.2004 (Пт) 9:28
Откуда: Москва

Сообщение Andrey Fedorov » 27.07.2007 (Пт) 8:26

uk8amk писал(а):Пойду пробовать с CopyMemory.


Если поймешь что делается в коде приведенном ниже, то вполне обойдешься и без CopyMemory.

Код: Выделить всё
Option Explicit

Private Type TBytes4
    b1 As Byte
    b2 As Byte
    b3 As Byte
    b4 As Byte
End Type

Private Type TLong
    ln As Long
End Type

Public Sub Main()
    Dim tb4 As TBytes4
    Dim tln As TLong
   
    tb4.b1 = 255
    LSet tln = tb4
   
    Debug.Print tln.ln
   
    tb4.b2 = 255
    LSet tln = tb4

    Debug.Print tln.ln
End Sub
Фиг Вам! - Сказал Чебурашка, обгладывая Крокодила Гену...

Andrey Fedorov
Член-корреспондент академии VBStreets
Член-корреспондент академии VBStreets
 
Сообщения: 3287
Зарегистрирован: 21.05.2004 (Пт) 9:28
Откуда: Москва

Сообщение Andrey Fedorov » 27.07.2007 (Пт) 8:36

И еще один, полезный для данной задачки, примерчик:

Код: Выделить всё
Option Explicit

Public Sub Main()
    Dim m(7) As Byte, s As String
   
    m(0) = 207
    m(1) = 240
    m(2) = 238
    m(3) = 226
    m(4) = 229
    m(5) = 240
    m(6) = 234
    m(7) = 224
   
    s = StrConv(m, vbUnicode)

    Debug.Print s
End Sub
Фиг Вам! - Сказал Чебурашка, обгладывая Крокодила Гену...

uk8amk
Обычный пользователь
Обычный пользователь
 
Сообщения: 67
Зарегистрирован: 26.07.2007 (Чт) 16:52
Откуда: Tashkent

Сообщение uk8amk » 27.07.2007 (Пт) 9:50

Чето я ничего не понимаю. Написал так:
Dim total_points As Long '0-65535
Dim frame_number As Long
Dim total_frames As Long '0-65535
Dim ta_byte(1) As Byte
.....
Get #1, , ta_byte(0) '25-26
Get #1, , ta_byte(1)
CopyMemory total_points, ta_byte(0), 2
Form1.Print Str(total_points) + " òî÷åê"
//это чето русские символы не скопировались:)
.....
Когда начинаю открывать файл, то на выходе либо нули либо десятки тысяч. Хотя файлы содержат всего несколько сотен точек. Мож я что не так cделал? А когда смотрел файлы в HEX редакторе, то там все впорядке, как написано в стандарте.
Кстати о том,как 2 байта в Long Integer запихнуть. Можно пойти в лоб. Накладывать на каждый байт маску на 1 бит а потом проверять "наличие" этого бита и если он есть, то прибавлять к переменной "вес" этого бита. Т.е. типа такого if ta_byte(1) AND 1024=1 then total_points=total_points+1024
Да и еще хотел спросить. Оператор Get# вытаскивает из файла данных сколько весит переменная или только 1 байт?
Вложения
ILD_viewer.rar
(9.03 Кб) Скачиваний: 46

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 27.07.2007 (Пт) 9:54

Оператор Get# вытаскивает из файла данных сколько весит переменная или только 1 байт?


Ы! Сколько весит переменная? :lol:

Ну, вобщем, если не придираться к фразе, то да.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Andrey Fedorov
Член-корреспондент академии VBStreets
Член-корреспондент академии VBStreets
 
Сообщения: 3287
Зарегистрирован: 21.05.2004 (Пт) 9:28
Откуда: Москва

Сообщение Andrey Fedorov » 27.07.2007 (Пт) 11:07

For uk8amk

Вот примерно так должен выглядеть код твоей формы, если его немножко привести в нормальный вид:

Код: Выделить всё
Option Explicit

Private Type TIDLFile
    ILDA(3) As Byte
    ByteH(2) As Byte
    Fmt As Byte
    FrameName(7) As Byte
    CompanyName(7) As Byte
    TotalPoints As Integer
    FrameNumber As Integer
    TotalFrames As Integer
    NumHeader As Byte
    ByteR As Byte
End Type

Private Type TPoints
    X As Byte
    Y As Byte
    Z As Byte
    Status As Integer
    R(2) As Byte
End Type

Private Type TInteger2
    i1 As Integer
    i2 As Integer
End Type

Private Type TLong
    ln As Long
End Type

Private Sub ild_open_Click()
    Dim iHFile As Integer, tb As TIDLFile
    Dim ui As TInteger2, uln As TLong, up() As TPoints
    Dim TotalPoints As Long, i As Long
    Dim prev_x As Single, nx As Single
    Dim prev_y As Single, ny As Single
   
    CommonDialog1.Filter = "ILDA(*.ild)|*.ild"
    CommonDialog1.ShowOpen
    If Len(CommonDialog1.FileName) = 0 Then Exit Sub
   
    iHFile = FreeFile
    Open CommonDialog1.FileName For Binary Access Read Lock Write As #iHFile
    Get #iHFile, , tb
   
    Print StrConv(tb.ILDA, vbUnicode) & " Формат"
    Select Case tb.Fmt
        Case 0: Print "3D формат"
        Case 1: Print "2D формат":          Close #iHFile: Exit Sub
        Case 2: Print "Заголовок палитры":  Close #iHFile: Exit Sub
    End Select
    Print "Frame: " & StrConv(tb.FrameName, vbUnicode)
    Print "Company: " & StrConv(tb.CompanyName, vbUnicode)
   
    ui.i1 = tb.TotalPoints
    LSet uln = ui
    Print uln.ln & " точек"
    TotalPoints = uln.ln
    If TotalPoints > 0 Then
        ReDim up(TotalPoints - 1)
        Get #iHFile, , up
    End If
    Close #iHFile
   
    ui.i1 = tb.FrameNumber
    LSet uln = ui
    Print "Номер кадра: " & uln.ln
   
    ui.i1 = tb.TotalFrames
    LSet uln = ui
    Print "Всего кадров: " & uln.ln
   
    Print "Номер головки: " & tb.NumHeader
   
    For i = 0 To TotalPoints - 1
        With up(i)
'            Debug.Print Hex(.X), Hex(.Y)
            nx = .X / 100 + 327
            ny = .Y / 100 + 327
            Picture1.Line (prev_x, prev_y)-(nx, ny), vbWhite
            prev_x = nx
            prev_y = ny
        End With
    Next i
End Sub

Private Sub ild_close_Click()
    Unload Me
End Sub


Замечу, - я не знаю формата этого файла, а просто немножко привел в порядок написанное тобой. Так что правильно ли там рисуется или нет - это зависит от твоего кода... А там явно есть проблемы...
Фиг Вам! - Сказал Чебурашка, обгладывая Крокодила Гену...

uk8amk
Обычный пользователь
Обычный пользователь
 
Сообщения: 67
Зарегистрирован: 26.07.2007 (Чт) 16:52
Откуда: Tashkent

Сообщение uk8amk » 27.07.2007 (Пт) 16:18

Пытался испытать Copymemory, но че-то у меня не пошло. Пришлось написать отдельную функцию:
Function btlc(b2 As Byte, b1 As Byte) As Long
'b1=mladshiy bayt
'b2-starshiy bayt
Dim t_res As Long
t_res = b1
If (b2 And 1) = 1 Then t_res = t_res + 256
If (b2 And 2) = 1 Then t_res = t_res + 512
.....
If (b2 And 128) = 1 Then t_res = t_res + 32768
btlc = t_res
End Function

После этого начал получать восновном правильные значения. Но одновременно столкнулся с новой трудностью. Каждой точке соответствуют 4 слова(8байт). 2 для X, 2 для У и 2 несут служебную информацию. В стандарте ILDA написано следующее:
"Byte 33-34. X coordinate. A 16 bit binary twos complement (signed) number. Extreme left is -32767; extreme right is +32767/(All directions stated using front projection.)" Примерно тоже написано и для остальных координат. Заманчиво сразу читать в переменную Integer, не правда ли? Но не тут то было. При считывании получается всякий мусор. Однако, если считывать только первые байты координат и выводить их в пикчурбокс, то получается картинка(правда если не учитывать то, что координаты должны иметь знак, а переменная Byte его не имеет и поэтому рисунок выводится по кусочкам в разных частях пикчурбокса). Неужели есть и другие виды представления чисел типа Integer и как это побороть?

keks-n
Доктор VB наук
Доктор VB наук
Аватара пользователя
 
Сообщения: 2509
Зарегистрирован: 19.09.2005 (Пн) 17:17
Откуда: г. Москва

Сообщение keks-n » 27.07.2007 (Пт) 16:29

Видать, порядок байт перевёрнутый
Изображение

HiSER
Обычный пользователь
Обычный пользователь
Аватара пользователя
 
Сообщения: 88
Зарегистрирован: 04.07.2007 (Ср) 18:17

Сообщение HiSER » 27.07.2007 (Пт) 16:57

Код: Выделить всё
Private Function ByteToUSInt(b2 As Byte, b1 As Byte) As Long
'b1 - Low byte
'b2 - High byte
ByteToUSInt = b1 Or b2 * &H100&
End Function

Private Function IntToUSInt(iNum As Integer) As Long
If iNum And &H8000 Then
IntToUSInt = iNum + &H10000
Else
IntToUSInt = iNum
End If
End Function
Последний раз редактировалось HiSER 27.07.2007 (Пт) 17:23, всего редактировалось 1 раз.

Andrey Fedorov
Член-корреспондент академии VBStreets
Член-корреспондент академии VBStreets
 
Сообщения: 3287
Зарегистрирован: 21.05.2004 (Пт) 9:28
Откуда: Москва

Сообщение Andrey Fedorov » 27.07.2007 (Пт) 17:18

uk8amk писал(а):Каждой точке соответствуют 4 слова(8байт). 2 для X, 2 для У и 2 несут служебную информацию.


Ах по два... Ну тогда поправил по два - код твоей формы теперь такой (что-то даже стало рисоваться):

Код: Выделить всё
Option Explicit

Private Type TIDLFile
    ILDA(3) As Byte
    ByteH(2) As Byte
    Fmt As Byte
    FrameName(7) As Byte
    CompanyName(7) As Byte
    TotalPoints(1) As Byte
    FrameNumber(1) As Byte
    TotalFrames(1) As Byte
    NumHeader As Byte
    ByteR As Byte
End Type

Private Type TPoints
    X(1) As Byte
    Y(1) As Byte
    R(3) As Byte
End Type

Private Type TByte4
    b1 As Byte
    b2 As Byte
    b3 As Byte
    b4 As Byte
End Type

Private Type TLong
    ln As Long
End Type

Private Sub ild_open_Click()
    Dim iHFile As Integer, tb As TIDLFile
    Dim up() As TPoints
    Dim TotalPoints As Long, i As Long, Status As Long
    Dim prev_x As Single, nx As Single
    Dim prev_y As Single, ny As Single
   
    CommonDialog1.Filter = "ILDA(*.ild)|*.ild"
    CommonDialog1.ShowOpen
    If Len(CommonDialog1.FileName) = 0 Then Exit Sub
   
    iHFile = FreeFile
    Open CommonDialog1.FileName For Binary Access Read Lock Write As #iHFile
    Get #iHFile, , tb
   
    Print StrConv(tb.ILDA, vbUnicode) & " Формат"
    Select Case tb.Fmt
        Case 0: Print "3D формат"
        Case 1: Print "2D формат":          Close #iHFile: Exit Sub
        Case 2: Print "Заголовок палитры":  Close #iHFile: Exit Sub
    End Select
    Print "Frame: " & StrConv(tb.FrameName, vbUnicode)
    Print "Company: " & StrConv(tb.CompanyName, vbUnicode)
   
    TotalPoints = BytesToLong(tb.TotalPoints)
    Print TotalPoints & " точек"
    If TotalPoints > 0 Then
        ReDim up(TotalPoints - 1)
        Get #iHFile, , up
    End If
    Close #iHFile
   
    Print "Номер кадра: " & BytesToLong(tb.FrameNumber)
    Print "Всего кадров: " & BytesToLong(tb.TotalFrames)
    Print "Номер головки: " & tb.NumHeader

    For i = 0 To TotalPoints - 1
        With up(i)
            nx = BytesToLong(.X) / 100 + 327
            ny = BytesToLong(.Y) / 100 + 327
            Picture1.Line (prev_x, prev_y)-(nx, ny), vbWhite
            prev_x = nx
            prev_y = ny
        End With
    Next i
End Sub

Private Sub ild_close_Click()
    Unload Me
End Sub

Private Function BytesToLong(b() As Byte) As Long
    Dim ub4 As TByte4, uln As TLong
    ub4.b1 = b(1)
    ub4.b2 = b(0)
    LSet uln = ub4
    BytesToLong = uln.ln
End Function
Фиг Вам! - Сказал Чебурашка, обгладывая Крокодила Гену...


Вернуться в Visual Basic 1–6

Кто сейчас на конференции

Сейчас этот форум просматривают: AhrefsBot, Majestic-12 [Bot] и гости: 22

    TopList