C - удаление / изменение строки из файла - PullRequest
0 голосов
/ 18 декабря 2011

У меня есть datas.txt файл:

формат: имя, фамилия, задолженность, оплата

bir bir 100 2 
iki iki 200 2 
eray alakese 100 5 
john doe 2000 10 

Я учусь C и я знаю толькопростые файловые функции (fscanf, fprinf, fopen и т. д.)

Я

  1. запросит имя пользователя и фамилию с помощью scanf, а затем назначит их имя и фамилия переменные.
  2. Будет выполнен поиск файла для имя и фамилия , а затем присвоение долга и платежа долг , платеж переменные (fscanf(file, "%s %s %d %d", name, surname, &debt, &payment);)
  3. Удалить или изменить эту строку

Это мой исходный код.

    scanf("%s", &name);
    scanf("%s", &surname);
    file = fopen("datas.txt", "r");
    /* this fscanf() is working as expected. There is no problem. */
    fscanf(file, "%s %s %d %d", name, surname, &debt, &payment);

    /* modify and delete actions here */
    fclose(file);

Примеры:

  1. Я хочу удалить запись "John Doe".
  2. Я хочу уменьшить долг "John Doe" до 100 $

Ответы [ 5 ]

3 голосов
/ 18 декабря 2011

Вы не можете удалить / изменить [*] отдельные строки текстового файла; Единственное решение состоит в том, чтобы: 1) создать новый временный файл, 2) скопировать содержимое до, но не включая строку, которую вы хотите изменить / удалить, 3) вывести измененную строку, 4) скопировать оставшуюся часть исходного файла , 5) заменить старый файл временным файлом.

[*] Модификация возможна ТОЛЬКО в том случае, если длина измененной строки равна длине исходной строки.

РЕДАКТИРОВАТЬ: PS: Использование fgets с последующим sscanf (или каким-либо другим способом токенизации строки) избавит вас от большого горя.

1 голос
/ 18 декабря 2011

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

Когда проблема становится понятнее, когда мы пытаемся перевести наши идеи - «изменить эту строку», «удалить эту строку» - в файловые операции. Мы можем прочитать строку, просто остановившись на новой строке, но просто нет команды разрезать ее на разделы; только для установки конца (ftruncate ()). Таким образом, чтобы изменить размер строки, нам нужно скопировать все данные, которые следуют за ней. Это можно сделать, но очень часто проще просто создать файл заново. Сравните тонкости реализации memmove ().

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

Один из них - записать обновленную версию в другой файл, а затем переименовать () на место. Преимущество этого заключается в том, что новый файл будет готов к тому времени, когда вы его поместите, но есть и недостатки, что он может не соответствовать старому файлу точно в том, что касается разрешений и т. Д., И он не будет виден другим программам, которые уже был открыт старый. Если две программы изменяют файл следующим образом, это условие гонки, поскольку одно из изменений перезаписывается другим.

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

Существуют также инструменты для управления побочными эффектами, такие как файловые системы с поддержкой версий, блокировка файлов и даже библиотеки, подготовленные для параллельных изменений (на ум приходит метакит). Большую часть времени мы будем использовать инструменты, которые уже есть, такие как sed -i.

0 голосов
/ 18 декабря 2011

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

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

например. попробуйте написать функцию, которая может читать структуру, например:

struct Client
{
    char name[255];
    double owes;
    double paid;
}

То, что вы тогда делаете, создает массив этих структур и манипулирует ими. Вы узнаете много нового о структурах, динамическом распределении памяти и, несомненно, столкнетесь с некоторыми интересными проблемами, которые помогут вам учиться.

Мой совет также пропустить C и перейти к C ++ ... изучение этого материала с использованием iostreams вместо функций * printf / * scanf и векторов, вероятно, будет лучше для вас в долгосрочной перспективе

0 голосов
/ 18 декабря 2011

Для того чтобы удалить или изменить строку, вам необходимо «переложить» все после нее. Например, рассмотрим эти два файла:

bir bir 100 2           bytes 0-14
iki iki 200 2           bytes 15-29
eray alakese 100 5      bytes 30-49
john doe 2000 10        bytes 50-67

и

bir bir 100 2           bytes 0-14
iki iki 200 2           bytes 15-29
john doe 2000 10        bytes 30-57  <-- byte offsets have changed

Это, безусловно, возможно, но в целом поддерживать довольно сложно (вам придется много искать и рассказывать). Более обычный подход состоит в том, чтобы эффективно скопировать файл: вы читаете входной файл и распечатываете все в выходной файл, внося необходимые изменения. (Например, чтобы «удалить» строку, вы просто не печатаете эту строку.) Затем, в конце, после закрытия обоих файлов, вы «переименовываете» выходной файл, чтобы перезаписать входной файл. Это подход, который используют утилиты командной строки, такие как sed и perl, когда им поручено изменить файл "на месте".

0 голосов
/ 18 декабря 2011

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

/* pseudo-code!! */
fopen();
while (fscanf(source, ...)) {
    /* massage data */
    fprintf(temporary, ...);
}
fclose();
remove(source);
rename(temporary, source);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...