【问题标题】:seek_cur command sets the cursor in an unknown place in the fileseek_cur 命令将光标设置在文件中的未知位置
【发布时间】:2019-04-10 19:59:02
【问题描述】:

我有一个文件,每行包含一个单词(单词的数量和长度未知),我需要将这些单词重写到另一个文件中,从最后一个单词开始到第一个单词。当我打印文件中的最后一个单词时,我尝试将光标 (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

【问题讨论】:

  • 不是问题,但seed_end = 2 使其成为汇编时间常数比从静态存储加载双字2 更有意义!此外,fgetcfscanf(fp, "%c") 更容易和更高效,并且会将字符返回到寄存器中,您可以比从内存中更有效地读取它。或者您可以使用fscanf(fp, "%s"),因为%s 会跳过空格。

标签: windows assembly x86 masm


【解决方案1】:

string db 0 为 1 个字节保留空间(初始化为零)。

然后您调用fgets(fp, string, back),如果它读取超过 1 个字节(包括终止的 0),它将覆盖您数据部分中的后续内容。

在 BSS 中使用更大的缓冲区,比如几 MB 之类的。


使用调试器来跟踪函数调用/系统调用。在 Linux 上,您可以使用 ltrace 跟踪 libc stdio 函数,或使用 strace 跟踪它们使用的系统调用。在 Windows IDK 上。您始终可以在每次调用之前单步执行并查看您在堆栈中推送的参数,以确保它们是理智的,但是在查找具有错误参数的参数时,通常更容易看到日志文件样式的列表。

【讨论】:

  • 将“string db 0”更改为“string dd 0”并立即生效。非常感谢!
  • @Kenshoru:呃,如果您读取的字符串长度超过 3 个字节(+ 1 个终止符),它仍然会中断。使用类似.bss / string db 1024*1024 dup(?)
  • 终于注意到了,它用 3 个字符就可以正常工作。我会研究一下。谢谢!
猜你喜欢
  • 1970-01-01
  • 2011-03-14
  • 1970-01-01
  • 2018-03-21
  • 1970-01-01
  • 2020-12-10
  • 2011-04-03
  • 2012-04-16
  • 1970-01-01
相关资源
最近更新 更多