Шаг 48.
Приемы работы с двоичными файлами

    На этом шаге мы рассмотрим два способа вывода содержимого текстового файла.

    Работа с файлами основана на нескольких функциях API, главной и наиболее сложной из которых является функция CreateFile.

    С помощью этой функции можно не только создавать или открывать файл, но и такие объекты, как каналы (PIPE), консоли, устройства жесткого диска (disk device), коммуникационный ресурс и др. Функция различает устройство по структуре имени. К примеру, "C:\config.sys" определяет файл, а "CONOUT$" - буфер вывода текущей консоли.

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

;Вывод на консоль содержимого текстового файла.
;Первый способ.
.386P
;Плоская модель памяти.
.MODEL FLAT, STDCALL
;Константы.
STD_OUTPUT_HANDLE  equ  -11
GENERIC_READ     equ 80000000h
GENERIC_WRITE    equ 40000000h
GEN  = GENERIC_READ or GENERIC_WRITE
SHARE = 0
OPEN_EXISTING    equ  3
;Прототипы внешних процедур.
EXTERN  GetStdHandle@4:NEAR
EXTERN  WriteConsoleA@20:NEAR
EXTERN  ExitProcess@4:NEAR
EXTERN  GetCommandLineA@0: NEAR
EXTERN	CreateFileA@28:NEAR
EXTERN	CloseHandle@4:NEAR
EXTERN	ReadFile@20:NEAR
;-----------------------------------------
;Директивы компоновщику для подключения библиотек 
includelib \masm32\lib\user32.lib 
includelib \masm32\lib\kernel32.lib
;-----------------------------------------
;Сегмент данных.
_DATA SEGMENT DWORD PUBLIC USE32 'DATA' 
      HANDL  DWORD  ?
      HFILE  DWORD  ?
      BUF    DB  100  DUP(0)
      BUFER  DB  300  DUP(0)
      NUMB   DWORD  ?
      NUMW   DWORD  ? 
_DATA ENDS 
;Сегмент кода.
_TEXT SEGMENT  DWORD  PUBLIC USE32   'CODE' 
START: 
;Получить  HANDLE  вывода.
      PUSH    STD_OUTPUT_HANDLE
      CALL    GetStdHandle@4
      MOV     HANDL,EAX 
;Получить количество параметров.
      CALL     NUMPAR
;Если параметр один, то искать в текущем каталоге.
      CMP      EAX,1
      JE       NO_PAR
;----------------------------------------
;Получить параметр c номером EDI.
      MOV      EDI,2
      LEA      EBX,BUF
      CALL     GETPAR
;Открыть файл.
      PUSH 0              ;Должен быть равен 0.
      PUSH 0	          ;Атрибут файла (если  создаем)
      PUSH OPEN_EXISTING  ;Как открывать.
      PUSH 0              ;Указатель на security attr.
      PUSH 0              ;Режим общего доступа.
      PUSH GEN            ;Режим доступа.
      PUSH OFFSET BUF     ;Имя файла.
      CALL CreateFileA@28
      CMP  EAX,-1
      JE   NO_PAR
      MOV HFILE,EAX 
LOO: 
;Прочесть в буфер.
      PUSH 0
      PUSH OFFSET NUMB
      PUSH 300
      PUSH OFFSET BUFER
      PUSH HFILE
      CALL ReadFile@20 
;Вывести содержимое буфера на консоль.
      PUSH 0
      PUSH OFFSET NUMW
      PUSH NUMB
      PUSH OFFSET BUFER
      PUSH HANDL
      CALL WriteConsoleA@20 
;Проверить, не последние ли байты прочитаны.
      CMP  NUMB,300
      JE   LOO 
;Закрыть файл.
      PUSH HFILE
      CALL CloseHandle@4 
;Конец работы программы.
NO_PAR:
      PUSH 0
      CALL ExitProcess@4 
;****************************
;Область процедур.
;****************************
;Процедура определения количества параметров в строке. 
;Определить количество параметров (->EAX) 
NUMPAR PROC
      CALL     GetCommandLineA@0
      MOV      ESI,EAX ;Указатель на строку.
      XOR      ECX,ECX ;Счетчик.
      MOV      EDX,1   ;Признак.
L1:
      CMP      BYTE PTR [ESI],0
      JE       L4
      CMP      BYTE PTR [ESI],32
      JE       L3
      ADD      ECX,EDX ;Номер параметра.
      MOV      EDX,0
      JMP      L2
L3:
      OR       EDX,1 
L2:
      INC      ESI
      JMP      L1 
L4:
      MOV      EAX,ECX
      RET
NUMPAR ENDP
;Получить параметр из командной строки. 
;ЕВХ - указывает на буфер, куда будет помещен параметр 
;В буфер помещается строка с нулем на конце. 
;EDI - номер параметра. 
GETPAR PROC
      CALL     GetCommandLineA@0
      MOV      ESI,EAX  ;Указатель  на строку.
      XOR      ECX,ECX  ;Счетчик.
      MOV      EDX,1    ;Признак. 
L1:
      CMP      BYTE  PTR [ESI], 0
      JE       L4		
      CMP      BYTE  PTR [ESI], 32
      JE       L3		
      ADD      ECX,EDX   ;Номер параметра.
      MOV      EDX,0		
      JMP      L2		
L3:			
      OR       EDX,1		
L2:			
      CMP      ECX,EDI		
      JNE      L5		
      MOV      AL, BYTE PTR [ESI]
      MOV      BYTE PTR [EBX], AL
      INC      EBX		
L5:
      INC      ESI		
      JMP      L1		
