Раздел «Язык Си».OOP-Instrumental_3sem:

Файлы (системные функции)

Примеры использования системных функций для работы с файлами. Программа должна читать из текстового файла информацию и записывать ее в бинарный файл. Файловый дескриптор — целое число ≤ 0. Информация считывается из тестового файла. Имена обих файлов передаются как параметры командной строки.

Необходим инструментарий для получения информации о:

  1. размере файла (в байтах)
  2. владельце файла
  3. том, является ли файл обычным файлом или директорией
  4. правах доступа
и др.

Пример создания файла с заданными правами, запись в файл бинарной информации

Пример на языке С Пример на языке С++
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>

// Описание структуры для записи в файл
typedef struct S{
   char key;
   float a,b;
}SomeStr;

int main(int argc, char **argv){
  int k;
// Дескриптор файла вывода
  int fd_out;
// Объявления файлового дескриптора
  FILE *f1;
//Если имена файлов не переданы, прервать программу
  if (argc<2){
    printf("Необходимо указать 2 имя файла\n");
    exit(1);
  }
// Открыть файл на чтение и запись только для владельца файла.
// Если файла нет - создать. Если есть - стереть все содержимое

  fd_out = open(argv[1],O_RDWR|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR);

// Если не удалось открыть файл, прервать программу
  if (fd_out < 1){
  perror("Файл не может быть открыт\n");
  exit(1);
  }
// Открываем текстовый файл на чтение. Права доступа определяются 
// автоматически
  f1 = fopen(argv[2],"r");
  if(errno){
    perror("Файл не может быть открыт\n");
     exit(1);
   }
// Буфер структур для записи в бинарный файл
  SomeStr data[10];

  int  n,i = 0;
  float a,b;
  char z[100];
  char key;
  int c;
  
    int bf = 0;
// Чтение из тестового файла
// Пример записи в файл: f12 45w1 2, то есть  f 12 45; w 1 2
// fcanf возвращает количество прочитанных значений для переменных
// из списка
// Проверим, если удалось прочитать меньше трех, 
// прерываем цикл (файл кончился)  
   while(c=fscanf(f1,"%c%f%f",&key,&a,&b)){
        if(c<3) break;
// отладочная печать
      printf("key=%c a=%0.2f b=%0.2f\n",key,a,b);
      data[bf].key = key;
      data[bf].a = a;
      data[bf].b = b;
      bf++;
// если буфер полон, записываем его в бинарный файл
        if(bf==10){
        printf("bf:=%d пишем\n",bf);
        write(fd_out,&data,sizeof(SomeStr)*10);
        bf = 0;    
       }
   }
// Если буфер не полон, тоже нужно записать в файл.       
      bf--;
      if(bf > 0 && bf <9){
        printf("bf:=%d после пишем\n",bf);
        write(fd_out,data,sizeof(SomeStr)*(bf));
      }
 
// Закрываем текстовый файл
  fclose(f1);

// Закрываем бинарный файл
   close(fd_out);

return 0;
}
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <errno.h>

#include <iostream>
#include <cstdlib>
#include <fstream>

using namespace std;

// Описание структуры для записи в файл
struct SomeStr{
   char key;
   float a,b;
};

int main(int argc, char **argv){
  int k;
// Дескриптор файла вывода
  int fd_out;
// объект-файл на ввод (i<nput>f<ile>stream).
// Все файлы - это потоки
  ifstream f1;

//Если имена файлов не переданы, прервать программу
  if (argc<2){
    printf("Необходимо указать 2 имя файла\n");
    exit(1);
  }
// Открыть файл на чтение и запись только для владельца файла.
// Если файла нет - создать. Если есть - стереть все содержимое

  fd_out = open(argv[1],O_RDWR|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR);

// Если не удалось открыть файл, прервать программу
  if (fd_out < 1){
  perror("Файл не может быть открыт\n");
  exit(1);
  }

// Открываем файл - объект типа ifstream
// функция open
  f1.open(argv[2]);
// ernno так же принимает нудные знвчения
  if(errno){
    perror("Файл не может быть открыт\n");
     exit(1);
   }
// Буфер структур для записи в бинарный файл
  SomeStr data[10];

  int  n,i = 0;
  float a,b;
  char key;
  int c;
  
    int bf = 0;
// Чтение из тестового файла
// Пример записи в файл: f12 45w1 2, то есть  f 12 45; w 1 2
// Если оператор ввода (>>) не может прочитать все значения для
// переменныз из списка, цикл прервется
   while(f1>>key>>a>>b){
// отладочная печать
      cout<<key<<' '<<a<<' '<<b<<endl;
      data[bf].key = key;
      data[bf].a = a;
      data[bf].b = b;
      bf++;
// если буфер полон, записываем его в бинарный файл
        if(bf==10){
        cout<<"bf:="<<bf<<" пишем\n";
        write(fd_out,&data,sizeof(SomeStr)*10);
        bf = 0;    
       }
   }
// Если буфер не полон, тоже нужно записать в файл.       
      bf--;
      if(bf > 0 && bf <9){
        cout<<"bf:="<<bf<<" после пишем\n";
        write(fd_out,data,sizeof(SomeStr)*(bf));
      }
 
// Закрываем текстовый файл
   f1.close();

// Закрываем бинарный файл
   close(fd_out);

return 0;
}

