Ассемблер для Windows

       

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



Рисунок 2.5.3(2). Вывод на консоль содержимого текстового фаша. Второй способ.

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

Основным признаком текстового файла является то, что он состоит из строк разной длины. Строки отделены друг от друга разделителями. Чаще всего это последовательность двух кодов - 13 и 10. Возможны и другие варианты, например, некоторые DOS-редакторы отделяли строки только одним кодом 13.

Построчное чтение текстового файла можно осуществить четырьмя наиболее очевидными способами.

  1. Побайтное чтение из файла. Как только достигаем символа-разделителя, производим действие над считанной строкой и переходим к чтению следующей строки. При этом, разумеется, следует учесть, что на конце файла может не быть символа-разделителя. Если кто-то решит, что это слишком медленный способ, то замечу, что Windows неплохо кэширует диск, так что все выглядит не так уж плохо.
  2. Чтение в небольшой буфер, но так чтобы туда входила, по крайней мере, одна строка. Прочитав, находим в буфере конец строки и производим над ней какое-либо действие. Далее следует обратиться к файлу и передвинуть указатель так, чтобы он был в файле на начале следующей строки и, разумеется, повторить действие.
  3. Чтение в произвольный буфер. После чтения производится поиск всех строк, попавших в буфер, и совершение над ними действий. При этом с большой вероятностью должна возникнуть ситуация, когда одна строка неполностью умещается в буфере. Мы обязаны учесть такую возможность.
  4. Чтение в буфер, в который помещается весь файл. Это частный случай третьего подхода, и наиболее простой с точки зрения программирования.
  5. В программе на Рисунок 2.5.4 реализуется третий подход.

    ; файл FILES2.ASM .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 EXTERN CharToOemA@8:NEAR ;------------------------------------------------- ; директивы компоновщику для подключения библиотек includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib ;-------------------------------------------------




    ; сегмент данных _DATA SEGMENT DWORD PUBLIC USE32 'DATA' HANDL DWORD ? ; дескриптор консоли HFILE DWORD ? ; дескриптор файла BUF DB 100 DUP (0) ; буфер для параметров BUFER DB 1000 DUP (0) ; буфер для файла NAMEOUT DB "CONOUT$" INDS DD 0 ; номер символа в строке INDB DD 0 ; номер символа в буфере NUMB DD ? NUMC DD ? PRIZN DD 0 STROKA DB 300 DUP (0) _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 ;--------------------------------------------------- ; получить параметр номером EDI MOV EDI,2 LEA EBX,BUF CALL GET PAR ; открыть файл PUSH 0 PUSH 0 PUSH OPEN_EXISTING PUSH 0 PUSH 0 PUSH GEN PUSH OFFSET BUF CALL CreateFileA@28 CMP EAX,-1 JE NO_PAR MOV HFILE, EAX ;++++++++++++++++++++++++++++ L00: ; читать 1000 байт PUSH 0 PUSH OFFSET NUMB PUSH 1000 PUSH OFFSET BUFER PUSH HFILE CALL ReadFile@20 MOV INDB, 0 ; проверим, есть ли в буфере байты CMP NUMB, 0 JZ _CLOSE ; заполняем строку L001: MOV EDI,INDS MOV ESI,INDB MOV AL,BYTE PTR BUFER[ESI] CMP AL,13 ; проверка на конец строки JE _ENDSTR MOV BYTE PTR STROKA[EDI],AL INC ESI INC EDI MOV INDS,EDI MOV INDB,ESI CMP NUMB, ESI ; проверка на конец буфера JNBE L001 ; закончился буфер MOV INDS,EDI MOV INDB,ESI JMP L00 _ENDSTR: ; делаем что-то со строкой CALL OUTST ; обнулить строку MOV INDS,0 ; перейти к следующей строке в буфере ADD INDB,2 ; не закончился ли буфер? MOV ESI,INDB CMP NUMB, ESI JAE L001 JMP L00 ;++++++++++++++++++++++++++++++ _CLOSE: ; проверим, не пустая ли строка CMP INDS,0 JZ CONT ; делаем что-то со строкой CALL OUTST CONT: ; закрыть файлы 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


    ; получить параметр из командной строки ; EBX - указывает на буфер, куда будет помещен параметр ; в буфер помещается строка с нулем на конце ; 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
    ; вывести строку в консоль с разделителем OUTST PROC MOV EBX,INDS MOV BYTE PTR STROKA[EBX],0 PUSH OFFSET STROKA PUSH OFFSET STROKA CALL CharToOemA@8 ; в конце строки - разделитель MOV BYTE PTR STROKA[EBX],6 INC INDS ; вывести строку PUSH 0 PUSH OFFSET NUMC PUSH INDS PUSH OFFSET STROKA PUSH HANDL CALL WriteFile@20 RET OUTST ENDP _TEXT ENDS END START

    Содержание раздела