Как переписать содержимое файла? Linux x86_64, сборка, ГАЗ - PullRequest
0 голосов
/ 02 ноября 2018

Привет, у меня есть эта захватывающая задача, почти выполненная на самом деле ... но она проваливается в забавном аспекте. Миссия состоит в том, чтобы загрузить файл с целыми числами, отсортировать их и записать их в файл. Уууу ... Ну, моя программа это делает, но она сохраняет оригинальный контент. то есть:

скажем 45 32

Должен получить заказанный контент 32 45

Что ж, моя программа сохраняет исходный контент и добавляет новое: 45 32 32 45.

Значит, есть какие-нибудь предположения по решению этого? Хотя удаление исходного файла и создание нового с тем же именем. Но это своего рода ошибка, если в файле есть нецелые числа, а в моем коде есть сообщения об ошибках.

Я дам здесь важный код:

_OpenFile:
    movq $2, %rax           # open file
    movq $inputfile, %rdi   # filename
    movq $2, %rsi           # read and write
    movq $0644, %rdx        # setting proper permissions
    syscall
    ret

И

_PrintNumber: #needs rdi as numberholder
    movq $1, %r9            # count the number of chars to print
    push $10                # store the chars on the stack, we always have '\n'
    movq %rdi, %rax         # things are easier with it in rax
    movq $10, %rcx
    decode_loop:
    movq $0, %rdx
    idivq %rcx              # do rdx:rax / rcx
    addq $48, %rdx          # convert the remainder to an ASCII digit
    pushq %rdx              # and save it on the stack
    addq $1, %r9            # while counting the number of chars
    cmpq $0, %rax
    jne decode_loop         # loop until rax == 0
    write_loop:
    movq $1, %rax           # write
    movq $3, %rdi           # to the file
    movq %rsp, %rsi         # which is stored here
    movq $1, %rdx           # a single character/byte
    syscall

    addq $8, %rsp           # pop the character
    addq $-1, %r9           # correct the char count

    jne write_loop          # loop until r9 reaches 0
    ret

Спасибо всем, кто хотел бы это прокомментировать!

1 Ответ

0 голосов
/ 03 ноября 2018

Похоже, что вы либо заново открываете файл с помощью O_APPEND, либо открыли его для чтения / записи и не пытались начать сначала, прежде чем переписать его. (Таким образом, после прочтения всего файла позиция дескриптора файла является концом файла, поэтому вновь записанные данные будут там.)

Системный вызов lseek(2) - это то, что вам нужно для перемещения позиции файла. lseek(fd, 0, SEEK_SET) перематывает в начало . (Аргументы идут в EDI, RSI, EDX, как обычно для соглашения системных вызовов x86-64 System V. Интерфейс ядра соответствует интерфейсу libc.)

Поскольку данные, которые вы будете записывать, имеют одинаковую длину, вам не нужно ftruncate(fd, len) файл, прежде чем начать перезапись. Вы перепишете все байты до конца.


И кстати, вам не нужно write каждый символ отдельно; вы можете создать небольшой буфер, содержащий все байты ASCII для номера, и сделать один системный вызов write; намного эффективнее и фактически требует меньше кода: Печать целого числа в виде строки с синтаксисом AT & T, с системными вызовами Linux вместо printf . Мой ответ там также показывает, что вы можете #include <asm/unistd.h> и использовать код как
mov $__NR_write, %eax # SYS_write, from unistd_64.h
вместо использования числовых литералов для номеров системных вызовов, если вы используете файл .S, чтобы gcc запускал его через препроцессор.

К сожалению, большинство заголовков, таких как <unistd.h> (не asm/unistd.h), также имеют объявления C, поэтому вы не можете так просто получить макросы для констант, таких как SEEK_SET или O_RDWR, которые позволили бы вам сделать mov $SEEK_SET, %edx или mov $O_WRONLY|O_CREAT|O_TRUNC, %esi.


Отмена ссылки на файл не повлияет на содержимое уже открытого файла; чтобы получить эффект, подобный тому, что вы изображаете в вопросе, вы можете закрыть / снова открыть файл. (В Unix удаление записи каталога для файла не влияет на программы, в которых он уже открыт. Это будет освобожден с диска, как только последняя запись в каталоге и дескриптор файла для него исчезли.)

Таким образом, вы откроете его для чтения, прочитаете все данные, а затем (после проверки на наличие ошибок, когда вы уверены, что у вас есть действительные данные для записи), open(infile, O_CREAT|O_TRUNC|O_WRONLY, 0666) и запишите данные. Вы не использовали O_APPEND, поэтому позиция нового FD только для записи будет находиться в начале файла. И размер файла будет усечен до 0. Точно так же, как echo foo > filename в оболочке.

(Он по-прежнему будет иметь тот же номер инода и будет «тем же файлом» с другим содержимым, если только вы не откроете его unlink(infile) для повторного создания нового файла с таким именем. В этом случае O_CREAT действительно необходимо . При повторном открытии существующего файла для записи + усечения O_CREAT не требуется, если файл уже существует.)

Ключевым моментом здесь является проверка ошибок чтения, прежде чем делать что-либо разрушительное , а не просто прочитать, уничтожить оригинал и продолжить. Таким образом, файл все еще находится на диске во время сортировки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...