• Functions for working with text files. Working with files in C language

    Working with text files in C++.

    There are two main types of files: text and binary. Files allow the user to read large amounts of data directly from the disk without having to enter it from the keyboard.

      Text files consisting of any characters are called. They are organized into lines, each ending with an end-of-line character. The end of the file itself is indicated by the "end of file" symbol. When writing information to a text file, which can be viewed using any text editor, all data is converted to a character type and stored in character form.

      IN binary In files, information is read and written in the form of blocks of a certain size, in which data of any type and structure can be stored.

    To work with files, special data types, called streams. Flow ifstream is used to work with files in reading mode, and ofstream in recording mode. To work with files in both writing and reading mode, a stream is used fstream.

    In C++ programs, when working with text files, you must include the iostream and fstream libraries.

    In order to record data to a text file, you need:

      describe a variable of type ofstream.

      output information to a file.

      be sure to close the file.

    For reading data from a text file, you need:

      describe a variable of type ifstream.

      open a file using the open function.

      close the file.

    Record information to a text file

      As mentioned earlier, in order to start working with text file, must be described type variable ofstream. For example, like this:

      A variable F will be created to write information to the file.

      On next stage the file must be opened for writing. In general, the stream opening operator will look like:

    F.open("file", mode);

    Here F is a variable described as ofstream,

    file - full name file on disk

    mode - mode of working with the file being opened.

    Please note that when specifying the full file name, you must use a double slash. For example, the full name of the file noobs.txt, located in the game folder on drive D:, will need to be written like this:

    D:\\game\\noobs.txt.

    The file can be opened in one of the following modes:

    ios::in - open the file in data reading mode, this mode is the default mode for ifstreams;

    ios::out - open a file in data writing mode (in this case, information about the existing file is destroyed), this mode is the default mode for ofstreams;

    ios::app - open the file in the mode of writing data to the end of the file;

    ios::ate - move to the end of an already open file;

    ios::trunc - clear the file, this also happens in ios::out mode;

    ios::nocreate - do not open a file if it does not exist;

    ios::noreplace - do not open an existing file.

    The mode parameter may be absent, in which case the file is opened in the default mode for this stream.

    After successful opening of the file (in any mode), the variable F will store true, otherwise false. This will allow you to check the correctness of the file opening operation.

    You can open a file (let's take the file D:\\game\\noobs.txt as an example) in recording mode in one of the following ways:

    // first way

    ofstream F;

    F.open("D:\\game\\noobs.txt", ios::out);

    //second method, ios::out mode is the default mode

    // For flowofstream

    ofstream F;

    //the third method combines the description of the variable and the stream type

    //and opening the file in one statement

    ofstream F("D:\\game\\noobs.txt", ios::out);

    After opening the file in write mode, an empty file will be created into which you can write information.

    If you want to open an existing file in write-first mode, you should use ios::app as the mode.

    After opening a file in recording mode, you can write to it in the same way as to the screen, only instead of the standard output devicecoutyou must specify the name of the open file.

    For example, to write variable a to stream F, the output statement will look like:

    For sequential output to stream G of variables b, c, d, the output operator will become like this:

    G<

    Closing a stream is done using the operator:

    EXAMPLE:

    Create a text file D:\\game\\noobs.txt and write n real numbers into it.

    #include "stdafx.h"

    #include

    #include

    #include

    using namespace std;

    int main()

    setlocale(LC_ALL, "RUS");

    int i, n;

    double a;

    //describes a stream for writing data to a file

    ofstream f;

    //open the file in write mode,

    //modeios:: outinstalled by default

    f.open("D:\\game\\noobs.txt", ios::out);

    //enter the number of real numbers

    cout<<" n="; cin>> n;

    //loop for entering real numbers

    //and write them to a file

    for (i=0; i

    cout<<"a=";

    //enter a number

    cin>>a;

    f<

    //closing the stream

    f.close();

    system("pause");

    return 0;

    _______________________________________________________________

    In order to read information from a text file, you need to describe a variable like ifstream. After this, you need to open the file for reading using the operator open. If the variable is called F, then the first two statements will be like this:

    F.open("D:\\game\\noobs.txt", ios::in);

    After opening a file in reading mode, you can read information from it in the same way as from the keyboard, only instead ofcinspecify the name of the stream from which data will be read.

    For example, to read from stream F into variable a, the input statement would look like this:

    Two numbers in a text editor are considered separated if there is at least one of the characters between them: space, tab, end of line. It’s good if the programmer knows in advance how many and what values ​​to store in the text file. However, often the type of values ​​stored in the file is simply known, but their number may vary. To solve this problem, you need to read values ​​from the file one at a time, and before each read, check whether the end of the file has been reached. There is a function for this F. eof().

    Here F is the name of the thread, the function returns a Boolean value: true or false, depending on whether the end of the file is reached. Therefore, a loop to read the contents of the entire file can be written like this:

    //organize for reading values ​​from a file, execution

    //the loop will break when we reach the end of the file,

    //in this case F.eof() will return true

    while (!F.eof())

    EXAMPLE:

    The text file D:\\game\\noobs.txt stores real numbers, display them on the screen and calculate their number.

    #include "stdafx.h"

    #include

    #include

    #include

    #include

    using namespace std;

    int main()

    setlocale(LC_ALL, "RUS");

    int n=0;

    float a;

    fstream F;

    //open the file in reading mode

    F.open("D:\\game\\noobs.txt");

    //if the file was opened correctly, then

    //loop for reading values ​​from a file; the loop execution will be interrupted,

    //when we reach the end of the file, in this case F.eof() will return true.

    while (!F.eof())

    //reading the next value from stream F into variable a

    F>>a;

    //output the value of variable a to the screen

    cout<

    //increase the number of numbers read

    //closing the stream

    F.close();

    //input on the screen the number of numbers read

    cout<<"n="<

    //if the file opening was incorrect, then the output

    //messages about the absence of such a file

    else cout<<" Файл не существует"<

    system("pause");

    return 0;

    C++. Binary file processing

    When writing information to a binary file, characters and numbers are written as a sequence of bytes.

    In order to write down data to a binary file, you need:

      describe a file variable of type FAIL * using the FILE *filename; operator. Here filename is the name of the variable where the pointer to the file will be stored.

      write information to a file using the fwrite function

    In order to count b data from a binary file, you need:

      describe a variable of type FILE *

      open a file using the fopen function

      close a file using the fclose function

    Basic functions required to work with binary files.

    For openings The file is intended for the fopen function.

    FILE *fopen(const *filename, const char *mode)

    Here filename is a string that stores the full name of the file being opened, mode is a string that determines the mode for working with the file; the following values ​​are possible:

    “rb” - open the binary file in read mode;

    “wb” - create a binary file for recording; if it exists, its contents are cleared;

    “ab” - create or open a binary file to append to the end of the file;

    “rb+” - open an existing binary file in read-write mode;

    “wb+” - open the binary file in read-write mode, the existing file is cleared;

    "ab+" - a binary file is opened or created to correct existing information and add new information to the end of the file.

    The function returns the NULL value in the file variable f if the file opening fails. After opening a file, its 0th byte is available, the file pointer is 0, the value of which, as it is read or written, is shifted by the number of bytes read (written). The current value of the file pointer is the byte number from which the read or write operation will occur.

    For closing the file is intended for the fclose function

    int fclose(FILE *filename);

    Returns 0 if the file was closed successfully, NULL otherwise.

    The remove function is for removal files.

    int remove(const char *filename);

    This function deletes a file named filenema from disk. The file to be deleted must be closed. The function returns a non-zero value if the file could not be deleted.

    For renaming files, the rename function is intended:

    int rename(const char *oldfilename, const char *newfilename);

    The first parameter is the old file name, the second is the new one. Returns 0 if the program terminates successfully.

    Reading from a binary file is done using the fread function:

    fread(void *ptr, size, n, FILE *filename);

    The fread function reads n elements of size size from file filename into an array ptr. The function returns the number of elements read. After reading from a file, its pointer is shifted by n*size bytes.

    Record to a binary file is done using the fwrite function:

    fwrite(const void *ptr, size, n, FILE *filename);

    The fwrite function writes to file filename from an array ptr of n elements of size size. The function returns the number of elements written. After writing information to the file, the pointer is shifted by n*size bytes.

    For end-of-file control there is a function feof:

    int feof(FILE *filename);

    It returns a non-zero value if the end of the file is reached.

    EXAMPLE:

    Create a binary file D:\\game\\noobs.dat and write n integer and n real numbers into it.

    #include "stdafx.h"

    #include

    using namespace std;

    int main()

    setlocale(LC_ALL, "RUS");

    int n, i;

    double a;

    //create a binary file in write mode

    f=fopen("D:\\game\\noobs.dat", "wb");

    // input numbersn

    cout<<"n="; cin>>n;

    fwrite(&n, sizeof(int), 1, f);

    //loop to enter n real numbers

    for (i=0; i

    //enter the next real number

    cout<<"a=";

    cin>>a;

    //writing a real number to a binary file

    fwrite(&a, sizeof(double), 1, f);

    // close file

    fclose(f);

    system("pause");

    return 0;

    EXAMPLE:

    Display the contents of the binary file D:\\game\\noobs.dat created in the previous task

    #include "stdafx.h"

    #include

    using namespace std;

    int main()

    setlocale(LC_ALL, "RUS");

    int n, i;

    double *a;

    FILE *f; //describe the file variable

    //open existing binary file in read mode

    //read one integer from the file into variable n

    //output n to screen

    cout<<"n="<

    //allocating memory for an array of n numbers

    a=new double[n];

    //read n real numbers from file into array a

    //output the array to the screen

    for (i=0; i

    cout<

    cout<

    // close file

    fclose(f);

    system("pause");

    return 0;

    Binary file- sequential data structure, after opening a file, the first byte stored in it is available. You can write or read data from a file sequentially. Let's say you need to count the fifteenth number, and then the first. Using sequential access this can be done in the following way:

    int n, i;

    double a;

    FILE *f;

    f=fopen("D:\\game\\noobs.dat", "rb");

    for (i=0; i<15; i++)

    fclose(f);

    f=fopen("D:\\game\\noobs.dat", "rb");

    fread(&a, sizeof(double), 1, f);

    fclose(f);

    As you can see, reading numbers from a file and then opening the file again is not the most convenient way. It will be much more convenient to use the fseek function to move the file pointer to a given byte.

    int fseek(FILE *filename, long int offset, int origin);

    The function sets the current file position pointer F in accordance with the origin and offset values. The offset parameter is equal to the number of bytes by which the file pointer will be offset relative to the origin specified by the origin parameter. The value for the origin parameter must be one of the following offset values ​​defined in the stdio.h header:

    SEEK_SET - from the beginning of the file;

    SEEK_CUR - from the current position;

    SEEK_END - from the end of the file.

    The function returns a zero value if the operation was successful, non-zero if a failure occurred during the offset execution

    The fseek function actually implements direct access to any value in a file. You only need to know the location (byte number) of the value in the file. Let's look at using direct access in binary files using the following problem as an example.

    EXAMPLE

    In the binary file D:\\game\\noobs.dat created earlier, swap the largest and smallest real numbers.

    The algorithm for solving the problem consists of the following stages:

      reading reals from a file into array a.

      search in array a for the maximum (max) and minimum (min) values ​​and their numbers (imax, imin).

      moving the file pointer to the maximum value and writing min.

      moving the file pointer to the minimum value and writing max.

    Below is the text of the program for solving the problem with comments.

    #include "stdafx.h"

    #include

    using namespace std;

    int main()

    setlocale(LC_ALL, "RUS");

    int n, i, imax, imin;

    double *a, max, min;

    FILE *f;

    //open a file in read-write mode

    f=fopen("D:\\game\\noobs.dat", "rb+");

    //read number from file into variable n

    //real numbers in the file

    fread(&n, sizeof(int), 1, f);

    cout<<"n="<

    //allocate memory for storing real numbers,

    //which will be stored in array a

    a=new double[n];

    //read from file into array and real numbers

    fread(a, sizeof(double), n, f);

    //search for maximum and minimum elements

    //in the array a and their indices

    for (imax=imin=0, max=min=a, i=1; i

    if (a[i]>max)

    max=a[i];

    if (a[i]

    min=a[i];

    // moving pointer To maximum element

    fseek(f, sizeof(int)+imax*sizeof(double), SEEK_SET);

    //write min instead of maximum file element

    fwrite(&min, sizeof(double), 1, f);

    // moving pointer To minimum element

    fseek(f, sizeof(int)+imin*sizeof(double), SEEK_SET);

    //write max instead of the minimum file element

    fwrite(&max, sizeof(double), 1, f);

    //closing the file

    fclose(f);

    //freeing memory

    delete [ ]a;

    system("pause");

    To the programmer, an open file is represented as a sequence of data being read or written. When a file is opened, it is associated with I/O stream . Output information is written to the stream, input information is read from the stream.

    When a stream is opened for I/O, it is associated with a standard FILE structure, which is defined in stdio.h. The FILE structure contains the necessary information about the file.

    Opening a file is done using the fopen() function, which returns a pointer to a structure of type FILE that can be used for subsequent operations on the file.

    FILE *fopen(name, type);

    name – name of the file to open (including path),
    type - pointer to a string of characters defining how the file is accessed:

    · "r" - open the file for reading (the file must exist);

    · "w" - open an empty file for writing; if the file exists, its contents are lost;

    · "a" - open the file for writing to the end (for appending); the file is created if it does not exist;

    · "r+" - open the file for reading and writing (the file must exist);

    · "w+" - open an empty file for reading and writing; if the file exists, its contents are lost;

    · "a+" - open the file for reading and appending; if the file does not exist, it is created.

    The return value is a pointer to the open stream. If an error is detected, NULL is returned.

    The fclose() function closes the stream or streams associated with files opened using the fopen() function. The stream to be closed is determined by the argument of the fclose() function.

    Return value: value 0 if the stream was closed successfully; constant EOF if an error occurred.

    #include
    int main()

    char name="my.txt";

    if(fp = fopen(name, "r")!=NULL)

    // was it possible to open the file?
    ... // required actions on data

    else printf("Failed to open file");

    Reading a character from a file:

    char fgetc(stream);

    The function argument is a pointer to a stream of type FILE. The function returns the code of the read character. If the end of the file is reached or an error occurs, the constant EOF is returned.
    Writing a symbol to a file:

    fputc(char, stream);

    The function's arguments are a character and a pointer to a stream of type FILE. The function returns the code of the read character.

    The fscanf() and fprintf() functions are similar to the scanf() and printf() functions, but work with data files, and have a pointer to the file as their first argument.

    fscanf(stream, "Input Format", arguments);
    fprintf(stream, "Output Format", arguments);

    The fgets() and fputs() functions are designed for string input/output; they are analogous to the gets() and puts() functions for working with files.

    fgets(Pointer To Line, Number Of Characters, stream);

    Characters are read from the stream until a newline character "\n" is read, which is included in the string, or until the stream ends EOF or the maximum number of characters has been read. The result is placed in a string pointer and ends with the null character "\0". The function returns the address of the string.

    fputs(Pointer To String, stream);

    Copies a string to the stream from the current position. The terminating null character is not copied.
    Example Enter the number and save it in the s1.txt file. Read the number from the file s1.txt, increase it by 3 and save it in the file s2.txt.

    The fopen() function opens a stream for use, associates a file with that stream, and then returns a FILE pointer to that stream. Most often the file is treated as a disk file. The fopen() function has the following prototype:

    FILE *fopen(const char *filename, const char *mode);

    Where mode points to a string containing the desired mode for opening the file. Valid values ​​for mode in Borland C++ are shown in the table. filename must be a character string that provides a valid filename to the operating system, and may contain a pathname.

    The fopen() function returns a pointer to the base type FILE. This pointer identifies the file and is used by most file system functions. You should never change it yourself. The function returns a null pointer if the file cannot be opened.

    As the table shows, the file can be opened in either text or binary mode. In text mode, as you type, the sequence of carriage return and line feed is translated into a newline character. In output, the opposite is true: the newline character is translated into a carriage return and a line feed. IN binary files no such translation occurs. When neither t nor b is specified in the mode argument, then the file's text/binary status is determined by the value of the global variable _fmode defined in Borland C++. By default, fmode is set to O_TEXT, that is, text mode is set. If you set _fmode to O_BINARY, then files will be opened in binary mode. (These macros are defined in fcntl.h.) Naturally, using an explicit t or b eliminates the effects associated with the _fmode variable. In addition, _fmode is specific to Borland products only. It is not defined in the ANSI C I/O system.

    If you need to open a file named test for writing, you should write:

    Fp = fopen("test", "w");

    Where fp is a variable of type FILE *. However, it is common to see the following:

    If((fp = fopen("test", "w"))==NULL) (
    puts("Cannot open file.");
    exit(1);
    }

    This method allows you to detect errors when opening a file, for example, the presence of write protection or lack of free space on the disk.

    If fopen() is used to open a file for writing, then any pre-existing file with the specified name will be deleted. If a file with the specified name does not exist, it will be created.

    If you need to append information to the end of the file, you should use mode a (append). If the file does not exist, it will be created.

    Opening a file for reading requires the presence of the file. If the file does not exist, an error will be returned. If the file is opened for read/write operation, then it is not deleted if it exists, and if the file does not exist, then it is created.

    Table: Allowable mode values

    Meaning

    Opens a file for reading. (Opens by default as a text file.)

    Creates a file to write to. (Opens by default as a text file.)

    Attaches to a file. (Opens by default as a text file.)

    Opens a binary file for reading.

    Opens a binary file for writing.

    Attaches to a binary file.

    Opens a file for reading/writing. (Opens by default as a text file.)

    Creates a read/write file. (Opens by default as a text file.)

    Attaches or creates a read/write file. (Opens by default as a text file.)

    Opens a binary file for reading/writing.

    Creates a read/write binary file.

    Attaches or creates a read/write binary file.

    Creates a text file for writing.

    Attaches to a text file.

    Opens a text file for reading.

    Creates a text file for reading/writing.

    Opens or creates a text file for reading/writing.

    Previously, when inputting and outputting data, we worked with standard streams - the keyboard and monitor. Now let's look at how the C language implements receiving data from files and writing them there. Before you can perform these operations, you must open the file and access it.

    In the C programming language, a pointer to a file is of type FILE and its declaration looks like this:
    FILE *myfile;

    On the other hand, the fopen() function opens a file at the address specified as the first argument in read mode ("r"), write mode ("w"), or append mode ("a") and returns a pointer to it to the program. Therefore, the process of opening a file and connecting it to the program looks something like this:
    myfile = fopen("hello.txt", "r");

    When reading or writing data to a file, it is accessed through a file pointer (in this case, myfile).

    If for one reason or another (there is no file at the specified address, access to it is denied) the fopen() function cannot open the file, then it returns NULL. In real programs, they almost always handle a file opening error in the if branch, but we will omit this further.

    The fopen() function declaration is contained in the stdio.h header file, so it must be included. Also in stdio.h the structure type FILE is declared.

    After working with a file is finished, it is customary to close it to free the buffer from data and for other reasons. This is especially important if the program continues to run after working with the file. Breaking the connection between an external file and a pointer to it from the program is done using the fclose() function. A pointer to the file is passed to it as a parameter:
    fclose(myfile);

    More than one file can be opened in the program. In this case, each file must be associated with its own file pointer. However, if the program first works with one file and then closes it, then the pointer can be used to open a second file.

    Reading from and writing to a text file

    fscanf()

    The fscanf() function is similar in meaning to the scanf() function, but unlike it, it provides formatted input from a file rather than standard input. The fscanf() function takes parameters: file pointer, format string, addresses of memory areas for writing data:
    fscanf(myfile, "%s%d", str, &a);

    Returns the number of successfully read data or EOF. Spaces and newline characters are counted as data delimiters.

    Let's say we have a file containing the following description of objects:

    Apples 10 23.4 bananas 5 25.0 bread 1 10.3

    #include main () ( FILE * file; struct food ( char name[ 20 ] ; unsigned qty; float price; ) ; struct food shop[ 10 ] ; char i= 0 ; file = fopen ( "fscanf.txt" , "r" ) ; while (fscanf (file, "%s%u%f" , shop[ i].name , & (shop[ i].qty ) , & (shop[ i].price ) != EOF) ( printf ("%s %u %.2f \n", shop[ i].name, shop[ i].qty, shop[ i].price) ; i++; ) )

    In this case, a structure and an array of structures are declared. Each line from the file corresponds to one element of the array; an array element is a structure containing a string and two numeric fields. The loop reads one row per iteration. When the end of file is encountered, fscanf() returns EOF and the loop ends.

    fgets()

    The fgets() function is similar to the gets() function and performs line-by-line input from a file. One call to fgets() will read one line. In this case, you can not read the entire line, but only part of it from the beginning. fgets() parameters look like this:
    fgets (character_array, number_of_characters_read, pointer_to_file)

    For example:
    fgets(str, 50, myfile)

    This function call will read from the file associated with the myfile pointer one full line of text if its length is less than 50 characters, including the "\n" character, which the function will also store in an array. The last (50th) element of the str array will be the "\0" character added by fgets() . If the string is longer, the function will read 49 characters and write "\0" at the end. In this case, "\n" will not be contained in the read line.

    #include #define N 80 main () ( FILE * file; char arr[ N] ; file = fopen ( "fscanf.txt" , "r" ) ; while (fgets (arr, N, file) != NULL) printf (" %s" , arr) ; printf (" \n") ; fclose(file); )

    In this program, unlike the previous one, the data is read line by line into the arr array. When the next line is read, the previous one is lost. The fgets() function returns NULL if it cannot read the next line.

    getc() or fgetc()

    The getc() or fgetc() function (both work) allows you to get the next single character from a file.

    while ((arr[ i] = fgetc (file) ) != EOF) ( if (arr[ i] == " \n") (arr[i] = " \0 " ; printf("%s \n", arr) ; i = 0 ; ) else i++; )arr[i] = " \0 " ; printf("%s \n", arr) ;

    The example code displays data from a file on the screen.

    Writing to a text file

    Just like input, output to a file can be different.

    • Formatted output. Function fprintf (file_index, format_string, variables) .
    • Post-by-line output. Function fputs(string, file_pointer) .
    • Character-by-character output. Function fputc() or putc(symbol, file_pointer) .

    Below are code examples that use three methods of outputting data to a file.

    Writing fields of one structure to each line of the file:

    file = fopen ("fprintf.txt" , "w" ) ; while (scanf ("%s%u%f" , shop[ i].name , & (shop[ i].qty ) , & (shop[ i].price ) ) != EOF) ( fprintf (file, " %s %u %.2f \n", shop[ i].name, shop[ i].qty, shop[ i].price) ; i++; )

    Line-by-line output to a file (fputs(), unlike puts() itself, does not place “\n” at the end of the line):

    while (gets (arr) != NULL) ( fputs (arr, file); fputs (" \n", file); )

    Example of character-by-character output:

    while ((i = getchar () ) != EOF) putc (i, file) ;

    Reading from and writing to a binary file

    You can work with a file not as a sequence of characters, but as a sequence of bytes. In principle, it is not possible to work with non-text files any other way. However, you can also read and write to text files this way. The advantage of this method of accessing a file is the read-write speed: a significant block of information can be read/written in one access.

    When opening a file for binary access, the second parameter to fopen() is the string "rb" or "wb".

    The topic of working with binary files is quite complex and requires a separate lesson to study it. Here only the features of the functions of reading and writing to a file, which is considered as a byte stream, will be noted.

    The fread() and fwrite() functions take as parameters:

    1. address of the memory area where data is written or read from,
    2. the size of one given any type,
    3. the amount of data read of the specified size,
    4. file index.

    These functions return the number of data successfully read or written. Those. you can “order” the reading of 50 data elements, but receive only 10. There will be no error.

    An example of using the fread() and fwrite() functions:

    #include #include main () ( FILE * file; char shelf1[ 50 ], shelf2[ 100 ] ; int n, m; file = fopen ("shelf1.txt" , "rb" ) ; n= fread (shelf1, sizeof (char ) , 50 , file) ; fclose (file) ; file = fopen ("shelf2.txt" , "rb" ) ; m= fread (shelf2, sizeof (char ) , 50 , file) ; = " \0 " ; shelf2[m] = " \n"; shelf2[ m+ 1 ] = " \0 " ; file = fopen ("shop.txt" , "wb" ) ; fwrite (strcat (shelf2, shelf1) , sizeof (char ) , n+ m, file) ; fclose(file); )

    Here an attempt is made to read 50 characters from the first file. n stores the number of characters actually read. The value of n can be 50 or less. The data is placed in a row. The same thing happens with the second file. Next, the first line is appended to the second, and the data is dumped into the third file.

    Problem solving

    1. Write a program that asks the user for the name (address) of a text file, then opens it and counts the number of characters and lines in it.
    2. Write a program that writes to a file data received from another file and modified in some way before writing. Each line of data obtained from a file must fit into a structure.

    Tags: Text files, fopen, fclose, feof, setbuf, setvbuf, fflush, fgetc, fprintf, fscanf, fgets, buffered stream, unbuffered stream.

    Working with text files

    Working with a text file is similar to working with the console: using formatted input functions we save data to a file, using formatted output functions we read data from a file. There are many nuances that we will look at later. The main operations that need to be done are

    • 1. Open the file so that it can be accessed. Accordingly, you can open it for reading, writing, reading and writing, rewriting or writing to the end of the file, etc. When you open a file, a bunch of errors can also happen - the file may not exist, it may be the wrong file type, you may not have permission to work with the file, etc. All this must be taken into account.
    • 2. Directly working with the file - writing and reading. Here we also need to remember that we are not working with random access memory, but with a buffered stream, which adds its own specifics.
    • 3. Close the file. Since the file is a resource external to the program, if it is not closed, it will continue to hang in memory, possibly even after the program is closed (for example, it will not be possible to delete an open file or make changes, etc.). In addition, sometimes it is necessary not to close, but to “re-open” a file in order, for example, to change the access mode.

    In addition, there are a number of tasks when we do not need to access the contents of the file: renaming, moving, copying, etc. Unfortunately, the C standard does not contain a description of functions for these needs. They are, of course, available for each of the compiler implementations. Reading the contents of a directory (folder, directory) is also accessing a file, because the folder itself is a file with metainformation.

    Sometimes it is necessary to perform some auxiliary operations: move to the desired location in the file, remember the current position, determine the length of the file, etc.

    To work with a file, you need a FILE object. This object stores the file stream's identifier and the information needed to manage it, including a pointer to its buffer, a file position indicator, and status indicators.

    The FILE object is itself a structure, but its fields should not be accessed. The portable program must treat the file as an abstract object that allows access to the file stream.

    Creation and allocation of memory for an object of type FILE is carried out using the fopen or tmpfile function (there are others, but we will only focus on these).

    The fopen function opens a file. It receives two arguments - a string with the file address and a string with the file access mode. The file name can be either absolute or relative. fopen returns a pointer to a FILE object that can be used to further access the file.

    FILE* fopen(const char* filename, const char* mode);

    For example, let's open a file and write Hello World into it

    #include #include #include void main() ( //Using the file variable we will access the file FILE *file; //Open a text file with write permissions file = fopen("C:/c/test.txt", "w+t") ; //Write to the file fprintf(file, "Hello, World!"); //Close the file fclose(file);

    The fopen function itself allocates memory for the object; cleaning is carried out by the fclose function. It is necessary to close the file; it will not close on its own.

    The fopen function can open a file in text or binary mode. The default is text. The access mode can be as follows

    File access options.
    Type Description
    r Reading. The file must exist.
    w Write a new file. If a file with the same name already exists, its contents will be lost.
    a Write to the end of the file. Positioning operations (fseek, fsetpos, frewind) are ignored. The file is created if it did not exist.
    r+ Reading and updating. You can both read and write. The file must exist.
    w+ Recording and updating. A new file is created. If a file with the same name already exists, its contents will be lost. You can both write and read.
    a+ End post and update. Positioning operations are read-only and ignored for writes. If the file did not exist, a new one will be created.

    If it is necessary to open the file in binary mode, then the letter b is added to the end of the line, for example “rb”, “wb”, “ab”, or, for mixed mode, “ab+”, “wb+”, “ab+”. Instead of b, you can add the letter t, then the file will open in text mode. It depends on the implementation. In the new C standard (2011), the letter x means that fopen should fail if the file already exists. Let's supplement our old program: reopen the file and consider what we wrote there.

    #include #include #include void main() ( FILE *file; char buffer; file = fopen("C:/c/test.txt", "w"); fprintf(file, "Hello, World!"); fclose(file); file = fopen("C:/c/test.txt", "r"); fgets(buffer, 127, file); printf("%s", buffer(file);

    Instead of the fgets function, you could use fscanf, but you need to remember that it can only read the line up to the first space.
    fscanf(file, "%127s", buffer);

    Also, instead of opening and closing a file, you can use the freopen function, which “re-opens” the file with new access rights.

    #include #include #include void main() ( FILE *file; char buffer; file = fopen("C:/c/test.txt", "w"); fprintf(file, "Hello, World!"); freopen("C:/ c/test.txt", "r", file); fgets(buffer, 127, file); printf("%s", buffer); fclose(file); getch(); )

    The functions fprintf and fscanf differ from printf and scanf only in that they take as their first argument a pointer to the FILE to which they will output or from which they will read data. It’s worth adding right away that the printf and scanf functions can be easily replaced by the fprintf and fscanf functions. In the OS (we are considering the most common and adequate operating systems) there are three standard streams: standard output stream stdout, standard input stream stdin and standard error output stream stderr. They are automatically opened when the application is launched and are associated with the console. Example

    #include #include #include void main() ( int a, b; fprintf(stdout, "Enter two numbers\n"); fscanf(stdin, "%d", &a); fscanf(stdin, "%d", &b); if (b == 0) ( fprintf(stderr, "Error: divide by zero"); ) else ( fprintf(stdout, "%.3f", (float) a / (float) b); ) getch();

    Error opening file

    If the fopen function call fails, it will return NULL. Errors while working with files occur quite often, so every time we open a file, we need to check the result of the work

    #include #include #include #define ERROR_OPEN_FILE -3 void main() ( FILE *file; char buffer; file = fopen("C:/c/test.txt", "w"); if (file == NULL) ( printf("Error opening file"); getch(); exit(ERROR_OPEN_FILE); ) fprintf(file, "Hello, World!"); freopen("C:/c/test.txt", "r", file); if (file = = NULL) ( printf("Error opening file"); getch(); exit(ERROR_OPEN_FILE); ) fgets(buffer, 127, file); printf("%s", buffer(file); ; )

    The problem arises when several files are opened at once: if one of them cannot be opened, then the rest must also be closed

    FILE *inputFile, *outputFile; unsigned m, n; unsigned i, j; inputFile = fopen(INPUT_FILE, READ_ONLY); if (inputFile == NULL) ( printf("Error opening file %s", INPUT_FILE); getch(); exit(3); ) outputFile = fopen(OUTPUT_FILE, WRITE_ONLY); if (outputFile == NULL) ( printf("Error opening file %s", OUTPUT_FILE); getch(); if (inputFile != NULL) ( fclose(inputFile); ) exit(4); ) ...

    In simple cases, you can act head-on, as in the previous piece of code. In more complex cases, methods are used that replace RAII from C++: wrappers, or compiler features (cleanup in GCC), etc.

    Data Buffering

    As mentioned earlier, when we output data, it is first placed in a buffer. The buffer is cleared

    • 1) If it is full
    • 2) If the stream is closed
    • 3) If we explicitly indicate that it is necessary to clear the buffer (there are exceptions here too :)).
    • 4) Also cleared if the program completed successfully. At the same time, all files are closed. In case of a runtime error, this may not happen.

    You can force buffer unloading by calling the fflush(File *) function. Let's look at two examples - with and without cleaning.

    #include #include #include void main() ( FILE *file; char c; file = fopen("C:/c/test.txt", "w"); do ( c = getch(); fprintf(file, "%c", c ); fprintf(stdout, "%c", c); //fflush(file); while(c != "q");

    Uncomment the fflush call. At runtime, open the text file and look at the behavior.

    You can assign a file buffer yourself by setting your own size. This is done using the function

    Void setbuf(FILE * stream, char * buffer);

    which takes an already open FILE and a pointer to a new buffer. The size of the new buffer must be no less than BUFSIZ (for example, on the current workstation BUFSIZ is 512 bytes). If you pass NULL as a buffer, the stream becomes unbuffered. You can also use the function

    Int setvbuf(FILE * stream, char * buffer, int mode, size_t size);

    which accepts a buffer of arbitrary size size. The mode can take the following values

    • _IOFBF- full buffering. Data is written to the file when it is full. On read, the buffer is considered full when an input operation is requested and the buffer is empty.
    • _IOLBF- linear buffering. Data is written to the file when it is full or when a newline character is encountered. On read, the buffer is filled to the newline character when an input operation is requested and the buffer is empty.
    • _IONBF– no buffering. In this case, the size and buffer parameters are ignored.
    If successful, the function returns 0.

    Example: let's set our own buffer and see how reading from a file is carried out. Let the file be short (something like Hello, World!), and we read it character by character

    #include #include #include void main() ( FILE *input = NULL; char c; char buffer = (0); input = fopen("D:/c/text.txt", "rt"); setbuf(input, buffer); while ( !feof(input)) ( c = fgetc(input); printf("%c\n", c); printf("%s\n", buffer); _getch(); ) fclose(input )

    It can be seen that the data is already in the buffer. Reading character by character is done from the buffer.

    feof

    Function int feof(FILE * stream); returns true if the end of the file is reached. The function is convenient to use when you need to go through the entire file from beginning to end. Let there be a file with text content text.txt. We read the file character by character and display it on the screen.

    #include #include #include void main() ( FILE *input = NULL; char c; input = fopen("D:/c/text.txt", "rt"); if (input == NULL) ( printf("Error opening file") ; _getch(); exit(0); while (!feof(input)) ( c = fgetc(input); fprintf(stdout, "%c", c); ) fclose(input());

    Everything would be fine, but the feof function does not work correctly... This is due to the fact that the concept of “end of file” is not defined. An error that often occurs when using feof is that the last data read is printed twice. This is due to the fact that the data is written to the input buffer, the last read occurs with an error and the function returns the old value read.

    #include #include #include void main() ( FILE *input = NULL; char c; input = fopen("D:/c/text.txt", "rt"); if (input == NULL) ( printf("Error opening file") ; _getch(); exit(0); while (!feof(input)) ( fscanf(input, "%c", &c); fprintf(stdout, "%c", c); ) fclose(input); _getch();

    This example will fail (most likely) and print the last character of the file twice.

    The solution is not to use feof. For example, store the total number of records, or use the fact that fscanf etc. functions usually return the number of values ​​correctly read and matched.

    #include #include #include void main() ( FILE *input = NULL; char c; input = fopen("D:/c/text.txt", "rt"); if (input == NULL) ( printf("Error opening file") ; _getch(); exit(0); while (fscanf(input, "%c", &c) == 1) ( fprintf(stdout, "%c", c); ) fclose(input()); ; )

    Examples

    1. One file contains two numbers - the dimensions of the array. Let's fill the second file with an array of random numbers.

    #include #include #include #include //File names and permissions #define INPUT_FILE "D:/c/input.txt" #define OUTPUT_FILE "D:/c/output.txt" #define READ_ONLY "r" #define WRITE_ONLY "w" //Maximum value for array size #define MAX_DIMENSION 100 //Error when opening a file #define ERROR_OPEN_FILE -3 void main() ( FILE *inputFile, *outputFile; unsigned m, n; unsigned i, j; inputFile = fopen(INPUT_FILE, READ_ONLY); if ( inputFile == NULL) ( printf("Error opening file %s", INPUT_FILE); getch(); exit(ERROR_OPEN_FILE); ) outputFile = fopen(OUTPUT_FILE, WRITE_ONLY); if (outputFile == NULL) ( printf("Error opening file %s", OUTPUT_FILE); getch(); //If the file could be opened for reading, then it must be closed if (inputFile != NULL) ( fclose(inputFile); ) exit(ERROR_OPEN_FILE); ) fscanf(inputFile, "%ud %ud", &m, &n); if (m > MAX_DIMENSION) ( m = MAX_DIMENSION; ) if (n > MAX_DIMENSION) ( n = MAX_DIMENSION; ) srand(time(NULL)); i< n; i++) { for (j = 0; j < m; j++) { fprintf(outputFile, "%8d ", rand()); } fprintf(outputFile, "\n"); } //Закрываем файлы fclose(inputFile); fclose(outputFile); }

    2. The user copies the file, and first selects the operating mode: the file can be output either to the console or copied to a new file.

    #include #include #include #define ERROR_FILE_OPEN -3 void main() ( FILE *origin = NULL; FILE *output = NULL; char filename; int mode; printf("Enter filename: "); scanf("%1023s", filename); origin = fopen (filename, "r"); if (origin == NULL) ( printf("Error opening file %s", filename); getch(); exit(ERROR_FILE_OPEN); ) printf("enter mode: "); "%d", &mode); if (mode == 1) ( printf("Enter filename: "); scanf("%1023s", filename); output = fopen(filename, "w"); if (output = = NULL) ( printf("Error opening file %s", filename); getch(); fclose(origin); exit(ERROR_FILE_OPEN); ) ) else ( output = stdout; ) while (!feof(origin)) ( fprintf (output, "%c", fgetc(origin)); fclose(origin);

    3. The user enters data from the console and it is written to a file until the esc key is pressed. Check out the program and see. how it behaves if you enter backspace: what is output to the file and what is output to the console.

    #include #include #include #define ERROR_FILE_OPEN -3 void main() ( FILE *output = NULL; char c; output = fopen("D:/c/test_output.txt", "w+t"); if (output == NULL) ( printf ("Error opening file"); _getch(); exit(ERROR_FILE_OPEN); for (;;) ( c = _getch(); if (c == 27) ( break; ) fputc(c, output); fputc( c, stdout);

    4. The file contains integers. Find the maximum of them. Let's take advantage of the fact that the fscanf function returns the number of correctly read and matched objects. The number 1 should be returned each time.

    #include #include #include #define ERROR_FILE_OPEN -3 void main() ( FILE *input = NULL; int num, maxn, hasRead; input = fopen("D:/c/input.txt", "r"); if (input == NULL) ( printf("Error opening file"); _getch(); exit(ERROR_FILE_OPEN); ) maxn = INT_MIN; hasRead = 1; while (hasRead == 1) ( hasRead = fscanf(input, "%d", &num); if (hasRead != 1) ( continue; ) if (num >

    Another solution is to read numbers until we reach the end of the file.

    #include #include #include #include #define ERROR_FILE_OPEN -3 void main() ( FILE *input = NULL; int num, maxn, hasRead; input = fopen("D:/c/input.txt", "r"); if (input == NULL) ( printf("Error opening file"); _getch(); exit(ERROR_FILE_OPEN); ) maxn = INT_MIN; while (!feof(input)) ( fscanf(input, "%d", &num); if (num > maxn ) ( maxn = num; ) ) printf("max number = %d", maxn);

    5. The file contains words: Russian word, tabulation, English word, in several rows. The user enters an English word, it is necessary to output the Russian one.

    The translation file looks something like this

    sun sun
    pencil pen
    ballpoint pen pencil
    door door
    windows window
    chair chair
    armchair

    and saved in cp866 encoding (OEM 866). It is important: the last pair of words also ends with a line feed.

    The algorithm is as follows: we read a line from a file, find a tab character in the line, replace the tab character with a zero, copy a Russian word from the buffer, copy an English word from the buffer, check for equality.

    #include #include #include #include #define ERROR_FILE_OPEN -3 void main() ( FILE *input = NULL; char buffer; char enWord; char ruWord; char usrWord; unsigned index; int length; int wasFound; input = fopen("D:/c/input.txt ", "r"); if (input == NULL) ( printf("Error opening file"); _getch(); exit(ERROR_FILE_OPEN); ) printf("enter word: "); fgets(usrWord, 127, stdin ); wasFound = 0; while (!feof(input)) ( fgets(buffer, 511, input); length = strlen(buffer); for (index = 0; index< length; index++) { if (buffer == "\t") { buffer = "\0"; break; } } strcpy(ruWord, buffer); strcpy(enWord, &buffer); if (!strcmp(enWord, usrWord)) { wasFound = 1; break; } } if (wasFound) { printf("%s", ruWord); } else { printf("Word not found"); } fclose(input); _getch(); }

    6. Count the number of lines in the file. We will read the file character by character, counting the number of "\n" characters until we encounter the EOF character. EOF is a special character that indicates that the input is complete and there is no more data to read. The function returns a negative value in case of error.
    NOTE: EOF is of type int, so you need to use int to read the characters. Additionally, the value of EOF is not defined by the standard.

    #define _CRT_SECURE_NO_WARNINGS #include #include #include int cntLines(const char *filename) ( int lines = 0; int any; //any is of type int because EOF is of type int! FILE *f = fopen(filename, "r"); if (f == NULL) ( return -1; ) do ( any = fgetc(f); //printf("%c", any);//debug if (any == "\n") ( lines++; ) ) while(any != EOF); ​​fclose(f); return lines; ) void main() ( printf("%d\n", cntLines("C:/c/file.txt")); _getch(); )

    Ru-Cyrl 18-tutorial Sypachev S.S. 1989-04-14 [email protected] Stepan Sypachev students

    Still not clear? – write questions to the mailbox