Но, для того чтобы получить ИНСТРУМЕНТ, лучше написать класс на С++.

Сначала определим какие функции этого инструмента будут нужны. Для этого пишем интерфейс класса. В нем определяются все атрибуты (объекты-переменные) и функции, которые будут использоваться для объектоа этого класса.

Файл с описанием всех констант, объявлением новых типов данных, классов, структур, и интерфейсов функций — это заголовочный файл. Имеет расширение .h

#include <sys/types.h> 
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <pwd.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>

#include <iostream>
#include <cstdlib>

using namespace std;


class SystemFile{
// системная структура для информации о пользователе
  struct passwd *result;
// системная структура для информации о файле
  struct stat sb;
// файловый дескрипор
  int id;
// для прав доступа к файлу
  char mod;

public:
// Конструктор. Вызывается всегда при создании объекта
  SystemFile();
// Деструктор вызывается всегда, когда должен быть
// удален объект: при завершении функции, в которой он локальный
// или при вызове delete
   ~SystemFile();
// закрытие файла
  bool closef();
// открытие файла по его имени в каталоге
  unsigned char openf(char*);
// получить имя владельца файла
  string getUser();
// получить имя владльца файла (в байтах)
  long getSize();
// запись в файл. Передается уаказатель
// на память для записи и размер (в байтах) 
  bool writef(void*, long);
// Чтение из файла. В память по указателю 
// указаного размера. Память предварительно 
// должна быть ВЫДЕЛЕНА 
  bool readf(void*, long);

// печать общей информации о файле
  void about();
// получить количество записей в файле, 
// если известен размер записи
  long numbersOfRecord(long);

// найти f1 и заменить запись на f2 (полное соответствие)
// только для бинарного файла
  replace(void* f1, void* f2, size_t sizeRec);

// найти f1 и удалить (полное соответствие)
// только для бинарного файла
// для удаления использовать функцию truncate
// или ftruncate
  deletef(void* f1,  size_t sizeRec);
};

Назовем этот файл с интерфейсом sysfile.h.

В отдельном файле опишем реализацию всех объявленных в файле sfile.h. Это будет отдельный файл file.cpp.

#include "systfile.h"
// Конструктор. Инициализируем атрибуты.
SystemFile::SystemFile(){
  result = 0; //указатель на структуру 0
  id = -1; // если файл не открыт, дескриптор -1
  
};

// Деструктор. При удаленни объкта типа SystemFile
// файл должен быть закрыт.
SystemFile::~SystemFile(){
    if (id > -1){
       close(id);
       id = -1;
    } 
};

//функция класса для открытия файла
unsigned char SystemFile::openf(char* name)
{
   unsigned char er;
// Связывание дескриптора id с именем файла
  id=open(name,O_RDWR|O_CREAT|O_EXCL,0775);
  er = errno;
//Проверка открылся ли файл
    if (errno){
      if (errno==EEXIST){
       id=open(name,O_RDWR);
       er = 0;    
      }else{   
       return er;
      }
   }
// заполнение структуры stat информацией о файле   
   fstat(id, &sb);
// получение имени пользователя по uid
   result = getpwuid(sb.st_uid);  
   return er;
};
// запись в файл всего что есть в буфере (побайтно)
bool SystemFile::writef(void* buf, long size){
    long skolko = 0;
// системный вызов 
    skolko = write(id, buf, size);
    if (skolko == size)
      return 1;
    else
      return 0;
};

// закрыть файл
bool SystemFile::closef(){
     if (id>-1){
       close(id);
       id = -1;
       return 1;
     }
    return 0;
};

// печать информации о файле
void SystemFile::about(){
    printf("user: %s\n",result->pw_name);
   switch (sb.st_mode & S_IFMT) {
        case S_IFBLK:  printf("block device\n");
                 break;
        case S_IFCHR:  printf("character device\n");
            break;
        case S_IFDIR:  printf("directory\n");
               break;
       case S_IFIFO:  printf("FIFO/pipe\n");
               break;
       case S_IFLNK:  printf("symlink\n");
                 break;
       case S_IFREG:  printf("regular file\n"); 
           break;
       case S_IFSOCK: printf("socket\n");
                  break;
       default:       printf("unknown?\n");
   }

   printf("I-node number:  %ld\n", (long) sb.st_ino);

   printf("Mode:           %lo (octal)\n",(unsigned long) sb.st_mode);

   printf("Link count:    %ld\n", (long) sb.st_nlink);
   printf("Ownership:  UID=%ld   GID=%ld\n",(long) sb.st_uid, (long) sb.st_gid);
   printf("File size:  %lld bytes\n",(long long) sb.st_size);
   printf("Last status change:  %s", ctime(&sb.st_ctime));
   printf("Last file access: %s", ctime(&sb.st_atime));
   printf("Last file modification:   %s",ctime(&sb.st_mtime));
     
           
};

