Вопрос № 183693: Здравствуйте, уважаемые эксперты! Прошу вас ответить на следующий вопрос: вывести на экран последнюю строку из текстового файла. Дополнительные условия: DOS, TASM, COM-программа. Приложил код (не мой). В нем чтение и отображение на экран тек...
Вопрос № 183693:
Здравствуйте, уважаемые эксперты! Прошу вас ответить на следующий вопрос: вывести на экран последнюю строку из текстового файла. Дополнительные условия: DOS, TASM, COM-программа. Приложил код (не мой). В нем чтение и отображение на экран текстового файла целиком.
Есть свои соображения по поводу алгоритма. Проблема в их реализации. Открывается файл на чтение (уже есть). Начинается чтение файла построчно (функцией BIOS или DOS)? (при чтении каждый символ копируется в переменную зарезервированную
для строки). Если символ не конец строки и не перевод каретки, то копирование продолжается. Если так, то идет проверка на EOF. Если EOF не найден после 0dh,0ah, то строка заново переписывается символами, прочитанными из следующей строки. Если символ EOF, то последняя строка найдена (она в памяти и уже содержит прочитанные символы). Переход на вывод строки. Конец программы.
Прошу дополнить приложенный код.
PS В коде не понятны несколько с
трок:
Код :
mov cx,ax
mov ah,40h
xchg bp,bx
int 21h
xchg bp,bx
Конкретнее, на счет регистров bp,bx. Разъясните подробнее, пожалуйста.
Отвечает Лысков Игорь Витальевич (Старший модератор) :
Здравствуйте, Ханинёв Пётр Валерьевич!
Я считаю, что для решения поставленной задачи читать весь файл, пробегать его по всем строкам только для того, чтобы найти последнюю строку нет никакого резона. Гораздо проще, сразу "встать" в конец файла и тут же определиться с последней строкой. А посему, предлагаю Вам такой алгоритм. Читаем последние максимум 256 байт и уже в них ищем с конца последнюю строку
Отвечу на
поставленные вопросы: 1) Прерывание 21h - это ДОС. 2) В языке ассемблера невозможно читать из файла построчно. Можно только прочитать кусок файла в буфер и самому там анализировать. 3) Что Вы понимаете под EOF? Если спецсимвол, то да, есть такой, его код 1ah. Но! Он практически не используется! Поэтому анализировать на него нет смысла. Обычно под EOF понимают ситуацию, когда нечего уже читать... (В программе, как Вы поняли сделано по-другому) В дан
ной программе я сделал анализ на код 1ah, но только для того, чтобы его исключить при выводе на экран. Кроме него, сделан анализ на код перевода страницы, для той же цели.
По поводу регистров BP и BX Вам уже ответили в мини-форуме...
Код :
;вывести на экран последнюю строку из текстового файла.
.model TINY
.code
org 100h
Entry:
mov ax, 3D00h ;открываем файл на чтение (al=0)
mov dx, offset file_name ;имя файла
int 21h
jc open_error ;проверка на ошибку
mov bx, ax ;описатель файла
mov ax, 4202h ;установим указатель на позицию от конца файла
mov dx, -100h ;cx:dx = -100h, т.е. 100h назад от конца
mov cx, -1
int 21h ;если файл меньше, чем 100h, то указатель останется
; на начале файла!
mov cx, 100h ;читаем максимум 100h (ровно последние)
mov ah, 3Fh ;ф-я чтения
mov dx, offset buffer ;буфер для чтения
int 21h
jc read_error ;проверка на ошибку
mov cx, ax ;реальная длина прочитанного фрагмента
mov ah, 3Eh ;файл закрываем
int 21h
;найдем начало последней строки
;будем искать код 0ah
mov di, dx ;адрес начала буфера
add di, cx ;на адрес за буфером
what_last: ;проверим последние байты:
;символы "конец файла" (ctrl-Z = SUB = 1ah) и
;"перевод страницы" (ctrl-L = FF = 0ch) отсекаем,
;"перевод строки" (ctrl-J = LF = 0ah) пропускаем,
;ищем предыдущий
jcxz zero_file ;если длина = 0, то выведем сообщение
dec di ;на адрес последнего байта
mov al, [di] ;читаем его
cmp al, 0ah ;перевод строки?
je EOL_found
cmp al, 1ah ;конец файла?
je EOF_found
cmp al, 0ch ;перевод страницы?
jne search_begin ;нет - на поиск начала последней строки
EOF_found: ;встретили код конца файла или перевод страницы
dec cx ;уменьшим длину буфера, тем самым исключим из вывода
jmp what_last ;продолжим анализ
EOL_found: ;конец строки
dec di ;уменьшим указатель, т.о. обойдем его
search_begin: ;ищем код 0ah в обратном направлении
std ;взведем флаг обратного направления
push cx ;сохраним длину буфера
mov al, 0ah ;код для поиска
repne scasb ;ищем cx раз, пока не равно
pop cx ;восстановим длину буфера
cld ;установим прямое направление
jne for_first ;если не равно, то имеем только одну строку
;и надо только компенсировать результат scasb
inc di ;если равно, то надо обойти коды 0dh и 0ah
for_first:
inc di ;scasb проскакивает на 1 позицию дальше
add cx, offset buffer ;cx = адресу за буфером
sub cx, di ;cx = длине строки
;строка найдена, ее адрес в di, длина в cx - выводим
mov al, 0 ;код возврата
out_string: ;вывод строки на стандартное устройство вывода (bx = 1)
push ax ;сохраним код возврата
mov dx, di ;адрес начала строки
mov bx, 1 ;описатель устройства - на экран
mov ah, 40h ;ф-я записи
int 21h
pop ax ;восстановим код возврата
mov ah, 4Ch ;выход в ДОС
int 21h
;обработка ошибок
zero_file: ;нулевой файл
lea di, s_zero_error ;адрес сообщения
mov cx, len_s_zero_error ;длина
mov al, 1 ;код возврата
jmp out_string ;на вывод
open_error: ;ошибка открытия файла
lea di, s_open_error ;адрес сообщения
mov cx, len_s_open_error ;длина
mov al, 2 ;код возврата
jmp out_string ;на вывод
read_error:
lea di, s_read_error ;адрес сообщения
mov cx, len_s_read_error ;длина
mov al, 3 ;код возврата
jmp out_string ;на вывод
.data ;сегмент инициализированных данных
file_name db "_file.txt",0 ;имя файла
s_zero_error db "Zero file"
len_s_zero_error equ $-s_open_error ;длина строки
s_open_error db "File open error"
len_s_open_error equ $-s_open_error
s_read_error db "File read error"
len_s_read_error equ $-s_read_error
.data? ;сегмент неинициализированных данных
buffer db 256 dup (?) ;буфер для чтения "хвоста" файла
END Entry
----- Люби своего ближнего, как самого себя
Ответ отправил: Лысков Игорь Витальевич (Старший модератор)
Ответ отправлен: 22.06.2011, 13:10
Номер ответа: 267810 Украина, Кировоград Тел.: +380957525051 ICQ # 234137952 Mail.ru-агент: igorlyskov@mail.ru
Оценка ответа: 5 Комментарий к оценке: Оптимальный вариант решения задачи. Подробные комментарии.
Вам помог ответ? Пожалуйста, поблагодарите эксперта за это! Как сказать этому эксперту "спасибо"?
Отправить SMS#thank 267810
на номер 1151 (Россия) |
Еще номера »
Оценить выпуск »
Нам очень важно Ваше мнение об этом выпуске рассылки!
* Стоимость одного СМС-сообщения от 7.15 руб. и зависит от оператора сотовой связи.
(полный список тарифов)
** При ошибочном вводе номера ответа или текста #thank услуга считается оказанной, денежные средства не возвращаются.
*** Сумма выплаты эксперту-автору ответа расчитывается из суммы перечислений на портал от биллинговой компании.