L4:
      MOV      BYTE  PTR   [EBX],0
      RET
GETPAR ENDP
_TEXT ENDS 
      END START
Текст этой программы можно взять здесь.
;Вывод на консоль содержимого текстового файла.
;Первый способ.
.386P
;Плоская модель памяти.
.MODEL FLAT, STDCALL
;Константы.
STD_OUTPUT_HANDLE  equ  -11
GENERIC_READ     equ 80000000h
GENERIC_WRITE    equ 40000000h
GEN  = GENERIC_READ or GENERIC_WRITE
SHARE = 0
OPEN_EXISTING    equ  3
;Прототипы внешних процедур.
EXTERN  ExitProcess@4:NEAR
EXTERN  GetCommandLineA@0: NEAR
EXTERN	CreateFileA@28:NEAR
EXTERN	CloseHandle@4:NEAR
EXTERN	ReadFile@20:NEAR
EXTERN  WriteFile@20:NEAR
;-----------------------------------------
;Директивы компоновщику для подключения библиотек 
includelib \masm32\lib\user32.lib 
includelib \masm32\lib\kernel32.lib
;-----------------------------------------
;Сегмент данных.
_DATA SEGMENT DWORD PUBLIC USE32 'DATA' 
      HANDL  DWORD  ?
      HFILE  DWORD  ?
      BUF    DB  100  DUP(0)
      BUFER  DB  300  DUP(0)
      NUMB   DWORD  ?
      NUMW   DWORD  ? 
      NAMEOUT DB "CONOUT$"
_DATA ENDS 
;Сегмент кода.
_TEXT SEGMENT  DWORD  PUBLIC USE32   'CODE' 
START: 
;Получить  HANDLE  вывода (консоли) как файла.
      PUSH    0
      PUSH    0
      PUSH    OPEN_EXISTING
      PUSH    0
      PUSH    0
      PUSH    GEN
      PUSH    OFFSET NAMEOUT
      CALL    CreateFileA@28
      MOV     HANDL,EAX 
;Получить количество параметров.
      CALL     NUMPAR
;Если параметр один, то искать в текущем каталоге.
      CMP      EAX,1
      JE       NO_PAR
;----------------------------------------
;Получить параметр c номером EDI.
      MOV      EDI,2
      LEA      EBX,BUF
      CALL     GETPAR
;Открыть файл.
      PUSH 0              ;Должен быть равен 0.
      PUSH 0	          ;Атрибут файла (если  создаем)
      PUSH OPEN_EXISTING  ;Как открывать.
      PUSH 0              ;Указатель на security attr.
      PUSH 0              ;Режим общего доступа.
      PUSH GEN            ;Режим доступа.
      PUSH OFFSET BUF     ;Имя файла.
      CALL CreateFileA@28
      CMP  EAX,-1
      JE   NO_PAR
      MOV HFILE,EAX 
LOO: 
;Прочесть в буфер.
      PUSH 0
      PUSH OFFSET NUMB
      PUSH 300
      PUSH OFFSET BUFER
      PUSH HFILE
      CALL ReadFile@20 
;Вывести на консоль как в файл.
      PUSH 0
      PUSH OFFSET NUMW
      PUSH NUMB
      PUSH OFFSET BUFER
      PUSH HANDL
      CALL WriteFile@20
;Проверить, не последние ли байты прочитаны.
      CMP  NUMB,300
      JE   LOO 
;Закрыть файл.
      PUSH HFILE
      CALL CloseHandle@4 
;Конец работы программы.
NO_PAR:
      PUSH 0
      CALL ExitProcess@4 
;****************************
;Область процедур.
;****************************
;Процедура определения количества параметров в строке. 
;Определить количество параметров (->EAX) 
NUMPAR PROC
      CALL     GetCommandLineA@0
      MOV      ESI,EAX ;Указатель на строку.
      XOR      ECX,ECX ;Счетчик.
      MOV      EDX,1   ;Признак.
L1:
      CMP      BYTE PTR [ESI],0
      JE       L4
      CMP      BYTE PTR [ESI],32
      JE       L3
      ADD      ECX,EDX ;Номер параметра.
      MOV      EDX,0
      JMP      L2
L3:
      OR       EDX,1 
L2:
      INC      ESI
      JMP      L1 
L4:
      MOV      EAX,ECX
      RET
NUMPAR ENDP
;Получить параметр из командной строки. 
;ЕВХ - указывает на буфер, куда будет помещен параметр 
;В буфер помещается строка с нулем на конце. 
;EDI - номер параметра. 
GETPAR PROC
      CALL     GetCommandLineA@0
      MOV      ESI,EAX  ;Указатель  на строку.
      XOR      ECX,ECX  ;Счетчик.
      MOV      EDX,1    ;Признак. 
L1:
      CMP      BYTE  PTR [ESI], 0
      JE       L4		
      CMP      BYTE  PTR [ESI], 32
      JE       L3		
      ADD      ECX,EDX   ;Номер параметра.
      MOV      EDX,0		
      JMP      L2		
L3:			
      OR       EDX,1		
L2:			
      CMP      ECX,EDI		
      JNE      L5		
      MOV      AL, BYTE PTR [ESI]
      MOV      BYTE PTR [EBX], AL
      INC      EBX		
L5:
      INC      ESI		
      JMP      L1		
L4:
      MOV      BYTE  PTR   [EBX],0
      RET
GETPAR ENDP
_TEXT ENDS 
      END START
Текст этой программы можно взять здесь.

    На следующем шаге мы рассмотрим структуру текстового файла.




Предыдущий шаг Содержание Следующий шаг