File operations

About this page

This page presents 4 functions, from the C Standard Library, that operate on files. It also shows how to verify the existence of a file and retrieve the size of one.

Removing a file

The function remove, which header is int remove(const char* path), is used to remove the file located at the path represented by the null-terminated string given in argument. If the file is successfully removed, 0 is returned.

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h> int main() { /* If the file "file.txt" inside the folder "myFiles" exists and the program has the permissions required to delete it, it will be removed. */ remove("myFiles/file.txt"); return 0; }

Renaming a file

The function rename, which header is int rename(const char* oldPath, const char* newPath), is used to rename the file located at the path given as first argument to the path given as second argument. If the filename specified as second argument is located in a different location than the one in first parameter, the file will be moved to it (If the path exists). If there is already a file at the path given as second argument, the operation might either fail or override the file depending on the compiler. In case of success, 0 is returned.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h> int main() { /* Changes the name of the file "oldName.txt", located in the folder "myFiles", to "newName.txt" and prints a different message depending on the result of the operation. */ if(rename("myFiles/oldName.txt", "myFiles/newName.txt") == 0) puts("Name of the file successfully changed."); else puts("An error occured."); return 0; }

Retrieving a name for a temporary file

The function tmpnam, which header is char* tmpnam(char* str), returns a null-terminated string containing a file name different from any existing file name. We can then use that name to create a temporary file (To temporarily store information on a storage device) without having to worry about overwriting an existing file. If the function fails, it returns a null pointer. If a null pointer is given as argument, the file name (Ended with a null-character) will be written somewhere in the static memory and a pointer to it will be returned. If a pointer to an array of bytes is given to it, the array pointed is filled with the file name (Ended with a null-character) and its address is returned back.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h> int main() { char name[30]; // Retrieves a name for a temporary file into the array 'name'. tmpnam(name); // Prints the name retrieved to the console. puts(name); // Creates a file using that name. FILE* tempFile = fopen(name, "wb"); fclose(tempFile); // Removes the file when we do not need it anymore. remove(name); return 0; }

Creating temporary file

The function tmpfile, which header is FILE* tmpfile(), creates, opens and returns a stream to a temporary file opened for binary input/output ("wb+") and automatically removes it when the file is closed (Using the function fclose) or if the program ends normally. If the program stops in an abnormal way (Example: The operating system kills the process), the file might not be removed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h> int main() { FILE* tempFile = tmpfile(); float array[] = {1, 2, 3, 4, 5}; // Saves the elements of the array 'array' inside the temporary file. fwrite(array, sizeof(float), 5, tempFile); // Let us pretend that there are lines of code that modify the content of array here. // Sets the cursor position of tempFile to the start of the file. rewind(tempFile); // Retrieves the values saved in the temporary file, into the array 'array'. fread(array, sizeof(float), 5, tempFile); // Closes and automatically removes the temporary file. fclose(tempFile); return 0; }

File existence

The C Standard Library does not provide any function to directly verify the existence of a file. A trick to do it is to try to open the file with the opening mode read only ("r"). If the function fopen returns a null pointer, then the file (probably) does not exist.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h> int main() { // Try to open the file "test.txt" FILE* file = fopen("test.txt", "r"); if(file != 0) puts("The file \"test.txt\" exists.\n"); else puts("The file \"test.txt\" does not exist.\n"); return 0; }

File size

Unlike the C++ Standard Library, the C Standard Library does not really provide the tools to nicely retrieve the size of a file.

A way to do it is to open the file in binary mode, set the stream cursor to the end of the file and then retrieve its position using ftell, which will be equal to the size, in bytes, of the file (In binary mode, ftell returns the number of bytes between the start of the stream and the cursor position). The problem with that solution is that in order to set the cursor to the end of the file using the function fseek, we must use the macro SEEK_END, which is not very portable because it is not guaranteed to be implemented by every C/C++ compiler.

An other way to achieve that is by opening the file in binary mode and then read all its content using the function fread to an array we know is bigger than the file. The number of successfully read bytes (The value returned by the function fread when the size of each element is 1 byte) is the size of the file. The problem with that approach is that it is heavy as we must read the whole file simply to know its size and we need to allocate a way too big array, especially if we have no idea the size of the file is in what range. This method might be not too bad if we need to read the whole file anyway.

We could also open the stream in binary mode and read the whole file using the function scanf (With the asterisk * sub-qualifier so it discards what it reads). Then we retrieve the size of the file by using the function ftell.

Streams opened in binary append/update mode ("ab+") have their cursor starting at the start of the file and each output operation brings the cursor to the end of the file. That could be used to retrieve the size of the file, but the problem is that we must really output something to the stream to bring the cursor to the end of it (Calling an outputting function like fwrite by giving it 0 as the number of element to write, so it does not actually output something, does not set the cursor to the end of the file).

Here is an example of how to retrieve a the size of a file using the macro SEEK_END:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h> int main() { FILE* file = fopen("file.txt", "rb"); /* Sets the cursor of the stream to the end of the file. Might not be supported by some compiler. */ fseek(file, 0, SEEK_END); // Retrieves the cursor position. unsigned long fileSize = ftell(file); // Prints the size of the file. printf("The size of the file is %lu.\n", fileSize); fclose(file); return 0; }

An example of the second method presented above:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h> #include <stdlib.h> int main() { /* We allocate the memory dynamically (Instead of using the static memory by creating a static array) because we need a big amount of memory. Using dynamically allocated memory also allows us to reduce the size of the array by using realloc once we retrieve the size of the file. */ const unsigned long BUFFER_SIZE = 40 * 1000 * 1000; // 40 MB char *buffer = (char*) malloc(BUFFER_SIZE); FILE* file = fopen("file.txt", "rb"); /* The function fread will stop when it reaches the end of the file and will return the number of bytes read (The size of the file), unless the buffer is smaller than the file. */ unsigned long fileSize = fread(buffer, 1, BUFFER_SIZE, file); // Prints the size of the file. printf("The size of the file is %lu.\n", fileSize); // Resizes the buffer to the size of the file. buffer = (char*)realloc(buffer, fileSize); free(buffer); fclose(file); return 0; }

An example of the third approach presented above:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h> int main() { FILE* file = fopen("file.txt", "rb"); /* Sets the cursor of the stream to the end of the file by reading all its content. By giving a huge amount of character (byte) to read to the function fscanf, it should stop when the end of the file is reached. */ fscanf(file, "%*2147483647c"); // Retrieves the cursor position. unsigned long fileSize = ftell(file); // Prints the size of the file. printf("The size of the file is %lu.\n", fileSize); fclose(file); return 0; }