Пример файла для тестирования функций (C++) testF.cpp.

// включиить заголовочный файл 
#include "sysfile.h"

int main(int argc, char** argv){
// создается объект типа  SystemFile
// работает коструктор 
  SystemFile file;
// функция openf() возвращает значение errno,
// полученное в процессе создания или открытия файла
  if(!file.openf(argv[1])){
     perror("не открывается никак");
     exit(1);
  }
//  печать информации о файле
  file.about();
// объект C++ string 
  string s;
// получить строку с консоли
  cin>>s;
// c_str() - функция string возвращает указатель на 
// C-строку (массив символов с окончанием '\0'
// length() - возвращает размер строки в символах
  file.writef((void*)s.c_str(),s.length()*sizeof(char));
// закрытие файлаint n
  file.closef();

}

Компиляция этого проекта

>g++ testF.cpp file.cpp -o testf

REFACTOR Задачи

Задача 1.

Дан интерфейс класса для работы с каталогами.

Написать функции класса.

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <pwd.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>

#include <iostream>
#include <cstdlib>

using namespace std;
class Catalog{
// указатель на системную файловую 
// структуру для записей в каталоге (список файлов) 
   struct dirent* dirrec;
// дескриптор каталога
   DIR *dir;
   unsigned int numbersf;
// массив имен файлов
   string names[100];
public:
// конструктор
   Catalog();
// деструктор
   ~Catalog();
// открыть каталог
   unsigned char dopen(char*);
// получить имя файла из списка (по номеру)
   string getFile(int);
// закрыть каталог
   bool dclose();
// получить количество файлов.
   int getNumbers();
   void list();
};

1. Реализовать все функции для работы с каталогом.

REFACTOR Задача 2.

Рассмотрим задачу блокировки файла и пример на языке С

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

Примеры двух программ. Первая (f1.c) записывает информацию в бинарный файл и ждет нажатия клавиши, а вторая (f2.c) - сразу пишет в файл. Сначала запусаем f1

f1.c f2.c
#include<stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int main(int argc, char** argv){
// текстовый файл с данными
  FILE *f=fopen(argv[1],"r");
// системная структура для информации об 
// устанавливаемой блокировке
  struct flock lock;
// блокировка на запись
  lock.l_type=F_WRLCK;
// c 0
  lock.l_start=0;
// с начала файла
  lock.l_whence=SEEK_SET;
// на весь файл
  lock.l_len=0;
// открытие файла. Блокировка устанавливается толко на
// ОТКРЫТЫЙ файл 
  int fb=open(argv[2],O_RDWR|O_APPEND);
 
  char bif[100];
  fscanf(f,"%s",bif);

  int k;
// установка блокировки с ожиданием.
// процесс будет ждать пока не сможет
// установить желаемую блокировку
  k= fcntl(fb,F_SETLKW,&lock);
// запись в файл прочитанной информации
  write(fb,bif,strlen(bif));
  int d;
// ждем нажатия клавиши и недаем записать другому файлу
  d=getchar();
// установка разблокировки в поле структуры
  lock.l_type=F_UNLCK;
// установка разблокировки
  fcntl(fb,F_SETLKW,&lock);
printf("k=%d ok\n",k);
close(fb);
return 0;
}
#include<stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int main(int argc, char** argv){
 FILE *f=fopen(argv[1],"r");

 struct flock lock;

 lock.l_type = F_WRLCK;
 lock.l_start = 0;
 lock.l_whence = SEEK_SET;
 lock.l_len = 0;
 int fb = open(argv[2],O_RDWR|O_APPEND);
 char bif[100];
 fscanf(f,"%s",bif);
 int k;
// попытка установить блокировку. 
// Если файл заблокирован, будем ждать
 k = fcntl(fb,F_SETLKW,&lock);
// запись возможна только после установки блокировки
 int bl = write(fb,bif,strlen(bif));
 int d;
// установка разблокировки в поле структуры
 lock.l_type = F_UNLCK;
// установка разблокировки
 fcntl(fb,F_SETLKW,&lock);

 printf("k=%d bl=%d ok\n",k,bl);
 close(fb);
 return 0;
}

Добавить в класс SystemFile две функции int lock() и int unlock(). Если удалось заблокировать или разблокировать файл, возвращается 0, если нет 1.

Проверить их работу.

Задача 3

Написать код для всех остальных функций класса и проверить их.

Задача решается двумя студентами.

В файл travel.dat пишется следующая информация:

struct Gorod{
  char name1[100]; // название города
   char name2[100]; // название города
  float dist; // расстояние до города
  time_t pathtime; // время доезда
};

Написать две программы. Первую программу пишет один студент, вторую — другой. Для решения задачи использовать написанные ранее классы CTime и DataTime

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

Вторая программа читает этот же файл, запрашивает список городов для посещения, время пребывания в каждом городе, дату путешествия и вычисляет:

  1. день приезда в каждый город
  2. общее время путешествия
  3. *! порядок посещения городов, чтобы минимизировать время в пути. Избежать конфликта при обращении к файлу.

    -- TatyanaOvsyannikova2011 - 19 Oct 2016