На этом шаге мы перечислим способы чтения текстового файла.
Сейчас мы поговорим более подробно о структуре текстового файла. Рассмотрим возможные варианты работы с текстовыми файлами.
Основным признаком текстового файла является то, что он состоит из строк разной длины. Строки отделены друг от друга разделителями. Чаще всего это последовательность двух кодов - 13 и 10. Возможны и другие варианты, например, некоторые DOS-редакторы отделяли строки только одним кодом 13.
Построчное чтение текстового файла можно осуществить четырьмя наиболее очевидными способами.
В следующем примере реализован третий подход.
.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 WriteFile@20:NEAR EXTERN ReadFile@20:NEAR EXTERN CharToOemA@8: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 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 ;---------------------------------------- ;Получить параметр 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: ;Читать 1000 байт. PUSH 0 PUSH OFFSET NUMB PUSH 1000 PUSH OFFSET BUFER PUSH HFILE CALL ReadFile@20 MOV INDB,0 ;Проверим, есть ли в буфере байты. CMP NUMB,0 JZ _CLOSE ;Заполняем строку. LOO1: 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 LOO1 ;Закончился буфер. MOV INDS,EDI MOV INDB,ESI JMP LOO _ENDSTR: ;Делаем что-то со строкой. CALL OUTST ;Обнулить строку. MOV INDS,0 ;Перейти к следующей строке в буфере. ADD INDB,2 ;Не закончился ли буфер? MOV ESI,INDB CMP NUMB,ESI JAE LOO1 JMP LOO ;---------------------------------------- _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 ;Получить параметр из командной строки. ;ЕВХ - указывает на буфер, куда будет помещен параметр ;В буфер помещается строка с нулем на конце. ;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
Приведенный текст программы демонстрирует один из возможных алгоритмов обработки текстового файла - построчное чтение текстового файла. Часть программы, занимающаяся чтением и анализом текстового файла, сосредоточена между метками LOO и CONT. Детально разберитесь в приведенной программе.
Со следующего шага мы начнем рассматривать директивы и макросредства ассемблера.