команда seek_cur устанавливает курсор в неизвестное место в файле

#windows #assembly #x86 #masm

#Windows #сборка #x86 #masm

Вопрос:

У меня есть файл, который содержит по одному слову в каждой строке (количество слов и их длина неизвестны), и мне нужно переписать эти слова в другой файл, начиная с последнего слова и заканчивая первым. Когда я печатаю последнее слово в файле, я пытаюсь настроить курсор (seek_cur) на поиск следующего слова, но он устанавливает его в неизвестное место. Попробовал распечатать текущий курсор, чтобы посмотреть, что произойдет, и он выдает символы типа «@A».

Второй jmp get_out останавливает программу после записи последнего слова, если оно удалено, оно переходит к тегу поиска jmp, а затем печатает одно и то же последнее слово бесконечно.

 .386
.model flat, stdcall
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;includem biblioteci, si declaram ce functii vrem sa importam
includelib msvcrt.lib
extern exit: proc
extern fopen: proc
extern fclose: proc
extern fscanf: proc
extern fprintf: proc
extern fseek: proc
extern fgets:proc

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;declaram simbolul start ca public - de acolo incepe executia
public start
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;sectiunile programului, date, respectiv cod
.data
file_read db "r",0
file_write db "w",0

file_name_read db "read.txt",0
file_name_write db "write.txt",0

seek_end dd 2
seek_cur dd 1
seek_set dd 0

caracter_format db "%c",0
string_format db "%s",0
decimal_format db "%d",0

string db 0
caracter db 0

back dd 0
first_word db 0

.code
start:
    push offset file_read
    push offset file_name_read
    call fopen
    add esp,8
    mov esi,eax

    push offset file_write
    push offset file_name_write
    call fopen
    add esp,8
    mov edi,eax

    ;in first_word se pune cuvantul de pe prima linie ca sa fie posibila comparatia mai incolo si sa se iese din bucla cand se ajunge la primul cuvant
    repeat_search_first_word:
        push offset caracter
        push offset caracter_format
        push esi
        call fscanf
        add esp,12

        inc back

        cmp caracter,0Ah
        je out_of_search_first_word 

    jmp repeat_search_first_word

        out_of_search_first_word:

        inc back
        neg back
        push seek_cur
        push back
        push esi
        call fseek
        add esp,12  

        neg back
        push esi
        push back
        push offset first_word
        call fgets
        add esp,12

        mov back,0


    ;incepe cautarea cuvintelor de la capat
    push seek_end
    push -1
    push esi
    call fseek
    add esp,12  

    search:
        push offset caracter
        push offset caracter_format
        push esi
        call fscanf
        add esp,12

        inc back

        cmp caracter,0Ah
        jne is_caracter

            push esi
            push back
            push offset string
            call fgets
            add esp,12

            push offset string
            push offset string_format
            push edi
            call fprintf
            add esp,12

            ;testam daca cuvantul coincide cu primul (first_word)
            mov ebx,0
            mov bl,string
            cmp bl,first_word
            je get_out


            add back,2
            neg back

            ;!!!!!!!!!!!!!!
            ;problema pentru rularea infinita ii aici fiindca seek_cur muta cursorul intr-o zona necunoscuta din fisier 
            push seek_cur
            push back
            push esi
            call fseek
            add esp,12


            mov back,0
            jmp get_out ;linia 152 lasata fara comentariu permite afisare ultimului cuvant fara sa intre in rularea infinita a buclei
            jmp search

        is_caracter:

        push seek_cur
        push -2
        push esi
        call fseek
        add esp,12  

    jmp search  
    get_out:

    push edi
    call fclose 
    add esp,4

    push esi
    call fclose 
    add esp,4



    push 0
    call exit
end start
  

read.txt содержит:

 abc                          
defg                         
hijklm                      
  

write.txt должно быть:

 hijklm
defg
abc
  

Комментарии:

1. не проблема, но seed_end = 2 сделать его постоянной времени сборки имело бы гораздо больше смысла, чем загрузка dword 2 из статического хранилища! Кроме того, fgetc это намного проще и эффективнее, чем fscanf(fp, "%c") , и вернет символ в регистр, где вы могли бы прочитать его более эффективно, чем из памяти. Или вы могли бы использовать fscanf(fp, "%s") , потому %s что пропускает пробелы.

Ответ №1:

string db 0 резервирует место для 1 байта (инициализируется нулем).

Затем вы вызываете fgets(fp, string, back) , который перезапишет более поздние данные в вашем разделе данных, если он считывает более 1 байта (включая завершающий 0).

Используйте больший буфер в BSS, например, пару МБ или что-то в этом роде.


Используйте отладчик для отслеживания вызовов функций / системных вызовов. В Linux вы можете использовать ltrace для отслеживания функций libc stdio или strace для отслеживания системных вызовов, которые они используют. В Windows IDK. Вы всегда можете просмотреть и просмотреть аргументы, которые вы поместили в стек перед каждым вызовом, чтобы убедиться, что они в порядке, но часто бывает проще увидеть список в стиле файла журнала, если вы ищете один с неправильными аргументами.

Комментарии:

1. Изменил «строку db 0» на «строку dd 0», и это сработало мгновенно. Большое спасибо!

2. @Kenshoru: э-э, это все равно сломается, если вы прочитаете строку длиной более 3 байт ( 1 ограничитель). Используйте что-то вроде .bss / string db 1024*1024 dup(?)

3. Заметил это в конце концов, он отлично работал с 3 символьными словами.. Я посмотрю на это. Спасибо!