Запись в текстовый файл

Последнее обновление: 03.07.2023

Для записи в файл мы можем использовать ранее рассмотренную функцию WriteFile, передавая ей дескриптор открытого файла. Например:

includelib kernel32.lib   ; подключаем библиотеку kernel32.lib

extrn WriteFile: proc
extrn CreateFileA: proc
extrn CloseHandle: proc

.data
text byte "Hello METANIT.COM"      ; тест для записи
textLen = $ - text            ; длина текста
filename byte "test.txt",0    ; имя файла

.code

; Параметры:
; RSI - адрес строки
; RCX - длина строки
; RAX - дескриптор файла
; Результат - через RAX возвращаем количество записанных байтов
write proc
  sub  rsp, 56
  mov rdx, rsi          ; Второй параметр - строка
  mov r8, rcx           ; Третий параметр - длина строки
  mov  rcx, rax         ; Первый параметр WriteFile - в регистр RCX помещаем дескриптор файла - консольного вывода
  lea  r9, bytesWritten       ; Четвертый параметр WriteFile - адрес для получения записанных байтов
  mov qword ptr [rsp + 32], 0  ; Пятый параметр WriteFile
  call WriteFile
    
  test rax, rax ; проверяем на наличие ошибки
  mov eax, bytesWritten ; если все нормально, помещаем в RAX количество записанных байтов
  jnz exit 
  mov rax, -1 ; Возвращаем через RAX код ошибки
exit:
  add  rsp, 56
  ret
bytesWritten equ [rsp+40]
write endp

; Процедура открытия существующего файла
; Параметр - в RSI - имя файла
; Результат - в RAX - дескриптор открытого файла
openFile proc
  sub rsp, 56
  mov rcx, rsi ; имя файла
  mov rdx, 080000004h ; доступ на запись и дозапись
  xor r8, r8 ; Эксклюзивный доступ
  xor r9, r9 ; атрибуты безопасности отсутствуют
  mov r10, 1 ; открыть существующий файл, если он не существует - создать
  mov [rsp + 32], r10
  mov r10, 128  ; обычный файл без атрибутов
  mov [rsp + 40], r10 
  mov [rsp + 48], r9    ; NULL - шаблон не используется
  call CreateFileA

  add rsp, 56
  ret
openFile endp

; Процедура закрытия существующего файла
; Параметр - в RAX - дескриптор открытого файла
; Результат - в RAX - состояние операции (ошибка или нет)
closeFile proc
  sub rsp, 40
  mov rcx, rax      ; В RCX помещаем дескриптор файла
  call CloseHandle
  add rsp, 40
  ret
closeFile endp

main proc
  lea rsi, filename
  call openFile   ; открываем файл

  push rax        ; сохраняем дескриптор файла
  lea rsi, text     ; записываемая строка
  mov rcx, textLen  ; длина записываемой строки
  call write        ; записываем строку
  pop rax           ; восстанавливаем дескриптор файла для закрытия файла

  call closeFile  ; закрываем файл
  ret
main endp
end

Здесь запись в файл производится в процедуре write, которая через регистр RSI получает адрес записываемого текста, через RCX - количество записываемых символов, а через RAX - дескриптор файла. Если файл уже существует, то выполняется дозапись.

Обработка ошибок

При записи могут возникнуть ошибки. Так, в программе выше у нас несколько потенциальных ситуации возникновения ошибки - файл не будет открыт, запись не будет произведена, закрытие файла пройдет неудачно. В качестве примера рассмотрим обработку ошибки при открытии файла:

includelib kernel32.lib  

extrn GetStdHandle: proc
extrn WriteFile: proc
extrn CreateFileA: proc
extrn CloseHandle: proc

.data
text byte "Hello METANIT.COM", 10      ; тест для записи
textLen = $ - text            ; длина текста
filename byte "test.txt",0    ; имя файла
badOpenMsg byte "Unable to open file", 10
badOpenMsgLen = $ - badOpenMsg
goodWriteMsg byte "File has been written", 10
goodWriteMsgLen = $ - goodWriteMsg

stdout qword 0  ; дескриптор консольного вывода
stdoutSet byte 0  ; установлен ли дескриптор консольного вывода

