На этом шаге мы рассмотрим два способа вывода содержимого текстового файла.
Работа с файлами основана на нескольких функциях 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
На следующем шаге мы рассмотрим структуру текстового файла.