basic-c/sections/12-files.tex

66 lines
9.2 KiB
TeX
Raw Permalink Normal View History

2021-10-15 14:15:06 +03:00
\section{Файлы}
В предыдущих разделах мы познакомились почти со всеми существующими в языке С типами данных, как примитивными, так и ссылочными. Довольно подробно рассмотрели работу почти всех операторов языка. Пришло время выйти за пределы программы (хотя бы в части хранения данных) и поговорить о взаимодействии программы с операционной системой, а именно - о чтении и записи в файловую систему компьютера. Файловая система \textit{любого} компьютера - \textbf{это структура}. Для языка С файл - это тоже структура. Структура, хранящая данные о положении курсора в файле, его название, буферы, флажки и прочие свойства. Файлы делятся на два основных типа - текстовые и бинарные. Мы рассмотрим работу с текстовыми, поскольку работа с бинарными практически ничем не отличается, а текст всё-таки проще воспринимается.
С места в карьер, опишем переменную, хранящую указатель на структуру <<файл>>. Вся основная работа будет проходить через неё. Для того, чтобы присвоить этой переменной указатель на какой-то реальный файл будем пользоваться функцией \code{fopen();}, которая возвращает указатель на адрес в памяти. Функция принимает в качестве аргументов имя файла в двойных кавычках и режим его открытия.
\begin{lstlisting}[language=C,style=CCodeStyle]
FILE *f;
\end{lstlisting}
Основных используемых режимов шесть:
\begin{itemize}
\item чтение,
\item запись,
\item добавление,
\item двоичное чтение,
\item двоичная запись,
\item двоичное добавление.
\end{itemize}
\subsection{Запись}
Функции \textbf{записи и добавления} \textit{создают} файл в случае его отсутствия. А функция \textbf{записи} \textit{стирает} файл, если он существует и не пустой. Итак создадим текстовый файл с каким-то неожиданным названием, вроде \code{filename.txt}, и скажем нашей программе, что нужно будет его создать, если его не существует, перезаписать, если существует, а дальше мы будем в него записывать данные, то есть режим открытия будет \code{"w"}. Имя файла в аргументе может быть как полным (абсолютным), вроде \code{C:\\FILE.TXT} тогда файл будет создан в корне диска C, или \code{/home/user/file.txt}. Также имя файла может быть и относительным, каким мы его указали сейчас, это значит, что файл будет создан в той папке, в которой запускается наша программа.
\begin{lstlisting}[language=C,style=CCodeStyle]
f = fopen("filename.txt", "w");
\end{lstlisting}
В случае, если файл не найден или по какой-то причине не создался, в переменную \code{f} запишется нулевой указатель, поэтому перед тем, как начать работу с файлом, нужно проверить, смогла-ли программа его открыть, для этого запишем условие, что если в наш указатель записался нулевой указатель, то дальнейшее выполнение функции \code{int main (int argc, char *argv[])} не имеет смысла.
\begin{lstlisting}[language=C,style=CCodeStyle]
if (file == NULL) return 1;
\end{lstlisting}
Если всё хорошо, можем записывать в файл данные. Для записи в файл есть несколько функций, мы воспользуемся самой простой и очевидной:\code{fprintf();}, в неё в качестве первого аргумента обязательно нужно передать указатель на файл, в который мы собираемся писать, а дальше можно использовать как знакомый нам \code{printf();} со всеми его удобствами, заполнителями, экранированными последовательностями и дополнительными аргументами. После того как мы закончили запись в файл его необходимо
закрыть, вызвав функцию \code{fclose();}
\begin{lstlisting}[language=C,style=CCodeStyle]
fprintf(f, "Hello, files! %s", "we did it!\n");
fclose(f);
\end{lstlisting}
Запустив проект, посмотрим что у нас получилось. В терминале не будет никакого вывода, но если перейти в проводник, то можно увидеть, что в папке проекта появился файл \code{filename.txt}, в котором написано наше содержимое. Открывается такой текстовый файл любым обычным блокнотом
\subsection{Чтение}
Отлично, программа теперь умеет сохранять результаты своей работы в файлы, то есть у нас появилось ещё одно средство вывода информации, помимо терминала. Теперь давайте рассмотрим не менее важную тему, а именно - чтение из файла. Ведь тогда мы сможем не просто иначе выводить информацию, но и сохранять её на жёстком диске от запуска к запуску программы, а значит хранить какие-то настройки или промежуточные значения. Для этого нам нужно совершить несколько несложных действий с применением неожиданно знакомой функции \code{fscanf();} чтобы прочитать форматированные значения из файла:
\begin{itemize}
\item создать массив \code{char[]}, для примера, назовем его \code{word};
\item нужный файл открыть в режиме чтения;
\item при помощи функции \code{fscanf();} считать из файла некоторую строку, которую положим в этот массив.
\end{itemize}
Далее выведем в консоль строку которую прочитали, для этого воспользуемся привычной нам функцией \code{printf();} а затем выведем пустую строку. Запустим нашу программу и увидим, что в консоль вывелось слово \code{Hello}, функция \code{fscanf();} отлично отработала, прочитав все символы из файла до пробела.
\begin{lstlisting}[language=C,style=CCodeStyle]
char word[256];
f = fopen("filename.txt", "r");
fscanf(f, "%s", &word);
printf("%s\n", word);
\end{lstlisting}
Но сколько данных читать? Как узнать, что достигнут конец файла? Для этого придумали функцию \code{feof();} (англ. file: end of file) возвращающую ноль, если конец файла не достигнут, и единицу если достигнут. Опишем цикл, который выведет в консоль все полученные \code{fscanf();} строки из нашего файла. То есть, мы циклически пройдемся по всему файлу, пока не будет достигнут его конец и будем выводить считанные строки в консоль:
\begin{lstlisting}[language=C,style=CCodeStyle]
char word[256];
f = fopen("filename.txt", "r");
while (!feof(file)) {
fscanf(f, "%s", &word);
printf("%s ", word);
}
printf("\n");
fclose(f);
\end{lstlisting}