.code

; Параметры:
; RSI - адрес строки
; RCX - длина строки
; RAX - дескриптор файла
; Результат - через RAX возвращаем количество записанных байтов
write proc
  sub  rsp, 56
  mov rdx, rsi          ; Второй параметр - строка
  mov r8, rcx           ; Третий параметр - длина строки
  mov  rcx, rax         ; Первый параметр WriteFile - в регистр RCX помещаем дескриптор файла - консольного вывода
  lea  r9, bytesWritten       ; Четвертый параметр WriteFile - адрес для получения записанных байтов
  mov qword ptr [rsp + 32], 0  ; Пятый параметр WriteFile
  call WriteFile
    
  test rax, rax ; проверяем на наличие ошибки
  mov eax, bytesWritten ; если все нормально, помещаем в RAX количество записанных байтов
  jnz exit 
  mov rax, -1 ; Возвращаем через RAX код ошибки
exit:
  add  rsp, 56
  ret
bytesWritten equ [rsp+40]
write endp

; Процедура вывода произвольной строки на консоль 
; Параметры
; RSI - адрес строки
; RCX - количество символов
; Результат - в RAX количество записанных байтов или -1, если произошла ошибка
writeToConsole proc
  cmp stdoutSet, 1
  jz writeData    ; если дескриптор консольного вывода установлен
  sub rsp, 32
  push rcx            ; сохраняем количество символов
  mov rcx, -11         ; Аргумент для GetStdHandle - STD_OUTPUT
  call GetStdHandle     ; вызываем функцию GetStdHandle
  pop rcx         ; восстанавливаем RCX - количество символов
  add rsp, 32
  mov stdout, rax   ; помещаем в stdout дескриптор консольного вывода
  mov stdoutSet, 1  ; дескриптор консольного вывода установлен
writeData:
  mov rax, stdout
  call write
  ret
writeToConsole endp

; Процедура открытия существующего файла
; Параметр - в RSI - имя файла
; Результат - в RAX - дескриптор открытого файла
openFile proc
  sub rsp, 56
  mov rcx, rsi ; имя файла
  mov rdx, 0B0000004h ; доступ на запись и чтение
  xor r8, r8 ; Эксклюзивный доступ
  xor r9, r9 ; атрибуты безопасности отсутствуют
  mov r10, 4 ; открыть существующий файл, если он не существует - создать
  mov [rsp + 32], r10
  mov r10, 128  ; обычный файл без атрибутов
  mov [rsp + 40], r10 
  mov [rsp + 48], r9    ; NULL - шаблон не используется
  call CreateFileA

  add rsp, 56
  ret
openFile endp

; Процедура закрытия существующего файла
; Параметр - в RAX - дескриптор открытого файла
; Результат - в RAX - состояние операции (ошибка или нет)
closeFile proc
  sub rsp, 40
  mov rcx, rax      ; В RCX помещаем дескриптор файла
  call CloseHandle
  add rsp, 40
  ret
closeFile endp

main proc
  lea rsi, filename
  call openFile   ; открываем файл
  
  cmp eax, -1     ; если произошла ошибка открытия
  je badOpen

  push rax        ; сохраняем дескриптор файла
  lea rsi, text     ; записываемая строка
  mov rcx, textLen  ; длина записываемой строки
  call write        ; записываем строку
  pop rax           ; восстанавливаем дескриптор файла для закрытия файла

  call closeFile  ; закрываем файл
  lea rsi, goodWriteMsg
  mov rcx, goodWriteMsgLen
  call writeToConsole

  jmp exit

badOpen:                ; если не удалось открыть файл
  lea rsi, badOpenMsg
  mov rcx, badOpenMsgLen
  call writeToConsole
exit:
  ret
main endp
end

Здесь добавлена процедура writeToConsole для вывода диагностического сообщения на консоль. Если при открытии файла возникает проблема, соответственно в регистре EAX отказывается число -1, то переходим к метке badOpen для обработки этой ситуации. В коде выше в этом случае просто выводим сообщение. Если же запись в файл прошла успешно, выводим другое сообщение на консоль.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850