It is quite common that errors may occur while reading data from a file in C++ or writing data to a file. For example, an error may arise due to the following:
- When trying to read a file beyond indicator.
- When trying to read a file that does not exist.
- When trying to use a file that has not been opened.
- When trying to use a file in an inappropriate mode i.e., writing data to a file that has been opened for reading.
- When writing to a file that is write-protected i.e., trying to write to a read-only file.
Failure to check for errors then the program may behave abnormally therefore an unchecked error may result in premature termination for the program or incorrect output.
Below are some Error handling functions during file operations in C/C++:
ferror():
In C/C++, the library function ferror() is used to check for the error in the stream. Its prototype is written as:
int ferror (FILE *stream);
The ferror() function checks for any error in the stream. It returns a value zero if no error has occurred and a non-zero value if there is an error. The error indication will last until the file is closed unless it is cleared by the clearerr() function.
Below is the program to implement the use of ferror():
C
#include <stdio.h>
#include <stdlib.h>
int
main()
{
FILE
* fp;
char
feedback[100];
int
i;
fp =
fopen
(
"GeeksForGeeks.TXT"
,
"w"
);
if
(fp == NULL) {
printf
(
"n The file could "
"not be opened"
);
exit
(1);
}
printf
(
"n Provide feedback on "
"this article: "
);
fgets
(feedback, 100, stdin);
for
(i = 0; i < feedback[i]; i++)
fputc
(feedback[i], fp);
if
(
ferror
(fp)) {
printf
(
"n Error writing in file"
);
exit
(1);
}
fclose
(fp);
}
C++
#include <bits/stdc++.h>
int
main()
{
FILE
* fp;
char
feedback[100];
int
i;
fp =
fopen
(
"GeeksForGeeks.TXT"
,
"w"
);
if
(fp == NULL) {
printf
(
"n The file could "
"not be opened"
);
exit
(1);
}
printf
(
"n Provide feedback on "
"this article: "
);
fgets
(feedback, 100, stdin);
for
(i = 0; i < feedback[i]; i++)
fputc
(feedback[i], fp);
if
(
ferror
(fp)) {
printf
(
"n Error writing in file"
);
exit
(1);
}
fclose
(fp);
}
Output:
Explanation: After executing this code “Provide feedback on this article:“ will be displayed on the screen and after giving some feedback “Process exited after some seconds with return value 0″ will be displayed on the screen.
clearerr():
The function clearerr() is used to clear the end-of-file and error indicators for the stream. Its prototype can be given as:
void clearerr(FILE *stream);
The clearerr() clears the error for the stream pointed by the stream. The function is used because error indicators are not automatically cleared. Once the error indicator for a specific stream is set, operations on the stream continue to return an error value until clearerr(), fseek(), fsetpos(), or rewind() is called.
Below is the program to implement the use of clearerr():
C
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int
main()
{
FILE
* fp;
char
feedback[100];
char
c;
fp =
fopen
(
"file.txt"
,
"w"
);
c =
fgetc
(fp);
if
(
ferror
(fp)) {
printf
(
"Error in reading from"
" file : file.txtn"
);
}
clearerr
(fp);
if
(
ferror
(fp)) {
printf
(
"Error in reading from "
"file : file.txtn"
);
}
fclose
(fp);
}
C++
#include <bits/stdc++.h>
int
main()
{
FILE
* fp;
char
feedback[100];
char
c;
fp =
fopen
(
"file.txt"
,
"w"
);
c =
fgetc
(fp);
if
(
ferror
(fp)) {
printf
(
"Error in reading from"
" file : file.txtn"
);
}
clearerr
(fp);
if
(
ferror
(fp)) {
printf
(
"Error in reading from "
"file : file.txtn"
);
}
fclose
(fp);
}
Output:
The function perror() stands for print error. In case of an error, the programmer can determine the type of error that has occurred using the perror() function. When perror() is called, then it displays a message describing the most recent error that occurred during a library function call or system call. Its prototype can be given as:
void perror (char*msg);
- The perror() takes one argument which points to an optional user-defined message the message is printed first followed by a colon and the implementation-defined message that describes the most recent error.
- If a call to perror() is made when no error has actually occurred then ‘No error’ will be displayed.
- The most important thing to remember is that a call to perror() and nothing is done to deal with the error condition, then it is entirely up to the program to take action. For example, the program may prompt the user to do something such as terminate the program.
- Usually, the program’s activities will be determined by checking the value of errno and the nature of the error.
- In order to use the external constant errno, you must include the header file ERRNO.H
Below is the program given below illustrates the use of perror(). Here, assume that the file “file.txt” does not exist.
C
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int
main()
{
FILE
* fp;
rename
(
"file.txt"
,
"newfile.txt"
);
fp =
fopen
(
"file.txt"
,
"r"
);
if
(fp == NULL) {
perror
(
"Error: "
);
return
(-1);
}
fclose
(fp);
return
(0);
}
C++
#include <bits/stdc++.h>
#include <errno.h>
int
main()
{
FILE
* fp;
rename
(
"file.txt"
,
"newfile.txt"
);
fp =
fopen
(
"file.txt"
,
"r"
);
if
(fp == NULL) {
perror
(
"Error: "
);
return
(-1);
}
fclose
(fp);
return
(0);
}
Output:
Last Updated :
10 Jan, 2022
Like Article
Save Article
It is quite common that errors may occur while reading data from a file in C++ or writing data to a file. For example, an error may arise due to the following:
- When trying to read a file beyond indicator.
- When trying to read a file that does not exist.
- When trying to use a file that has not been opened.
- When trying to use a file in an inappropriate mode i.e., writing data to a file that has been opened for reading.
- When writing to a file that is write-protected i.e., trying to write to a read-only file.
Failure to check for errors then the program may behave abnormally therefore an unchecked error may result in premature termination for the program or incorrect output.
Below are some Error handling functions during file operations in C/C++:
ferror():
In C/C++, the library function ferror() is used to check for the error in the stream. Its prototype is written as:
int ferror (FILE *stream);
The ferror() function checks for any error in the stream. It returns a value zero if no error has occurred and a non-zero value if there is an error. The error indication will last until the file is closed unless it is cleared by the clearerr() function.
Below is the program to implement the use of ferror():
#include <stdio.h>
#include <stdlib.h>
int
main()
{
FILE
* fp;
char
feedback[100];
int
i;
fp =
fopen
(
"GeeksForGeeks.TXT"
,
"w"
);
if
(fp == NULL) {
printf
(
"n The file could "
"not be opened"
);
exit
(1);
}
printf
(
"n Provide feedback on "
"this article: "
);
fgets
(feedback, 100, stdin);
for
(i = 0; i < feedback[i]; i++)
fputc
(feedback[i], fp);
if
(
ferror
(fp)) {
printf
(
"n Error writing in file"
);
exit
(1);
}
fclose
(fp);
}
C++
#include <bits/stdc++.h>
int
main()
{
FILE
* fp;
char
feedback[100];
int
i;
fp =
fopen
(
"GeeksForGeeks.TXT"
,
"w"
);
if
(fp == NULL) {
printf
(
"n The file could "
"not be opened"
);
exit
(1);
}
printf
(
"n Provide feedback on "
"this article: "
);
fgets
(feedback, 100, stdin);
for
(i = 0; i < feedback[i]; i++)
fputc
(feedback[i], fp);
if
(
ferror
(fp)) {
printf
(
"n Error writing in file"
);
exit
(1);
}
fclose
(fp);
}
Output:
Explanation: After executing this code “Provide feedback on this article:“ will be displayed on the screen and after giving some feedback “Process exited after some seconds with return value 0″ will be displayed on the screen.
clearerr():
The function clearerr() is used to clear the end-of-file and error indicators for the stream. Its prototype can be given as:
void clearerr(FILE *stream);
The clearerr() clears the error for the stream pointed by the stream. The function is used because error indicators are not automatically cleared. Once the error indicator for a specific stream is set, operations on the stream continue to return an error value until clearerr(), fseek(), fsetpos(), or rewind() is called.
Below is the program to implement the use of clearerr():
C
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int
main()
{
FILE
* fp;
char
feedback[100];
char
c;
fp =
fopen
(
"file.txt"
,
"w"
);
c =
fgetc
(fp);
if
(
ferror
(fp)) {
printf
(
"Error in reading from"
" file : file.txtn"
);
}
clearerr
(fp);
if
(
ferror
(fp)) {
printf
(
"Error in reading from "
"file : file.txtn"
);
}
fclose
(fp);
}
C++
#include <bits/stdc++.h>
int
main()
{
FILE
* fp;
char
feedback[100];
char
c;
fp =
fopen
(
"file.txt"
,
"w"
);
c =
fgetc
(fp);
if
(
ferror
(fp)) {
printf
(
"Error in reading from"
" file : file.txtn"
);
}
clearerr
(fp);
if
(
ferror
(fp)) {
printf
(
"Error in reading from "
"file : file.txtn"
);
}
fclose
(fp);
}
Output:
The function perror() stands for print error. In case of an error, the programmer can determine the type of error that has occurred using the perror() function. When perror() is called, then it displays a message describing the most recent error that occurred during a library function call or system call. Its prototype can be given as:
void perror (char*msg);
- The perror() takes one argument which points to an optional user-defined message the message is printed first followed by a colon and the implementation-defined message that describes the most recent error.
- If a call to perror() is made when no error has actually occurred then ‘No error’ will be displayed.
- The most important thing to remember is that a call to perror() and nothing is done to deal with the error condition, then it is entirely up to the program to take action. For example, the program may prompt the user to do something such as terminate the program.
- Usually, the program’s activities will be determined by checking the value of errno and the nature of the error.
- In order to use the external constant errno, you must include the header file ERRNO.H
Below is the program given below illustrates the use of perror(). Here, assume that the file “file.txt” does not exist.
C
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int
main()
{
FILE
* fp;
rename
(
"file.txt"
,
"newfile.txt"
);
fp =
fopen
(
"file.txt"
,
"r"
);
if
(fp == NULL) {
perror
(
"Error: "
);
return
(-1);
}
fclose
(fp);
return
(0);
}
C++
#include <bits/stdc++.h>
#include <errno.h>
int
main()
{
FILE
* fp;
rename
(
"file.txt"
,
"newfile.txt"
);
fp =
fopen
(
"file.txt"
,
"r"
);
if
(fp == NULL) {
perror
(
"Error: "
);
return
(-1);
}
fclose
(fp);
return
(0);
}
Output:
It is quite common that errors may occur while reading data from a file in C++ or writing data to a file. For example, an error may arise due to the following:
- When trying to read a file beyond indicator.
- When trying to read a file that does not exist.
- When trying to use a file that has not been opened.
- When trying to use a file in an inappropriate mode i.e., writing data to a file that has been opened for reading.
- When writing to a file that is write-protected i.e., trying to write to a read-only file.
Failure to check for errors then the program may behave abnormally therefore an unchecked error may result in premature termination for the program or incorrect output.
Below are some Error handling functions during file operations in C/C++:
ferror():
In C/C++, the library function ferror() is used to check for the error in the stream. Its prototype is written as:
int ferror (FILE *stream);
The ferror() function checks for any error in the stream. It returns a value zero if no error has occurred and a non-zero value if there is an error. The error indication will last until the file is closed unless it is cleared by the clearerr() function.
Below is the program to implement the use of ferror():
C
#include <stdio.h>
#include <stdlib.h>
int
main()
{
FILE
* fp;
char
feedback[100];
int
i;
fp =
fopen
(
"GeeksForGeeks.TXT"
,
"w"
);
if
(fp == NULL) {
printf
(
"n The file could "
"not be opened"
);
exit
(1);
}
printf
(
"n Provide feedback on "
"this article: "
);
fgets
(feedback, 100, stdin);
for
(i = 0; i < feedback[i]; i++)
fputc
(feedback[i], fp);
if
(
ferror
(fp)) {
printf
(
"n Error writing in file"
);
exit
(1);
}
fclose
(fp);
}
C++
#include <bits/stdc++.h>
int
main()
{
FILE
* fp;
char
feedback[100];
int
i;
fp =
fopen
(
"GeeksForGeeks.TXT"
,
"w"
);
if
(fp == NULL) {
printf
(
"n The file could "
"not be opened"
);
exit
(1);
}
printf
(
"n Provide feedback on "
"this article: "
);
fgets
(feedback, 100, stdin);
for
(i = 0; i < feedback[i]; i++)
fputc
(feedback[i], fp);
if
(
ferror
(fp)) {
printf
(
"n Error writing in file"
);
exit
(1);
}
fclose
(fp);
}
Output:
Explanation: After executing this code “Provide feedback on this article:“ will be displayed on the screen and after giving some feedback “Process exited after some seconds with return value 0″ will be displayed on the screen.
clearerr():
The function clearerr() is used to clear the end-of-file and error indicators for the stream. Its prototype can be given as:
void clearerr(FILE *stream);
The clearerr() clears the error for the stream pointed by the stream. The function is used because error indicators are not automatically cleared. Once the error indicator for a specific stream is set, operations on the stream continue to return an error value until clearerr(), fseek(), fsetpos(), or rewind() is called.
Below is the program to implement the use of clearerr():
C
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int
main()
{
FILE
* fp;
char
feedback[100];
char
c;
fp =
fopen
(
"file.txt"
,
"w"
);
c =
fgetc
(fp);
if
(
ferror
(fp)) {
printf
(
"Error in reading from"
" file : file.txtn"
);
}
clearerr
(fp);
if
(
ferror
(fp)) {
printf
(
"Error in reading from "
"file : file.txtn"
);
}
fclose
(fp);
}
C++
#include <bits/stdc++.h>
int
main()
{
FILE
* fp;
char
feedback[100];
char
c;
fp =
fopen
(
"file.txt"
,
"w"
);
c =
fgetc
(fp);
if
(
ferror
(fp)) {
printf
(
"Error in reading from"
" file : file.txtn"
);
}
clearerr
(fp);
if
(
ferror
(fp)) {
printf
(
"Error in reading from "
"file : file.txtn"
);
}
fclose
(fp);
}
Output:
The function perror() stands for print error. In case of an error, the programmer can determine the type of error that has occurred using the perror() function. When perror() is called, then it displays a message describing the most recent error that occurred during a library function call or system call. Its prototype can be given as:
void perror (char*msg);
- The perror() takes one argument which points to an optional user-defined message the message is printed first followed by a colon and the implementation-defined message that describes the most recent error.
- If a call to perror() is made when no error has actually occurred then ‘No error’ will be displayed.
- The most important thing to remember is that a call to perror() and nothing is done to deal with the error condition, then it is entirely up to the program to take action. For example, the program may prompt the user to do something such as terminate the program.
- Usually, the program’s activities will be determined by checking the value of errno and the nature of the error.
- In order to use the external constant errno, you must include the header file ERRNO.H
Below is the program given below illustrates the use of perror(). Here, assume that the file “file.txt” does not exist.
C
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int
main()
{
FILE
* fp;
rename
(
"file.txt"
,
"newfile.txt"
);
fp =
fopen
(
"file.txt"
,
"r"
);
if
(fp == NULL) {
perror
(
"Error: "
);
return
(-1);
}
fclose
(fp);
return
(0);
}
C++
#include <bits/stdc++.h>
#include <errno.h>
int
main()
{
FILE
* fp;
rename
(
"file.txt"
,
"newfile.txt"
);
fp =
fopen
(
"file.txt"
,
"r"
);
if
(fp == NULL) {
perror
(
"Error: "
);
return
(-1);
}
fclose
(fp);
return
(0);
}
Output:
Обработка исключительных ситуаций при работе с файлами
Операции
по открытию файлов могут завершиться
неудачно, например, при ошибке в имени
существующего файла или отсутствии
свободного места на диске, поэтому
рекомендуется всегда контролировать
результаты этих операций. В случае
непредвиденных ситуаций среда выполнения
генерирует различные исключения,
обработку которых следует предусмотреть
в программе, например:
-
FileNotFoundException,
если файла с указанным именем в указанном
каталоге не существует; -
DirectoryNotFoundException,
если не существует указанный каталог; -
ArgumentEception,
если неверно задан режим открытия
файла; -
IOException,
если файл не открывается из-за ошибок
ввода-вывода.
Удобно
обрабатывать наиболее вероятные ошибки
раздельно, чтобы предоставить пользователю
программы в выводимом сообщении наиболее
точную информацию. В приведенном далее
примере отдельно перехватывается ошибка
в имени файла, а затем обрабатывается
все остальные возможные ошибки:
try
{
FileStream
f=new FileStream( @»d:C#text.txt»,
FileMode.Open, FileAccess.Read);
… //действия
над
файлом
f.Close();
}
catch(FileNotFoundException
e)
{
Console.WriteLine(e.Message);
Console.WriteLine(«Проверьте
правильность
имени
файла!
«);
return;
}
catch(Exception
e)
{
Console.WriteLine(«Error:
»
+ e.Message);
return;
}
При
закрытии файла освобождаются все
связанные с ним ресурсы, например, для
файла, открытого для записи, в файл
выгружается содержимое буфера. Поэтому
рекомендуется всегда закрывать файлы
после окончания работы, в особенности
файлы, открытые для записи.
Текстовые файлы
Символьные
потоки — классы StreamWriterиStreamReaderработают сUnicode-символами, следовательно,
ими удобнее всего пользоваться для
работы с файлами, предназначенными для
восприятия человеком. Эти потоки являются
наследниками классовTextWriterиTextReaderсоответственно.
В таблицах 11.5 и 11.6 приведены основные
элементы этих классов.Произвольный
доступдля текстовых файловне
поддерживается.
Таблица
11.5 – Основные элементы класса TextWriter
Элемент Описание
CloseЗакрыть
файл и освободить связанные с ним
ресурсы. Если в процессе
записи
используется буфер, он будет очищен
FlushОчистить
все буферы для текущего файла и записать
накопленные
в
них данные в место их постоянного
хранения. Сам файл при этом
не
закрывается
NewLineИспользуется
для задания последовательности символов,
означающих
начало
новой строки. По умолчанию используется
последовательность
«возврат
каретки» — «перевод строки» (rn)
WriteЗаписать
фрагмент текста в поток
WriteLineЗаписать
строку в поток и перейти на другую строку
Таблица
11.6 — Основные элементы класса TextReader
Элемент Описание
PeekВозвратить
следующий символ, не изменяя позицию
указателя в файле
ReadСчитать
данные из входного потока
ReadBlockСчитать
из входного потока указанное пользователем
количество
символов
и записать их в буфер, начиная с заданной
позиции
ReadLineСчитать
строку из текущего потока и возвратить
ее как значение
типа
string. Пустая строка (null)
означает конец файла(EOF)
ReadToEndСчитать
все символы до конца потока, начиная с
текущей позиции,
и
возвратить считанные данные как одну
строку типа string
Примеры
записи и чтения текстового файла
В
данном примере создается текстовый
файл, в который записываются 2 строки.
Вторая формируется из преобразования
численных значений переменных и
поясняющего текста. Содержимое файла
можно посмотреть в любом текстовом
редакторе. Файл создается в том же
каталоге, куда среда записывает
исполняемый файл. По умолчанию это
каталог …ConsoleApplication1binDebug.
//ЗАПИСЬ
В ТЕКСТОВЫЙ ФАЙЛ
using
System;
using
System.IO;
namespace
ConsoleApplication1
{
class
Class1
{
static
void
Main()
{
try
{ StreamWriter
f=new
StreamWriter(«text.txt»);
f.WriteLine(«Запись
в текстовый файл:»);
double
a =12/234;
int
b=29;
f.WriteLine(«a={0,6:C}
b={1,2:X}»,
a, b);
f.Close();
}
catch
(Exception
e)
{ Console.WriteLine(«Error:»
+e.Message);
Console.ReadKey();
return;
} } }}
Файл,
созданный в предыдущем примере выводиться
на экран.
//ЧТЕНИЕ
ТЕКСТОВОГО ФАЙЛА
using
System;
using
System.IO;
namespace
ConsoleApplication1
{
class
Class1
{
static
void
Main()
{
try
{ StreamReader
f=new
StreamReader
(«text.txt»);
string
s=f.ReadToEnd();
Console.WriteLine(s);
f.Close();
}
catch
(FileNotFoundException
e)
{
Console.WriteLine(e.Message);
Console.WriteLine(«Проверьте
правильность
имени
файла!»);
Console.ReadKey();
return;
}
catch
(Exception
e)
{
Console.WriteLine(«Error:»
+ e.Message);
Console.ReadKey();
return;
} } }}
В
этой программе весь файл считывается
за один приемс помощью методаReadToEnd. Чаще возникает
необходимость считывать файл построчно.
Такой
пример приведен ниже. Каждая строка при
выводе представляется номером.
//Построчное
чтение текстового периода
using
System;
using
System.IO;
namespace
ConsoleApplication1
{
class
Class1
{
static
void
Main()
{
try
{ StreamReader
f=new
StreamReader
(«text.txt»);
string
s;
long
i=0;
while((s=f.ReadLine())
!= null)
Console.WriteLine(«{0}:
{1}»,
++i, s);
f.Close();
}
catch
(FileNotFoundException
e)
{
Console.WriteLine(e.Message);
Console.WriteLine(«Проверьте
правильность имени файла!»);
Console.ReadKey();
return;
}
catch
(Exception
e)
{
Console.WriteLine(«Error:»
+ e.Message);
Console.ReadKey();
return;
} } }}
Соседние файлы в папке Все лекции С#
- #
- #
- #
- #
- #
- #
- #
- #
Довольно часто ошибки могут возникать при чтении данных из файла на C ++ или записи данных в файл. Например, ошибка может возникнуть по следующим причинам:
- При попытке прочитать файл за пределами индикатора.
- При попытке прочитать несуществующий файл.
- При попытке использовать файл, который не был открыт.
- При попытке использовать файл в соответствующем режиме, т. Е. При записи данных в файл, который был открыт для чтения.
- При записи в файл, защищенный от записи, т. Е. При попытке записи в файл, доступный только для чтения.
Если не выполнить проверку на наличие ошибок, программа может вести себя ненормально, поэтому непроверенная ошибка может привести к преждевременному завершению программы или неправильному выводу.
Ниже приведены некоторые функции обработки ошибок во время файловых операций в C / C ++:
феррор ():
В C / C ++ библиотечная функция ferror () используется для проверки наличия ошибки в потоке. Его прототип записывается так:
int ferror (FILE *stream);
Функция ferror () проверяет наличие ошибок в потоке. Он возвращает нулевое значение, если ошибок не произошло, и ненулевое значение, если ошибка есть. Индикация ошибки будет длиться до тех пор, пока файл не будет закрыт, если он не будет очищен функцией clearerr () .
Ниже приведена программа для реализации использования ferror () :
#include <stdio.h>
#include <stdlib.h>
int
main()
{
FILE
* fp;
char
feedback[100];
int
i;
fp =
fopen
(
"GeeksForGeeks.TXT"
,
"w"
);
if
(fp == NULL) {
printf
(
"
The file could "
"not be opened"
);
exit
(1);
}
printf
(
"
Provide feedback on "
"this article: "
);
fgets
(feedback, 100, stdin);
for
(i = 0; i < feedback[i]; i++)
fputc
(feedback[i], fp);
if
(
ferror
(fp)) {
printf
(
"
Error writing in file");
exit
(1);
}
fclose
(fp);
}
C ++
#include <bits/stdc++.h>
int
main()
{
FILE
* fp;
char
feedback[100];
int
i;
fp =
fopen
(
"GeeksForGeeks.TXT"
,
"w"
);
if
(fp == NULL) {
printf
(
"
The file could "
"not be opened"
);
exit
(1);
}
printf
(
"
Provide feedback on "
"this article: "
);
fgets
(feedback, 100, stdin);
for
(i = 0; i < feedback[i]; i++)
fputc
(feedback[i], fp);
if
(
ferror
(fp)) {
printf
(
"
Error writing in file");
exit
(1);
}
fclose
(fp);
}
Выход:
Объяснение: После выполнения этого кода на экране будет отображаться сообщение «Предоставьте отзыв об этой статье:», а после предоставления некоторой обратной связи на экране отобразится сообщение «Процесс завершился через несколько секунд с возвращаемым значением 0».
clearrr ():
Функция clearerr () используется для очистки индикаторов конца файла и ошибок для потока. Его прототип можно представить как:
void clearerr(FILE *stream);
Clearerr () очищает ошибку для потока, на который указывает поток. Функция используется, потому что индикаторы ошибок не очищаются автоматически. После того, как индикатор ошибки для конкретного потока устанавливается, операции на потоке продолжают возвращаться значение ошибки до clearerr (), FSEEK (), fsetpos () или перемотки назад () вызывается.
Ниже приведена программа для реализации использования clearerr () :
C
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int
main()
{
FILE
* fp;
char
feedback[100];
char
c;
fp =
fopen
(
"file.txt"
,
"w"
);
c =
fgetc
(fp);
if
(
ferror
(fp)) {
printf
(
"Error in reading from"
" file : file.txt
");
}
clearerr
(fp);
if
(
ferror
(fp)) {
printf
(
"Error in reading from "
"file : file.txt
");
}
fclose
(fp);
}
C ++
#include <bits/stdc++.h>
int
main()
{
FILE
* fp;
char
feedback[100];
char
c;
fp =
fopen
(
"file.txt"
,
"w"
);
c =
fgetc
(fp);
if
(
ferror
(fp)) {
printf
(
"Error in reading from"
" file : file.txt
");
}
clearerr
(fp);
if
(
ferror
(fp)) {
printf
(
"Error in reading from "
"file : file.txt
");
}
fclose
(fp);
}
Выход:
Функция perror () означает ошибку печати. В случае ошибки программист может определить тип возникшей ошибки с помощью функции perror () . Когда вызывается perror () , он отображает сообщение, описывающее самую последнюю ошибку, которая произошла во время вызова библиотечной функции или системного вызова. Его прототип можно представить как:
void perror (char*msg);
- Perror () принимает один аргумент, который указывает на необязательное определяемое пользователем сообщение, сообщение печатается первым, за которым следует двоеточие и сообщение, определяемое реализацией, которое описывает самую последнюю ошибку.
- Если вызов perror () выполняется, когда на самом деле ошибок не было, то отображается сообщение «Нет ошибки».
- Самая важная вещь, о которой следует помнить, — это то, что при вызове perror () ничего не делается для исправления состояния ошибки, тогда принятие мер полностью зависит от программы. Например, программа может предложить пользователю что-то сделать, например, завершить программу.
- Обычно действия программы определяются путем проверки значения errno и характера ошибки.
- Чтобы использовать внешнюю константу errno , вы должны включить заголовочный файл ERRNO.H
Ниже приведенная ниже программа иллюстрирует использование perror () . Предположим, что файл «file.txt» не существует.
C
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int
main()
{
FILE
* fp;
rename
(
"file.txt"
,
"newfile.txt"
);
fp =
fopen
(
"file.txt"
,
"r"
);
if
(fp == NULL) {
perror
(
"Error: "
);
return
(-1);
}
fclose
(fp);
return
(0);
}
C ++
#include <bits/stdc++.h>
#include <errno.h>
int
main()
{
FILE
* fp;
rename
(
"file.txt"
,
"newfile.txt"
);
fp =
fopen
(
"file.txt"
,
"r"
);
if
(fp == NULL) {
perror
(
"Error: "
);
return
(-1);
}
fclose
(fp);
return
(0);
}
Выход:
Хотите учиться на лучших видео и практических задачах, ознакомьтесь с Базовым курсом C для базового и продвинутого C.
Признак достижения конца файла выставляется только после неудачной попытки чтения за его концом. Поэтому, если в теле цикла нет проверки, успешно ли выполнено чтение из файла — последнее чтение окажется именно тем неудачным чтением, которое выставит признак достигнутого конца файла (а для вас это будет выглядеть как, например, еще раз считанная последняя строка, если она находилась в буфере для чтения).
Лучше в заголовке цикла while
выполнять само чтение с проверкой — например, в программе на C это могло бы выглядеть как
while(fread(&data,sizeof(data),1,file)==1)
{
// Обработка считанных данных
}
/* Или, скажем,
while(fgets(buf, bufsize, file)) { .... }
*/
if (feof(file)) // Достигнут конец файла
puts("Ошибка чтения: достигнут конец файла");
else if (ferror(file)) {
puts("Ошибка чтения файла");
или на C++ — наподобие
for(int n; file >> n; )
{
// Обработка считанного значения n
}
if (file.bad())
std::cout << "Ошибка ввода-вывода при чтенииn";
else if (file.eof())
std::cout << "Достигнут конец файлаn";
else if (file.fail())
std::cout << "Неверный формат данныхn";
Лабораторные работы си шарп и задания по теме «Язык программирования c sharp: файлы»
Содержание:
- Теория
- Классификация файлов
- Операции с файлами
- Классы и пространства имен .NET
- Основные методы класса File
- Конструкция try .. catch
- ИнструкцияUsing
- Задания и лабораторные работы
- Использование режима для создания файла и работы с ним без класса BinaryReader
- Обработка ошибок при работе с файлами. Оператор Try. Считывание данных из файла
Теория
- Компонентный тип:
- текстовые файлы (состоит из строк в некоторой кодировке)
- бинарные файлы
- Тип доступа к файлу:
- Произвольный доступ: время доступа = Θ(1), компоненты данных имеют одинаковый размер
(байты в простейшем случае) - Последовательный доступ: компоненты данных имеют различный размер, для поиска
i
-го компонента необходимо считать предыдущийi-1
компонент (пример: текстовые файлы) - Типы операций с файлами:
- только для чтения
- только для записи
- чтение и запись
- using System.IO;
- using System.Text;
- File – статический класс, операции с закрытыми файлами
- FileMode – режим открытия файла
- FileStream
- StreamReader/StreamWriter – потоки для чтения-записи в текстовые файлы
- BinaryReader/BinaryWriter — потоки для чтения-записи в двоичные файлы
- Encoding – кодировки файлов
- Классы
File
иFileInfo
оба предоставляют методы для создания, копирования, удаления, перемещения и открытия файлов. Они имеют очень похожие интерфейсы, разница лишь в том, чтоFileInfo
обеспечивает методы экземпляра, тогда какFile
обеспечивает методы static. - Разница в том, что если у вас будет небольшое количество операций, удобнее обращаться к статическим методам класса
File
. Если у вас будет много операций, более эффективно создать классFileInfo
и получить доступ ко всем его методам экземпляра. - Класс
Directory
предоставляет статические методы, в то время какDirectoryInfo
предоставляет методы экземпляра. - У нас также есть класс
Path
, который предоставляет методы для работы со строкой, содержащей информацию о пути к файлу или каталогу. - FileMode.Create
- FileMode.Open
- FileMode.OpenOrCreate
- FileMode.Append
- Инструкция
Using
обеспечивает автоматический вызов методаClose()
при работе с классомFileStream
.
Классификация файлов
Операции с файлами
с закрытыми файлами: открыть проверить существует ли удалить скопировать переименовать переместить |
С уже открытыми файлами: закрыть записать информацию читать информацию проверить, достигнут ли конец файла поиск n-й позиции |
Классы и пространства имен .NET
-
Пространства имен:
В платформе .net есть пространство имен System.IO
, именно там расположено множество классов для работы с файлами и каталогами.
Классы:
FileMode
тип перечисления
Основные методы класса File
-
Operations with closed files:
using System.IO; // File.Exists("a.dat") Console.WriteLine(File.Exists("a.dat")); // true или false // File.Exists("d:a.dat") Console.WriteLine(File.Exists("d:a.dat")); // true или false // File.Exists(@"d:a.dat") Console.WriteLine(File.Exists(@"d:a.dat"); // true или false File.Delete("a.dat"); File.Copy("d:a.dat", "d:b.dat"); // будет создан файл b.dat, если он существует - возникнет исключение File.Copy("a.dat", @"d:a.dat"); File.Move("a.dat", @"..a.dat");
Конструкция try .. catch
try { // код с возможным исключением } catch (Exception e) { // для обработки исключения }
Пример:
try { var path = @"d:a.dat"; var fs = new FileStream(path, FileMode.Open); ... } catch (FileNotFoundException e) { Console.WriteLine($"Файл с именем {e.FileName} не найден"); } catch (FileLoadException e) { Console.WriteLine($"К файлу с именем {e.FileName} невозможно получить доступ"); }
ИнструкцияUsing
Пример работы с двоичными файлами:
using System.IO; // ... string path = @"Lesson14.dat"; // данные в файле: 1 2 3 4 var fs = new FileStream(path, FileMode.Open); var br = new BinaryReader(fs); var a=br.ReadInt32(); Console.WriteLine(a); // 1 var b = br.ReadInt32(); Console.WriteLine(b); // 2 var c = br.ReadInt32(); Console.WriteLine(c); // 3 fs.Close();
Example:
var path = @"d:a.dat"; using (var fs = new FileStream(path, FileMode.Create)) { fs.WriteByte(1); fs.WriteByte(2); fs.WriteByte(3); } using (var fs = new FileStream(path, FileMode.Open)) { Console.WriteLine(fs.ReadByte()); Console.WriteLine(fs.ReadByte()); Console.WriteLine(fs.ReadByte()); }
Задания и лабораторные работы
Использование режима для создания файла и работы с ним без класса BinaryReader
Задание 0:
Выполнить: Create a file with a name myFile.txt
on your root folder d
or c
(@"D:myFile.txt"
). Open created file and write there some entered string (don’t forget to convert it into a byte array -> Encoding.Default.GetBytes()
). Then, read the file’s data and output it to the console window (by convering data again into string -> Encoding.Default.GetString(...)
).
Создайте файл с именем myFile.txt
в вашей корневой папке d
или c
(@"D:myFile.txt"
). Откройте созданный файл и запишите туда некоторую введенную строку (не забудьте преобразовать ее в байтовый массив -> Encoding.Default.GetBytes()
). Затем считайте данные файла и выводите их в окно консоли (путем повторного преобразования данных в строку -> Encoding.Default.GetString(...)
).
Примерный вывод:
Введите строку: Hello, world! Строка из файла: Hello, world!
[Название проекта: Lesson_14Task0
, Название файла L14Task0.cs
]
Обработка ошибок при работе с файлами. Оператор Try. Считывание данных из файла
Лабораторная работа 1. Ошибка доступа на чтение к несуществующему файлу. Считывание данных из файла
Выполнить:
- Создайте программу для вывода содержимого файла в окно консоли (числа могут быть выведены через пробелы в строку). Вы должны использовать класс
BinaryReader
. - Скачайте файл L01.dat и поместите его в свою рабочую папку (
~/Lesson_14 Lab1/bin/Debug
). В файле записано 15 целых чисел.. - Вы должны использовать инструкцию
using
при работе с уже открытым файлом. Тогда, независимо от успеха работы с открытым файлом, он всегда будет закрыт. - Используйте код для обработки исключений:
- Чтобы предотвратить «сбой» программы и не напугать пользователя, добавьте обработку исключений — блок
try..catch
. Операторusing
должен находиться внутри блокаtry..catch
.
... try { ... } catch (IOException e) { Console.WriteLine($"Ошибка чтения из файла: {e.Message}"); }
- Чтобы предотвратить «сбой» программы и не напугать пользователя, добавьте обработку исключений — блок
- Измените имя файла в программном коде на несуществующее. Запустите приложение и проверьте обработку исключения. Снова измените имя на правильное имя.
- Здесь нет необходимости создавать методы.
using (var fs = new FileStream(path, FileMode.Create)) { ... }
Примерный вывод:
Данные из файла: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +++++++++++++ Данные из файла: Ошибка чтения из: Файл 'C:UsersUserDocumentsVisual Studio 2015ProjectsL14_Lab1binDebugL02.dat' не найден
[Название проекта: Lesson_14Lab1
, название файла L14Lab1.cs
]
✍ Алгоритм:
- Создайте новый проект с именем файла, как указано в задании.
- Загрузите файл L01.dat и поместите его в свою рабочую папку (
~/Lesson_14Lab1/bin/Debug
). - Чтобы избежать необходимости постоянно упоминать класс
Console
, мы можем включить его объявление прямо в начале окна редактора: - Ниже этого кода подключите пространство имен
System.IO
— для работы с файлами: - В функции
Main
объявите переменную для хранения пути к файлу. Лучше использовать дословную строку (символ@
): - Чтобы прочитать что-то из файла, сначала нужно открыть файл. Чтобы открыть файл, требуется указать путь к файлу в системе и режим.
- Итак, нам нужно инициализировать новый экземпляр класса
FileStream
с указанным путем и режимом. Будем использовать режимopen
(для чтения из файла), потому что нам не нужно ничего записывать в файл. Переменнаяfs
будет хранить входной поток: - После этого нам нужно создать переменную (
br
) — экземпляр классаBinaryReader
, чтобы иметь возможность считывать данные из файла в виде двоичных значений: - Чтобы не возникало ошибки, если файл не существует или если имя файла написано неверно, мы будем использовать блок
try...catch
. Поместите добавленные инструкцииusing
в блокtry
: - Кроме того, внутри блока
try
мы собираемся считывать байты или символы из файла и выводить их в консоль. Для этой цели будем использовать методPeekChar
классаBinaryReader
: - В блоке
catch
мы собираемся распечатать сообщение в случае возникновения какой-либо ошибки, например, в случае неправильного имени файла: - В конце программного кода поместите метод
ReadKey
, чтобы не закрывать окно консоли при запуске программы в режиме отладки: - Нажмите клавишу
F5
, чтобы запустить программу в режиме отладки и проверить выходные данные. - Чтобы увидеть результат при возникновении ошибки, измените имя файла, например:
- Нажмите клавишу
F5
, чтобы запустить программу в режиме отладки и проверить выходные данные. - Верните прежнее название файла
- Загрузите файл
.cs
на проверку.
... using static System.Console; ...
using System.IO; ...
string path = @"L01.dat";
using (var fs = new FileStream(path, FileMode.Open))
Класс FileStream
предоставляет поток (последовательность байтов) для ввода/считывания из файла; поддерживает как синхронные, так и асинхронные операции чтения и записи.
... using (var br = new BinaryReader(fs))
... try { using (var fs = new FileStream(path, FileMode.Open)) using (var br = new BinaryReader(fs)) }
// в блоке try, после использования инструкций using: while (br.PeekChar() != -1) { var x = br.ReadInt32(); Write($"{x} "); }
BinaryReader
класс считывает примитивные типы данных как двоичные значения в определенной кодировке.
PeekChar
метод возвращает следующий доступный символ или -1
, если больше нет доступных символов или поток не поддерживает поиск.
// under try block: catch (IOException e) { WriteLine($"Ошибка чтения из файла: {e.Message}"); }
Класс IOException
хранит исключение, которое генерируется при возникновении ошибки ввода-вывода.
Message
— это атрибут класса Exception
, который возвращает сообщение об ошибке, объясняющее причину исключения, или пустую строку.
// после закрывающей скобки блока catch
ReadKey();
Метод ReadKey
ожидает ввода или нажатия клавиши клавиатуры. Нажатая клавиша отображается в окне консоли.
string path = @"L02.dat";
Задание 1:
Выполнить: Скопируйте файл L01.dat
для предыдущей лабораторной работы и вставьте его в папку текущего задания, пусть к файлу: (~/Lesson_14Task1/bin/Debug
).
1. Создайте программу для вывода чисел в квадрате из файла в окно консоли (числа могут быть выведены через пробел в пределах одной строки), а также для вычисления количества четных цифр среди чисел. Вы должны использовать класс BinaryReader
. Создайте программу, используя блок try..catch
.
Указание: Здесь нет необходимости создавать методы.
Примерный вывод:
Квадраты чисел: 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 Количество четных: 7 +++++++++++++++ Ошибка чтения из файла: Файл 'c:usersuserdocumentsvisual studio 2015ProjectsL_14Task1binDebugL02.dat' не найден.
[Название проекта: Lesson_14Task1
, название файла L14Task1.cs
]
Лабораторная работа 2. Несколько блоков try..catch. Считывание из файла
Выполнить:
- Загрузите файл в папку с проектом L01.dat. В файле записаны целые числа. Выведите эти числа в окно консоли по
N
чисел в каждой строке (N
вводится (N > 0
) и имеет значение по умолчанию =10
). Необходимо использовать классBinaryReader
. В случае если файл пустой выведите сообщение «пустой файл». - Может быть несколько блоков
try..catch
, и каждый из них может обрабатывать разные ошибки. Напишите свой код так, чтобы один блокtry..catch
обрабатывал ошибки, возникающие при открытии файла, а другой блок обрабатывал ошибки, возникающие при работе с уже открытым файлом. - Тогда структура программы должна быть следующей:
- Создайте метод с двумя параметрами для выполнения задачи. Сигнатура метода:
- Проверьте, правильно ли работают блоки
try..catch
. Смоделируйте ошибку при работе с уже открытым файлом и ошибку с именем файла. После проверки верните предыдущий (правильный) код и закомментируйте неверный код.
... try { using ( ... ) using ( ... ) { try { // работа с открытым файлом... } catch { // обработка ошибки при работе с уже открытым файлом } } catch { // обработка ошибки, связанной с открытием файла }
static void PrintFileInRows(string path, int N=10)
Примерный вывод:
Введите N: 3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +++++++++++++ Введите N: 3 Ошибка чтения из файла: Файл 'c:usersuserdocumentsvisual studio 2015ProjectsL_14Lab2binDebugL02.dat' не найден. +++++++++++++ Введите N: 3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Ошибка работы с данными файла: Чтение после конца потока невозможно.
[Название проекта: Lesson_14Lab2
, название файла L14Lab2.cs
]
✍ Алгоритм:
- Создайте новый проект с именем и именем файла, как указано в задаче.
- Загрузите файл L01.dat и разместите его в рабочей папке проекта (
~/Lesson_14Lab2/bin/Debug
). - Чтобы не печатать постоянно класс
Console
, мы можем включить его объявление прямо в начале окна редактора: - Подключите пространство имен <код>System.IO для работы с файлами:
- В функции
Main
объявите переменную для хранения пути к файлу. Лучше использовать дословную строку (символ@
): - Мы собираемся вывести числа из файла в нескольких строках: <код> N чисел в каждой строке. В функции main нам нужно запросить пользователя число <код>N:
- Затем необходимо объявить метод с именем
PrintFileInRows
с двумя параметрами — путь к файлу и количествоN
: - Чтобы прочитать что-то из файла, сначала нужно открыть файл. Но в соответствии с требованием задачи у нас должно быть два блока
try..catch
, один из них обрабатывает ошибки, возникающие при открытии файла, а другой блок обрабатывает ошибки, возникающие при работе с уже открытым файлом. - Итак, давайте сначала создадим блоки
try..catch
, а затем поместим в них остальной код: - Чтобы открыть файл, необходимо указать путь к файлу в вашей системе и режим открытия — как операционная система должна открывать файл. Поместите следующий код после открытой фигурной скобки первого блока
try..catch
: - После этого мы собираемся работать с файлом, это означает, что мы будем считывать некоторые данные из файла и что-то с ними делать. Итак, нам нужно создать переменную (
br
) как экземпляр классаBinaryReader
, чтобы иметь возможность считывать данные из файла в виде двоичных значений. Следующий код должен быть помещен во второй блокtry..catch
, который находится внутри инструкцииusing
: - В соответствии с требованиями задачи мы должны вывести сообщение «пустой файл», если в файле нет никаких данных. Лучше использовать метод
PeekChar
, который возвращает-1
, если файл пуст: - После этого мы будем считывать числа одно за другим из файла и выводить их в консоль. Необходимо выводить <код>N чисел в каждой строке. Используем счетчик
i
, чтобы подсчитать, сколько чисел мы уже прочитали. Если количество считанных чисел равноN
, то мы перейдем к следующей строке: - В первом блоке
catch
мы собираемся распечатать сообщение в случае возникновения какой-либо ошибки при работе с файлом: - Во втором блоке
catch
мы собираемся распечатать сообщение в случае возникновения какой-либо ошибки при открытии файла: - В конце кода программы поместите метод
ReadKey
, чтобы не закрывать окно консоли при запуске программы в режиме отладки: - Нажмите F5, чтобы запустить программу в режиме отладки и проверить выходные данные.
- Чтобы увидеть результат при возникновении ошибки, измените имя файла, например:
- Нажмите F5, чтобы запустить программу в режиме отладки и проверить выходные данные.
- Верните правильное имя файлу.
- Чтобы увидеть результат при возникновении ошибки во время работы с файлом, добавьте следующую строку после инструкции while:
- Нажмите F5, чтобы запустить программу в режиме отладки и проверить выходные данные.
- Прокомментируйте неправильную строку кода.
- Загрузите файл
.cs
в систему moodle.
... using static System.Console; ...
using System.IO; ...
string path = @"L01.dat";
... WriteLine($"Введите N:"); var n = int.Parse(ReadLine());
// после закрывающей фигурной скобки основной функции static void PrintFileInRows(string path, int n = 10) { ... }
Метод ничего не вернет, поэтому используем ключевое слово void
.
// в фигурных скобках метода try { /* здесь будет код, в котором могут возникать ошибки при открытии файла*/ { try { /* здесь будет код, в котором могут возникать ошибки при работе с уже открытым файлом*/ } catch (IOException e) { // сообщение об ошибке при работе с файлом } } } catch (IOException e) { // сообщение об ошибке при открытии файла } }
using (var f = File.Open(path, FileMode.Open)) { ... }
// после открытой фигурной скобки оператора using try { var br = new BinaryReader(f);
if (br.PeekChar() == -1) { WriteLine("пустой файл"); }
// после предыдущего оператора if int i = 0; while (br.PeekChar() != -1) { if (i >= n) { WriteLine(); i = 0; } Write(br.ReadInt32() + " "); i++; }
// within the first catch block: catch (IOException e) { WriteLine($"Ошибка работы с файлом: {e.Message}"); }
Класс IOException
хранит исключение, которое генерируется при возникновении ошибки ввода-вывода.
Message
— это атрибут класса Exception
, который возвращает сообщение об ошибке, объясняющее причину исключения, или пустую строку («»).
// в пределах первого блока catch: catch (IOException e) { Console.WriteLine($"Ошибка считывания из файла: {e.Message}"); }
// после закрывающей скобки блока фиксации:
ReadKey();
Метод ReadKey
получает следующий символ или функциональную клавишу, нажатую пользователем. Нажатая клавиша отображается в окне консоли.
string path = @"L02.dat";
// после закрытия фигурной скобки оператора while: WriteLine(br.ReadInt32());
В этой строке кода мы пытаемся прочитать из файла, когда мы уже прочитали все.
Задание 2:
Выполнить: Загрузите файл Task02.dat и вставьте его в папку с проектом: (~/Lesson_14Task2/bin/Debug
). В файле записаны целые числа. Создайте программу, которая сначала выведет эти числа в окно консоли, а после этого подсчитает количество чисел, которые меньше, чем их сосед слева (меньше, чем предыдущее число в последовательности чисел).
Указание 1: Создайте метод для печати данных из файла:
static void PrintData(string path)
Указание 2: Создайте метод для вывода количества чисел, меньших, чем их сосед слева:
static int CountLessThanLeft(string path)
Указание 3: Возможно, потребуется использовать определенную кодировку символов при чтении из файла:
using (var fs = new FileStream(path, FileMode.Open)) using (var br = new BinaryReader(fs, Encoding.ASCII))
Указание 4: Может быть несколько блоков try..catch
, и каждый из них может обрабатывать разные ошибки. Напишите свой код так, чтобы один блок try..catch
обрабатывал ошибки, возникающие при открытии файла, а другой блок обрабатывал ошибки, возникающие при работе с уже открытым файлом.
Тогда структура программы должна быть следующей:
... try { using ( ... ) using ( ... ) { try { //работа с открытым файлом... } catch { // обработка ошибки при работе с уже открытым файлом } } catch { // обработка ошибки открытия файла }
Указание 5: Проверьте, правильно ли работают блоки try..catch
. Смоделируйте ошибку при работе с уже открытым файлом и ошибку с именем файла. После проверки верните предыдущий (правильный) код и закомментируйте неправильный.
Примерный вывод:
Числа в файле: -35 25 28 -37 13 -9 -24 12 -35 37 -24 20 29 -11 -45 -8 43 12 12 44 Количество чисел, меньших, чем их левый сосед: 8 +++++++++++++++ Числа в файле: -35 25 28 -37 13 -9 -24 12 -35 37 -24 20 29 -11 -45 -8 43 12 12 44 Ошибка работы с файлом: Чтение после конца потока невозможно. Количество чисел, меньших, чем их левый сосед: 8 +++++++++++++++
[Название проекта: Lesson_14Task2
, Название файла L14Task2.cs
]
Содержание
- Исключения (Exceptions) и инструкция try
- Оговорка catch
- Блок finally
- Инструкция using
- Выбрасывание исключений
- Основные свойства System.Exception
- Основные типы исключений
- Директивы препроцессора
- Pragma Warning
- Атрибут Conditional
- Классы Debug и Trace
- TraceListener
- Fail и Assert
Исключения, их обработка, и некоторые другие моменты, связанные с ошибками в приложении на C#.
Исключения (Exceptions) и инструкция try
Инструкция try
отмечает блок кода как объект для обработки ошибок или очистки. После блока try
обязательно должен идти либо блок catch
, либо блок finally
, либо они оба. Блок catch
выполняется, когда внутри блока try возникает ошибка. Блок finally
выполняется после того, как прекращает выполнять блок try
(или, если присутствует, блок catch
), независимо от того, выполнился ли он до конца или был прерван ошибкой, что позволяет выполнить так называемый код очистки.
Блок catch
имеет доступ к объекту исключения (Exception
), который содержит информацию об ошибке. Блок catch
позволяет обработать исключительную ситуацию и как-либо скорректировать ошибку или выбросить новое исключение. Повторное выбрасывание исключения в блоке catch
обычно применяется с целью логирования ошибок или чтобы выбросить новое, более специфическое исключение.
Блок finally
добавляет в программу прогнозируемость, позволяя выполнить определенный код при любых обстоятельствах. Это может быть полезно для выполнения операций очистки, например, закрытия сетевого подключения и т.д.
В целом конструкция try выглядит следующим образом:
try { ... // в пределах этого блока может быть выброшено исключение } catch (ExceptionA ex) { ... // обработчик исключений типа ExceptionA } catch (ExceptionB ex) { ... // обработчик исключений типа ExceptionB } finally { ... // код очистки } |
Например, следующий код выбросит ошибку DivideByZeroException
(поскольку делить на ноль нельзя) и наша программа завершить досрочно:
int x = 3, y = 0; Console.WriteLine (x / y); |
Чтобы этого избежать можно использовать конструкцию try
:
try { int x = 3, y = 0; Console.WriteLine (x / y); } catch (DivideByZeroException ex) { Console.Write («y cannot be zero. «); } // выполнение программы продолжится отсюда |
Обработка исключений довольно ресурсоёмкая операция, поэтому на практике для таких случаев как в примере ее лучше не использовать (лучше непосредственно перед делением проверить делить на равенство нулю).
Когда выбрасывается исключение, CLR проверяет выброшено ли оно непосредственно внутри блока try
, который может обработать данное исключение. Если да, выполнение переходит в соответствующий блок catch
. Если блок catch
успешно завершается, выполнение переходит к следующей после блока try
инструкции (если имеется блок finally
, то сначала выполняется он). Если же исключение выброшено не внутри блока try
или конструкция try
не содержит соответствующего блока catch
, выполнение переходит в точку вызова метода (при этом сначала выполняется блок finally
), и проверка повторяется снова.
Если не одна функция в стэке вызовов не способна обработать исключение, ошибка выводиться пользователю и программа завершается досрочно.
Оговорка catch
В оговорке catch указывается какой тип исключения она должна перехватывать. Это может быть либо System.Exception
, либо его производный класс. Перехватывая непосредственно System.Exception
, мы перехватим все возможные ошибки. Это может быть полезно в нескольких случаях:
- программа потенциально должна и может продолжить работать несмотря на ошибки любых типов
- исключение будет выброшено повторно в блоке
catch
, например, после логирования ошибок - блок
catch
является последним в очереди, способным предотвратить аварийное завершение программы
Однако обычно перехватываются исключения более специфического типа, чтобы избежать ситуации, когда обработчику ошибки придется иметь дело с исключением, для которого он не предназначен (например, OutOfMemoryException
).
Можно обработать несколько типов исключений с помощью нескольких оговорок catch:
try { DoSomething(); } catch (IndexOutOfRangeException ex) { ... } catch (FormatException ex) { ... } catch (OverflowException ex) { ... } |
Каждая оговорка способна обработать только то исключение, которое точно совпадает с ее типом. Для одного выброшенного исключения может быть выполнена только одна оговорка catch. Обрабатываются блоки catch в том порядке, в котором они идут в коде. В этой связи более специфические исключения должны перехватываться раньше чем более общие.
Исключение может быть перехвачено и без указания переменной, если не нужен доступ к ее членам:
catch (StackOverflowException) // без переменной { ... } |
Более того, в оговорке catch можно опустить и переменную и тип исключения — такая оговрка будет перехватывать все исключения:
Блок finally
Блок finally
выполняется всегда, независимо от того выброшено исключение или нет. Блок finally
обычно содержит код очистки.
Блок finally
выполняется в следующих случаях:
- после завершения блока
catch
- если выполнение блока
try
прервано jump-инструкциями:return
,goto
и т.д. - после выполнения блока
try
полностью, если исключений так и не было выброшено
Блок finally
делает программу более прогнозируемой. Например, в следующем примере открываемый файл в итоге всегда будет закрыт, независимо от того, завершиться ли блок try
без ошибок, или будет прерван выброшенным исключением, или сработает инструкция return
если файл окажется пустым:
static void ReadFile() { StreamReader reader = null; try { reader = File.OpenText («file.txt»); if (reader.EndOfStream) return; Console.WriteLine (reader.ReadToEnd()); } finally { if (reader != null) reader.Dispose(); } } |
В пример для закрытия файла вызывается метод Dispose
. Использование этого метода внутри блока finally
является стандартной практикой. C# даже позволяет заменить всю конструкцию инструкцией using
.
Инструкция using
Многие классы инкапсулируют неуправляемые ресурсы, такие как дескриптор файла, соединение с базой данных и т.д. Эти классы реализуют интерфейс System.IDisposable
, который содержит единственный метод без параметров Dispose
, освобождающий соответствующие машинные ресурсы. Инструкция using
предусматривает удобный синтаксис вызова метода Dispose
для объектов реализующих IDisposable
внутри блока finally
:
using (StreamReader reader = File.OpenText («file.txt»)) { ... } |
Что эквивалентно следующей конструкции:
StreamReader reader = File.OpenText («file.txt»); try { ... } finally { if (reader != null) ((IDisposable)reader).Dispose(); } |
Выбрасывание исключений
Исключение может быть выброшено автоматически во время выполнения программы либо явно в коде программы с помощью ключевого слова throw
:
static void Display (string name) { if (name == null) throw new ArgumentNullException («name»); Console.WriteLine (name); } |
Также исключение может быть выброшено повторно внутри блока catch
:
try { ... } catch (Exception ex) { // логирование ошибки ... throw; // повторное выбрасывание того же самого исключения } |
Такой подход позволяет заносить ошибки в лог без их дальнейшего поглощения. Также это позволяет уклониться от обработки неожиданных исключений.
Если throw
заменить на throw ex
, то пример по прежнему будет работать, но свойство исключения StackTrace
не будет отражать исходную ошибку.
Другой распространенный сценарий использования повторного выбрасывания исключения — повторное выбрасывание более специфического и конкретного типа исключения, чем было перехвачено ранее:
try { ... // парсинг даты рождения из xml-данных } catch (FormatException ex) { throw new XmlException («Неправильная дата рождения», ex); } |
В таких случаях необходимо передать исходное исключение в качестве первого параметра конструктора нового исключения, ссылка на объект исходного исключения позже будет доступна через свойство InnerException
внутреннего исключения.
Основные свойства System.Exception
К наиболее важным свойствам класса System.Exception
можно отнести:
StackTrace
— строка, представляющая все методы, которые были вызваны, начиная с того, в котором было выброшено исключение, и заканчивая тем, в котором содержится блокcatch
, перехвативший исключение;Message
— строка с описанием ошибки;InnerException
— содержит ссылку на объектExeption
, который вызвал текущее исключение (например, при повторном выбрасывании исключения).
Основные типы исключений
Следующие типы исключений являются наиболее распространенными в среде CLR и .NET Framework. Их можно выбрасывать непосредственно или использовать как базовые классы для пользовательских типов исключений.
System.ArgumentException
— выбрасывается при вызове функции с неправильным аргументом.System.ArgumentNullException
— производный отArgumentException
класс, выбрасывается если один из аргументов функции неожиданно равенnull
.System.ArgumentOutOfRangeException
— производный отArgumentException
класс, выбрасывается когда аргумент функции имеет слишком большое или слишком маленькое значение для данного типа (обычно касается числовых типов). Например, такое исключение будет выброшено если попытаться передать отрицательное число в функцию, которая ожидает только положительные числа.System.InvalidOperationException
— выбрасывается когда состояние объекта является неподходящим для нормального выполнения метода, например, при попытке прочесть не открытый файл.System.NotSupportedException
— выбрасывается, когда запрошенный функционал не поддерживается, например, если попытаться вызвать методAdd
для коллекции доступной только для чтения (свойство коллекцииIsReadOnly
возвращаетtrue
).System.NotImplementedException
— выбрасывается, когда запрошенный функционал еще не реализован.System.ObjectDisposedException
— выбрасывается при попытке вызвать метод объекта, который уже был уничтожен (disposed).
Директивы препроцессора
Директивы препроцессора снабжают компилятор дополнительной информацией об областях кода. Самые распространенные директивы препроцессора — условные директивы, позволяющие включить или исключить области кода из компиляции.
#define DEBUG class MyClass { int x; void Foo() { # if DEBUG Console.WriteLine («Testing: x = {0}», x); # endif } } |
В этом классе инструкции в методе Foo
скомпилируются если определен символ DEBUG
, а если его удалить — инструкции не скомпилируются. Символы препроцессора могут быть определены в исходном коде (как в примере), а могут быть переданы компилятору в командной строке с помощью параметра /define:symbol
.
С директивами #if
и #elif
можно использовать операторы ||
, &&
и !
с несколькими символами:
Директивы #error
и #warning
предотвращают некорректное использование условных директив, заставляя компилятор генерировать предупреждение или ошибку при передаче неверного набора символов.
Директивы препроцессора схожи с условными конструкциями и статическими переменными, однако дают возможности, недоступные для последних:
- условное включение атрибута
- изменение типа, объявляемого для переменной
- переключение между разными пространствами имен или псевдонимами типа в директиве using:
using TestType =
#if V2
MyCompany.Widgets.GadgetV2;
#else
MyCompany.Widgets.Gadget;
#endif
- создавать новые версии кода и быстро переключаться между ними при компиляции
- создавать библиотеки, компилируемые для разных версий .NET Framework
Полный список директив препроцессора:
#define symbol
— определяет символ#undef symbol
— удаляет символ#if symbol [оператор symbol2]...
— условная компиляция; допустимые операторы==
,!=
,&&
и||
#else
— выполняет код после#endif
#elif symbol [оператор symbol2]
— объединяет#else
и#if
#endif
— конец условных директив#warning text
— текст предупреждения, которое появится в выдаче компилятора#error text
— текст ошибки, которая появится в выдаче компилятора#line [число["файл"] | hidden]
— число указывает номер строки в исходном коде; файл — имя файла, которое появится в выдаче компилятора; hidden — дает указание дебагеру пропустить код от этой точки до следующей директивы#line
#region name
— отмечает начало области#endregion
— отмечает конец области#pragma warning
Pragma Warning
Компилятор генерирует предупреждения, когда что-то в коде ему кажется неуместным (но корректным). В отличии от ошибок предупреждения не препятствуют компиляции программы. Предупреждения компилятора могут быть очень полезны при поиске багов в программе. Однако часто предупреждения оказываются ложными, поэтому целесообразно иметь возможность получать предупреждения только о действительных багах. С этой целью компилятор дает возможность выборочно подавить предупреждения с помощью директивы #pragma warning
.
public class Foo { static void Main() { } #pragma warning disable 414 static string Message = «Hello»; #pragma warning restore 414 } |
В примере мы указываем компилятору не выдавать предупреждения о том, что поле Message
не используется.
Если не указывать номер директива #pragma warning
отменит или восстановит вывод всех предупреждений.
Если скомпилировать программу с параметром /warnaserror
, то все не отмененные директивой #pragma warning
предупреждения будут расцениваться компилятором как ошибки.
Атрибут Conditional
Атрибут Conditional
указывает компилятору на необходимость игнорировать все обращения к определенному классу или методу, если заданный символ не был определен:
[Conditional («LOGGINGMODE»)] static void LogStatus (string msg) { ... } |
Это равносильно тому, что каждый вызов метода будет окружен условными директивами:
#if LOGGINGMODE LogStatus («Message Headers: « + GetMsgHeaders()); #endif |
Классы Debug и Trace
Статические классы Debug
и Trace
предлагают базовые возможности логирования. Оба класса схожи, отличие заключается в их назанчении. Класс Debug
предназначен для отладочных сборок, класс Trace
— для отладочных и финальных. В связи с этим все методы класса Debug
определены с атрибутом [Conditional("DEBUG")]
, а методы класса Trace
— с атрибутом [Conditional("TRACE")]
. Это значит, что все обращения к Debug
и Trace
будут подавляться компилятором, пока не определен символ DEBUG
или TRACE
.
Класс Debug
и Trace
определяют методы Write
, WriteLine
и WriteIf
. По умолчанию они отправляют сообщения в окно вывода отладчика:
Debug.Write («Data»); Debug.WriteLine (23 * 34); int x = 5, y = 3; Debug.WriteIf (x > y, «x is greater than y»); |
Класс Trace
также содержит методы TraceInformation
, TraceWarning
и TraceError
. Их действия зависят от зарегистрированных прослушивателей.
TraceListener
Классы Debug
и Trace
имеют свойство Listeners
, которое представляет собой статическую коллекцию экземпляров TraceListener
. Они отвечают за обработку данных, возвращаемых методами Write
, Fail
и Trace
.
По умолчанию коллекция Listeners
обоих классов включает единственный прослушиватель — DefaultTraceListener
— стандартный прослушиватель, имеющий две ключевые возможности:
- при подключении к отладчику (например, Visual Studio) сообщения записываются в окно вывода отладчика, во всех остальных случаях сообщения игнорируются
- при вызове метода
Fail
отображается диалоговое окно, запрашивающее у пользователя дальнейшие действия: продолжить, прервать или повторить отладку (независимо от того, подключен ли отладчик)
Это поведение можно изменить или дополнить, удалив (на обязательно) стандартный прослушиватель и/или добавив один или более собственных прослушивателей.
Прослушиваетли трассировки можно написать с нуля (создав производный класс от TraceListener
) или воспользоваться готовыми классами:
TextWriterTraceListener
записывает вStream
илиTextWriter
или добавляет в файл; имеет четыре подкласса:ConsoleTraceListener
,DelimitedListTraceListener
,XmlWriterTraceListener
иEventSchemaTraceListener
EventLogTraceListener
записывает в журнал событий WindowsEventProviderTraceListener
записывает в систему трассировки событий Windows (Event Tracing for Windows — ETW)WebPageTraceListener
выводит на веб-страницу ASP.NET
Ни один из этих прослушивателе не отображает диалоговое окно при вызове Fail
, это делает только DefaultTraceListener
.
// Удалить стандартный прослушиватель, очистив коллекцию прослушивателей: Trace.Listeners.Clear(); // Добавить средство записи в файл trace.txt: Trace.Listeners.Add (new TextWriterTraceListener («trace.txt»)); // Добавит средство записи в консоль: System.IO.TextWriter tw = Console.Out; Trace.Listeners.Add (new TextWriterTraceListener (tw)); // Добавить средство записи в журнал событий Windows: if (!EventLog.SourceExists («DemoApp»)) EventLog.CreateEventSource («DemoApp», «Application»); Trace.Listeners.Add (new EventLogTraceListener («DemoApp»)); |
В случае журнала событий Windows сообщения, отправляемые с помощью Write
, Fail
или Assert
, записываются как сведения, а сообщения методов TraceWarning
и TraceError
записываются как предупреждения или ошибки.
Каждый экземпляр TraceListener
имеет свойство Filter
и TraceFilter
, с помощью которых можно управлять, будет ли сообщение записано в этот прослушиватель. Для этого необходимо создать экземпляр классов EventTypeFilter
или SourceFilter
(производных от TraceFilter
) или создать свой класс, наследующий от TraceFilter
и переопределить в нем метод ShouldTrace
.
В TraceListener
также определены свойства IndentLevel
и IndentSize
для управления отступами и свойство TraceOutputOptions
для записи дополнительных данных:
TextWriterTraceListener tl = new TextWriterTraceListener (Console.Out); tl.TraceOutputOptions = TraceOptions.DateTime | TraceOptions.Callstack; // Это применяется при использовании метода Trace: Trace.TraceWarning («Orange alert»); DiagTest.vshost.exe Warning: 0 : Orange alert DateTime=2007—03—08T05:57:13.6250000Z Callstack= at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo) at System.Environment.get_StackTrace() at ... |
Прослушиватели, которые записывают данные в поток, кэшируются. По этой причине данные не появляются в потоке немедленно, а также поток перед завершением приложения должен быть закрыт, или хотя бы сброшен, чтоб не потерять данные в кэше. Для этой цели классы Trace
и Debug
содержат статические методы Close
и Flush
, которые вызывают Close
и Flush
во всех прослушивателях (а они в свою очередь закрывают или сбрасывают все потоки). Метод Close
вызывает метод Flush
, закрывает файловые дескрипторы и предотвращает дальнейшую запись.
Классы Trace
и Debug
также определяют свойство AutoFlush
, которое если равно true
вызывает Flush
после каждого сообщения.
Fail и Assert
Классы Debug
и Trace
содержат методы Fail
и Assert
.
Метод Fail
отправляет сообщения каждому TraceListener
:
Debug.Fail («File data.txt does not exist!»); |
Метод Assert
вызывает Fail
если аргумент типа bool
равен false
. Это называется созданием утверждения и указывает на ошибку, если оно нарушено. Можно также создать необязательное сообщение об ошибке:
Debug.Assert (File.Exists («data.txt»), «File data.txt does not exist!»); var result = ... Debug.Assert (result != null); |
Методы Write
, Fail
и Assert
также могут принимать категорию в виде строки ,которая может быть использована при обработке вывода.
Долго я пытался решить проблему самостоятельно, а так же и искал ответы на форумах. Но в итоге не смог решить свою проблему.
Проблема в том, что я сохраняю свой класс в файл, и потом после перезапуска программы, когда в файле уже есть данные, то при компиляции идет логическая ошибка при чтении файла.
#include<iostream>
#include<windows.h>
#include<fstream>
#include<conio.h>
using namespace std;
class Player{
public:
Player(){
age = exp = 0;
name = "player";
level = 1;
balance = 3000;
HP = 100;
nxtLvl = 500;
damage = 150;
}
Player(int age, string name):Player(){
this->age = age;
this->name = name;
}
Player(const Player &obj){
age = obj.age;
name = obj.name;
level = obj.level;
balance = obj.balance;
HP = obj.HP;
nxtLvl = obj.nxtLvl;
damage = obj.damage;
exp = obj.exp;
}
string getName(){return name;}
int getAge(){return age;}
int getLevel(){return level;}
int getHP(){return HP;}
int getBalance(){return balance;}
int getNxt(){return nxtLvl;}
int getDamage(){return damage;}
void printHero(){
cout << name << " " << age << " лет.n" << level << " lvl.n"
<< "Бабки: " << balance << "$nЗдоровье: " << HP << " ХПn" << "Сила удара: максимум " << damage << "nОпыт: " << exp << "/" <<nxtLvl << "n";
}
void upLevel(){
++level;
nxtLvl = nxtLvl * 2 + (nxtLvl/2);
damage += (damage/10);
}
void addExp(int exp){this->exp += exp;}
void addHealth(int health){HP += health;}
void remHealth(int health){HP -= health;}
void addDamage(int damage){this->damage += damage;}
private:
int age, level, balance, HP, exp, nxtLvl, damage;
string name;
};
void Arena();
void myHero();
void Shop();
main(){
setlocale(LC_ALL, "rus");
Player me;
string path = "game_saves.txt";
ofstream fout;
fout.open(path, ofstream::app);
fout.close();
ifstream fin;
fin.open(path);
if(!fin.read((char*)&me, sizeof(Player))){
cout << "Введите ваше имя:n";
string name;
cin >> name;
system("cls");
cout << "Введите сколько вам лет:n";
int age;
cin >> age;
me = Player(age, name);
ofstream fout;
fout.open(path);
fout.write((char*)&me, sizeof(Player));
fout.close();
}
fin.close();
system("cls");
//int l; cin >> l;
cout << "Hello " << me.getName() << "!n";
cout << ">Новая играn Продолжить игруn";
char c;
int choose = 1;
while(c != 13){
c = _getch();
if(c == 72){
system("cls");
cout << "Hello " << me.getName() << "!n";
cout << ">Новая играn Продолжить игруn";
choose = 1;
}
if(c == 80){
system("cls");
cout << "Hello " << me.getName() << "!n";
cout << " Новая играn>Продолжить игруn";
choose = 2;
}
}
if(choose == 1){
system("cls");
cout << "Введите ваше имя:n";
string name;
cin >> name;
cout << "Введите сколько вам лет:n";
int age;
cin >> age;
ofstream fout;
fout.open(path);
fout.write((char*)&me, sizeof(Player));
fout.close();
}
else{
system("cls");
cout << ">Аренаn Мой геройn Магазинn";
char k='a';
char vote = 1;
while(k != 13){
k = _getch();
if(k == 72){
system("cls");
if(vote == 2){
cout << ">Аренаn Мой геройn Магазинn";
vote = 1;
}
if(vote == 3){
cout << " Аренаn>Мой геройn Магазинn";
vote = 2;
}
}
if(k == 80){
system("cls");
if(vote == 1){
cout << " Аренаn>Мой геройn Магазинn";
vote = 2;
}
else{
cout << " Аренаn Мой геройn>Магазинn";
vote = 3;
}
}
}
/* if(vote == 1) Arena();
if(vote == 2) myHero();
if(vote == 3) Shop();*/
}
}
void Arena(){
}
Буду рад помощи. Прошу не ругаться, я начал изучать ООП и работу с файлами только позавчера.
Большинство компьютерных программ работают с файлами, и поэтому возникает необходимость создавать, удалять, записывать читать, открывать файлы. Что же такое файл? Файл – именованный набор байтов, который может быть сохранен на некотором накопителе. Ну, теперь ясно, что под файлом понимается некоторая последовательность байтов, которая имеет своё, уникальное имя, например файл.txt
. В одной директории не могут находиться файлы с одинаковыми именами. Под именем файла понимается не только его название, но и расширение, например: file.txt
и file.dat
— разные файлы, хоть и имеют одинаковые названия. Существует такое понятие, как полное имя файлов – это полный адрес к директории файла с указанием имени файла, например: D:docsfile.txt
. Важно понимать эти базовые понятия, иначе сложно будет работать с файлами.
Для работы с файлами необходимо подключить заголовочный файл <fstream>
. В <fstream>
определены несколько классов и подключены заголовочные файлы <ifstream>
— файловый ввод и <ofstream>
— файловый вывод.
Файловый ввод/вывод аналогичен стандартному вводу/выводу, единственное отличие – это то, что ввод/вывод выполнятся не на экран, а в файл. Если ввод/вывод на стандартные устройства выполняется с помощью объектов cin
и cout
, то для организации файлового ввода/вывода достаточно создать собственные объекты, которые можно использовать аналогично операторам cin
и cout
.
Например, необходимо создать текстовый файл и записать в него строку Работа с файлами в С++
. Для этого необходимо проделать следующие шаги:
- создать объект класса
ofstream
; - связать объект класса с файлом, в который будет производиться запись;
- записать строку в файл;
- закрыть файл.
Почему необходимо создавать объект класса ofstream
, а не класса ifstream
? Потому, что нужно сделать запись в файл, а если бы нужно было считать данные из файла, то создавался бы объект класса ifstream
.
// создаём объект для записи в файл ofstream /*имя объекта*/; // объект класса ofstream
Назовём объект – fout
, Вот что получится:
ofstream fout;
Для чего нам объект? Объект необходим, чтобы можно было выполнять запись в файл. Уже объект создан, но не связан с файлом, в который нужно записать строку.
fout.open("cppstudio.txt"); // связываем объект с файлом
Через операцию точка получаем доступ к методу класса open(), в круглых скобочках которого указываем имя файла. Указанный файл будет создан в текущей директории с программой. Если файл с таким именем существует, то существующий файл будет заменен новым. Итак, файл открыт, осталось записать в него нужную строку. Делается это так:
fout << "Работа с файлами в С++"; // запись строки в файл
Используя операцию передачи в поток совместно с объектом fout
строка Работа с файлами в С++
записывается в файл. Так как больше нет необходимости изменять содержимое файла, его нужно закрыть, то есть отделить объект от файла.
fout.close(); // закрываем файл
Итог – создан файл со строкой Работа с файлами в С++
.
Шаги 1 и 2 можно объединить, то есть в одной строке создать объект и связать его с файлом. Делается это так:
ofstream fout("cppstudio.txt"); // создаём объект класса ofstream и связываем его с файлом cppstudio.txt
Объединим весь код и получим следующую программу.
// file.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" #include <fstream> using namespace std; int main(int argc, char* argv[]) { ofstream fout("cppstudio.txt"); // создаём объект класса ofstream для записи и связываем его с файлом cppstudio.txt fout << "Работа с файлами в С++"; // запись строки в файл fout.close(); // закрываем файл system("pause"); return 0; }
Осталось проверить правильность работы программы, а для этого открываем файл cppstudio.txt
и смотрим его содержимое, должно быть — Работа с файлами в С++
.
Для того чтобы прочитать файл понадобится выполнить те же шаги, что и при записи в файл с небольшими изменениями:
- создать объект класса
ifstream
и связать его с файлом, из которого будет производиться считывание; - прочитать файл;
- закрыть файл.
// file_read.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" #include <fstream> #include <iostream> using namespace std; int main(int argc, char* argv[]) { setlocale(LC_ALL, "rus"); // корректное отображение Кириллицы char buff[50]; // буфер промежуточного хранения считываемого из файла текста ifstream fin("cppstudio.txt"); // открыли файл для чтения fin >> buff; // считали первое слово из файла cout << buff << endl; // напечатали это слово fin.getline(buff, 50); // считали строку из файла fin.close(); // закрываем файл cout << buff << endl; // напечатали эту строку system("pause"); return 0; }
В программе показаны два способа чтения из файла, первый – используя операцию передачи в поток, второй – используя функцию getline()
. В первом случае считывается только первое слово, а во втором случае считывается строка, длинной 50 символов. Но так как в файле осталось меньше 50 символов, то считываются символы включительно до последнего. Обратите внимание на то, что считывание во второй раз (строка 17) продолжилось, после первого слова, а не с начала, так как первое слово было прочитано в строке 14. Результат работы программы показан на рисунке 1.
CppStudio.com
Работа с файлами в С++ Для продолжения нажмите любую клавишу . . .
Рисунок 1 — Работа с файлами в С++
Программа сработала правильно, но не всегда так бывает, даже в том случае, если с кодом всё впорядке. Например, в программу передано имя несуществующего файла или в имени допущена ошибка. Что тогда? В этом случае ничего не произойдёт вообще. Файл не будет найден, а значит и прочитать его не возможно. Поэтому компилятор проигнорирует строки, где выполняется работа с файлом. В результате корректно завершится работа программы, но ничего, на экране показано не будет. Казалось бы это вполне нормальная реакции на такую ситуацию. Но простому пользователю не будет понятно, в чём дело и почему на экране не появилась строка из файла. Так вот, чтобы всё было предельно понятно в С++ предусмотрена такая функция — is_open()
, которая возвращает целые значения: 1 — если файл был успешно открыт, 0 — если файл открыт не был. Доработаем программу с открытием файла, таким образом, что если файл не открыт выводилось соответствующее сообщение.
// file_read.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" #include <fstream> #include <iostream> using namespace std; int main(int argc, char* argv[]) { setlocale(LC_ALL, "rus"); // корректное отображение Кириллицы char buff[50]; // буфер промежуточного хранения считываемого из файла текста ifstream fin("cppstudio.doc"); // (ВВЕЛИ НЕ КОРРЕКТНОЕ ИМЯ ФАЙЛА) if (!fin.is_open()) // если файл не открыт cout << "Файл не может быть открыт!n"; // сообщить об этом else { fin >> buff; // считали первое слово из файла cout << buff << endl; // напечатали это слово fin.getline(buff, 50); // считали строку из файла fin.close(); // закрываем файл cout << buff << endl; // напечатали эту строку } system("pause"); return 0; }
Результат работы программы показан на рисунке 2.
CppStudio.com
Файл не может быть открыт! Для продолжения нажмите любую клавишу . . .
Рисунок 2 — Работа с файлами в С++
Как видно из рисунка 2 программа сообщила о невозможности открыть файл. Поэтому, если программа работает с файлами, рекомендуется использовать эту функцию, is_open()
, даже, если уверены, что файл существует.
Режимы открытия файлов
Режимы открытия файлов устанавливают характер использования файлов. Для установки режима в классе ios_base
предусмотрены константы, которые определяют режим открытия файлов (см. Таблица 1).
Константа | Описание |
---|---|
ios_base::in | открыть файл для чтения |
ios_base::out | открыть файл для записи |
ios_base::ate | при открытии переместить указатель в конец файла |
ios_base::app | открыть файл для записи в конец файла |
ios_base::trunc | удалить содержимое файла, если он существует |
ios_base::binary | открытие файла в двоичном режиме |
Режимы открытия файлов можно устанавливать непосредственно при создании объекта или при вызове функции open()
.
ofstream fout("cppstudio.txt", ios_base::app); // открываем файл для добавления информации к концу файла fout.open("cppstudio.txt", ios_base::app); // открываем файл для добавления информации к концу файла
Режимы открытия файлов можно комбинировать с помощью поразрядной логической операции или |
, например: ios_base::out | ios_base::trunc
— открытие файла для записи, предварительно очистив его.
Объекты класса ofstream
, при связке с файлами по умолчанию содержат режимы открытия файлов ios_base::out | ios_base::trunc
. То есть файл будет создан, если не существует. Если же файл существует, то его содержимое будет удалено, а сам файл будет готов к записи. Объекты класса ifstream
связываясь с файлом, имеют по умолчанию режим открытия файла ios_base::in
— файл открыт только для чтения. Режим открытия файла ещё называют — флаг, для удобочитаемости в дальнейшем будем использовать именно этот термин. В таблице 1 перечислены далеко не все флаги, но для начала этих должно хватить.
Обратите внимание на то, что флаги ate
и app
по описанию очень похожи, они оба перемещают указатель в конец файла, но флаг app
позволяет производить запись, только в конец файла, а флаг ate
просто переставляет флаг в конец файла и не ограничивает места записи.
Разработаем программу, которая, используя операцию sizeof()
, будет вычислять характеристики основных типов данных в С++ и записывать их в файл. Характеристики:
- число байт, отводимое под тип данных
- максимальное значение, которое может хранить определённый тип данных.
Запись в файл должна выполняться в таком формате:
/* data type byte max value bool = 1 255.00 char = 1 255.00 short int = 2 32767.00 unsigned short int = 2 65535.00 int = 4 2147483647.00 unsigned int = 4 4294967295.00 long int = 4 2147483647.00 unsigned long int = 4 4294967295.00 float = 4 2147483647.00 long float = 8 9223372036854775800.00 double = 8 9223372036854775800.00 */
Такая программа уже разрабатывалась ранее в разделе Типы данных С++ , но там вся информация о типах данных выводилась на стандартное устройство вывода, а нам необходимо программу переделать так, чтобы информация записывалась в файл. Для этого необходимо открыть файл в режиме записи, с предварительным усечением текущей информации файла (строка 14). Как только файл создан и успешно открыт (строки 16 — 20), вместо оператора cout
, в строке 22 используем объект fout
. таким образом, вместо экрана информация о типах данных запишется в файл.
// write_file.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" #include <iostream> #include <fstream> // работа с файлами #include <iomanip> // манипуляторы ввода/вывода using namespace std; int main(int argc, char* argv[]) { setlocale(LC_ALL, "rus"); // связываем объект с файлом, при этом файл открываем в режиме записи, предварительно удаляя все данные из него ofstream fout("data_types.txt", ios_base::out | ios_base::trunc); if (!fout.is_open()) // если файл небыл открыт { cout << "Файл не может быть открыт или созданn"; // напечатать соответствующее сообщение return 1; // выполнить выход из программы } fout << " data type " << "byte" << " " << " max value " << endl // заголовки столбцов << "bool = " << sizeof(bool) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных bool*/ << (pow(2,sizeof(bool) * 8.0) - 1) << endl << "char = " << sizeof(char) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных char*/ << (pow(2,sizeof(char) * 8.0) - 1) << endl << "short int = " << sizeof(short int) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных short int*/ << (pow(2,sizeof(short int) * 8.0 - 1) - 1) << endl << "unsigned short int = " << sizeof(unsigned short int) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных unsigned short int*/ << (pow(2,sizeof(unsigned short int) * 8.0) - 1) << endl << "int = " << sizeof(int) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных int*/ << (pow(2,sizeof(int) * 8.0 - 1) - 1) << endl << "unsigned int = " << sizeof(unsigned int) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных unsigned int*/ << (pow(2,sizeof(unsigned int) * 8.0) - 1) << endl << "long int = " << sizeof(long int) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных long int*/ << (pow(2,sizeof(long int) * 8.0 - 1) - 1) << endl << "unsigned long int = " << sizeof(unsigned long int) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных undigned long int*/ << (pow(2,sizeof(unsigned long int) * 8.0) - 1) << endl << "float = " << sizeof(float) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных float*/ << (pow(2,sizeof(float) * 8.0 - 1) - 1) << endl << "long float = " << sizeof(long float) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных long float*/ << (pow(2,sizeof(long float) * 8.0 - 1) - 1) << endl << "double = " << sizeof(double) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных double*/ << (pow(2,sizeof(double) * 8.0 - 1) - 1) << endl; fout.close(); // программа больше не использует файл, поэтому его нужно закрыть cout << "Данные успешно записаны в файл data_types.txtn"; system("pause"); return 0; }
Нельзя не заметить, что изменения в программе минимальны, а всё благодаря тому, что стандартный ввод/вывод и файловый ввод/вывод используются абсолютно аналогично. В конце программы, в строке 45 мы явно закрыли файл, хотя это и не обязательно, но считается хорошим тоном программирования. Стоит отметить, что все функции и манипуляторы используемые для форматирования стандартного ввода/вывода актуальны и для файлового ввода/вывода. Поэтому не возникло никаких ошибок, когда оператор cout
был заменён объектом fout
.
При открытии файла при помощи wifstream программа крашится и выдает такую ошибку. Файл формата txt существует и путь к нему указан верно. Может у кого нибудь была похожая проблема?
Код функции main:
#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
//setlocale(LC_ALL, "rus");
wifstream nja(L"text1.txt");
if (!nja.is_open())
cout<<"Ошибка!";
Parm::PARM parm;
cout << argc;
cout << "---- Тест Error::geterror ---" << endl << endl;
try { ERROR_THROW(104); }
catch (Error::ERROR e)
{
cout << "Ошибка " << e.id << ": " << e.message << endl << endl;
};
cout << "---- Тест Error::geterrorin ---" << endl << endl;
try { ERROR_THROW_IN(111, 7, 7); }
catch (Error::ERROR e)
{
cout << "Ошибка " << e.id << ": " << e.message
<< ", строка " << e.inext.line
<< ", позиция " << e.inext.col << endl << endl;
};
cout << "---- Тест Parm::getparm ---" << endl << endl;
try
{
parm = Parm::getparm(argc, argv);
wcout << L"-in:" << parm.in << L", -out:" << parm.out << L", -log:" << parm.log << endl << endl;
}
catch (Error::ERROR e)
{
cout << "Ошибка " << e.id << ": " << e.message << endl << endl;
};
cout << "---- Тест Parm::getin ---" << endl << endl;
try
{
In::IN in = In::getin(parm.in);
cout << in.text << endl;
cout << "Всего символов: " << in.size << endl;
cout << "Всего строк: " << in.lines << endl;
cout << "Пропущено: " << in.ignor << endl;
}
catch (Error::ERROR e)
{
cout << "Ошибка " << e.id << ": " << e.message << endl << endl;
};
system("pause");
return 0;
}
Содержимое stdafx.h:
#pragma once
#include "targetver.h"
#include <fstream>
#include <iostream>
#include <tchar.h>
#include <stdio.h>
#include <cwchar>
#include <stdio.h>
#include <cstring>
#include <wchar.h>
#include <locale>
#include "Error.h"
#include "Parm.h"
#include "In.h"
using namespace std;